Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openscreen-application/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ license.workspace = true
repository.workspace = true

[dependencies]
bytes = { version = "1", default-features = false }
openscreen-common = { path = "../openscreen-common" }
openscreen-network = { path = "../openscreen-network" }
openscreen-crypto = { path = "../openscreen-crypto" }
Expand Down
26 changes: 15 additions & 11 deletions openscreen-application/src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ impl SerialNumber {
bytes
}

/// Base64 encode serial number per RFC 4648
/// URL-safe base64 encode serial number (no padding).
/// Spec says RFC 4648 base64, but standard base64 contains +/= which are
/// invalid in DNS labels. See: https://github.com/w3c/openscreenprotocol/issues/365
pub fn to_base64(&self) -> String {
use base64::{engine::general_purpose::STANDARD, Engine};
STANDARD.encode(self.to_bytes())
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
URL_SAFE_NO_PAD.encode(self.to_bytes())
}

/// Parse from 160-bit bytes
Expand Down Expand Up @@ -325,7 +327,7 @@ mod tests {
assert_eq!(bytes.len(), 20); // 160 bits

let base64 = serial.to_base64();
assert_eq!(base64.len(), 28); // base64 encoding of 20 bytes
assert_eq!(base64.len(), 27); // URL-safe base64 (no padding) of 20 bytes
}

#[test]
Expand Down Expand Up @@ -386,8 +388,9 @@ mod tests {
let parts: Vec<&str> = cert.hostname.split('.').collect();
assert!(parts.len() >= 3);

// First part should be base64 serial (28 chars for 160-bit number)
assert_eq!(parts[0].len(), 28);
// First part should be URL-safe base64 serial (27 chars for 160-bit number, no padding)
// See: https://github.com/w3c/openscreenprotocol/issues/365
assert_eq!(parts[0].len(), 27);

// Second part should be sanitized name
assert_eq!(parts[1], "receiver");
Expand All @@ -413,12 +416,13 @@ mod tests {
let serial = SerialNumber::generate();
let base64 = serial.to_base64();

// 20 bytes encoded in base64 = ceil(20 * 8 / 6) = 28 chars (with padding)
assert_eq!(base64.len(), 28);
// 20 bytes encoded in URL-safe base64 (no padding) = ceil(20 * 8 / 6) = 27 chars
// See: https://github.com/w3c/openscreenprotocol/issues/365
assert_eq!(base64.len(), 27);

// Should be valid base64
use base64::{engine::general_purpose::STANDARD, Engine};
let decoded = STANDARD.decode(&base64).unwrap();
// Should be valid URL-safe base64 (no padding)
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
let decoded = URL_SAFE_NO_PAD.decode(&base64).unwrap();
assert_eq!(decoded.len(), 20);
}

Expand Down
Loading
Loading