pub const AUTH_HMAC_LABEL: &[u8] = b"lo-auth-v1";
pub const KEX_HKDF_INFO_PFX: &[u8] = b"lo-kex-v1";
pub const SPK_SIG_LABEL: &[u8] = b"lo-spk-sig-v1";
pub const INITIATOR_SIG_LABEL: &[u8] = b"lo-kex-init-sig-v1";
pub const RATCHET_HKDF_INFO: &[u8] = b"lo-ratchet-v1";
pub const DM_AAD: &[u8] = b"lo-dm-v1";
pub const STORAGE_AAD: &[u8] = b"lo-storage-v1";
pub const DM_QUEUE_AAD: &[u8] = b"lo-dm-queue-v1";
pub const CALL_HKDF_INFO: &[u8] = b"lo-call-v1";
pub const PHRASE_HASH_LABEL: &[u8] = b"lo-verification-v1";
pub const PHRASE_EXPAND_LABEL: &[u8] = b"lo-phrase-expand-v1";
pub const CALL_ID_SIZE: usize = 16;
pub const CALL_KEY_A_BYTE: &[u8] = &[0x04];
pub const CALL_KEY_B_BYTE: &[u8] = &[0x05];
pub const CALL_CHAIN_ADV_BYTE: &[u8] = &[0x06];
pub const MSG_KEY_DOMAIN_BYTE: u8 = 0x01;
pub const HKDF_ZERO_SALT: [u8; 32] = [0u8; 32];
pub const MAX_RECV_SEEN: u32 = 65536;
pub const RATCHET_BLOB_VERSION: u8 = 0x01;
pub const CRYPTO_VERSION: &str = "lo-crypto-v1";
pub const LO_PUBLIC_KEY_SIZE: usize = 3200;
pub const LO_SECRET_KEY_SIZE: usize = 2496;
pub const XWING_SECRET_KEY_SIZE: usize = 2432;
pub const XWING_PUBLIC_KEY_SIZE: usize = 1216;
pub const XWING_CIPHERTEXT_SIZE: usize = 1120;
pub const FINGERPRINT_SIZE: usize = 32;
pub const ED25519_SIGNATURE_SIZE: usize = 64;
pub const MLDSA_SIGNATURE_SIZE: usize = 3309;
pub const HYBRID_SIGNATURE_SIZE: usize = 3373;
pub const SHARED_SECRET_SIZE: usize = 32;
pub const AEAD_TAG_SIZE: usize = 16;
pub const AEAD_NONCE_SIZE: usize = 24;
pub const STREAM_AAD: &[u8] = b"lo-stream-v1";
pub const STREAM_CHUNK_SIZE: usize = 1_048_576;
pub const STREAM_HEADER_SIZE: usize = 26;
pub const STREAM_CHUNK_OVERHEAD: usize = 17;
pub const STREAM_ZSTD_OVERHEAD: usize = 256;
pub const STREAM_ENCRYPT_MAX: usize =
STREAM_CHUNK_SIZE + STREAM_ZSTD_OVERHEAD + STREAM_CHUNK_OVERHEAD;
pub const STREAM_VERSION: u8 = 0x01;
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::assertions_on_constants)]
fn constants_match_spec() {
assert_eq!(AUTH_HMAC_LABEL, b"lo-auth-v1");
assert_eq!(KEX_HKDF_INFO_PFX, b"lo-kex-v1");
assert_eq!(SPK_SIG_LABEL, b"lo-spk-sig-v1");
assert_eq!(INITIATOR_SIG_LABEL, b"lo-kex-init-sig-v1");
assert_eq!(RATCHET_HKDF_INFO, b"lo-ratchet-v1");
assert_eq!(DM_AAD, b"lo-dm-v1");
assert_eq!(STORAGE_AAD, b"lo-storage-v1");
assert_eq!(DM_QUEUE_AAD, b"lo-dm-queue-v1");
assert_eq!(CALL_HKDF_INFO, b"lo-call-v1");
assert_eq!(PHRASE_HASH_LABEL, b"lo-verification-v1");
assert_eq!(PHRASE_EXPAND_LABEL, b"lo-phrase-expand-v1");
assert_eq!(CALL_ID_SIZE, 16);
assert_eq!(CALL_KEY_A_BYTE, &[0x04]);
assert_eq!(CALL_KEY_B_BYTE, &[0x05]);
assert_eq!(CALL_CHAIN_ADV_BYTE, &[0x06]);
assert_eq!(MSG_KEY_DOMAIN_BYTE, 0x01);
assert_eq!(HKDF_ZERO_SALT, [0u8; 32]);
assert_eq!(MAX_RECV_SEEN, 65536);
assert_eq!(LO_PUBLIC_KEY_SIZE, 3200);
assert_eq!(LO_SECRET_KEY_SIZE, 2496);
assert_eq!(XWING_SECRET_KEY_SIZE, 2432);
assert_eq!(XWING_PUBLIC_KEY_SIZE, 1216);
assert_eq!(XWING_CIPHERTEXT_SIZE, 1120);
assert_eq!(FINGERPRINT_SIZE, 32);
assert_eq!(ED25519_SIGNATURE_SIZE, 64);
assert_eq!(MLDSA_SIGNATURE_SIZE, 3309);
assert_eq!(HYBRID_SIGNATURE_SIZE, 3373);
assert_eq!(SHARED_SECRET_SIZE, 32);
assert_eq!(AEAD_TAG_SIZE, 16);
assert_eq!(AEAD_NONCE_SIZE, 24);
assert_eq!(STREAM_AAD, b"lo-stream-v1");
assert_eq!(STREAM_CHUNK_SIZE, 1_048_576);
assert_eq!(STREAM_HEADER_SIZE, 26);
assert_eq!(STREAM_CHUNK_OVERHEAD, 17);
assert_eq!(STREAM_ZSTD_OVERHEAD, 256);
assert_eq!(
STREAM_ENCRYPT_MAX,
STREAM_CHUNK_SIZE + STREAM_ZSTD_OVERHEAD + STREAM_CHUNK_OVERHEAD
);
assert_eq!(STREAM_VERSION, 0x01);
assert_eq!(
HYBRID_SIGNATURE_SIZE,
ED25519_SIGNATURE_SIZE + MLDSA_SIGNATURE_SIZE
);
assert_eq!(LO_PUBLIC_KEY_SIZE, XWING_PUBLIC_KEY_SIZE + 32 + 1952);
assert_eq!(LO_SECRET_KEY_SIZE, XWING_SECRET_KEY_SIZE + 32 + 32);
assert_eq!(CRYPTO_VERSION, "lo-crypto-v1");
assert!(MAX_RECV_SEEN <= 65536);
}
#[test]
fn domain_labels_pairwise_unique() {
let labels: Vec<(&str, &[u8])> = vec![
("AUTH_HMAC_LABEL", AUTH_HMAC_LABEL),
("KEX_HKDF_INFO_PFX", KEX_HKDF_INFO_PFX),
("SPK_SIG_LABEL", SPK_SIG_LABEL),
("INITIATOR_SIG_LABEL", INITIATOR_SIG_LABEL),
("RATCHET_HKDF_INFO", RATCHET_HKDF_INFO),
("DM_AAD", DM_AAD),
("STORAGE_AAD", STORAGE_AAD),
("DM_QUEUE_AAD", DM_QUEUE_AAD),
("CALL_HKDF_INFO", CALL_HKDF_INFO),
("PHRASE_HASH_LABEL", PHRASE_HASH_LABEL),
("PHRASE_EXPAND_LABEL", PHRASE_EXPAND_LABEL),
("STREAM_AAD", STREAM_AAD),
];
for i in 0..labels.len() {
for j in (i + 1)..labels.len() {
assert_ne!(
labels[i].1, labels[j].1,
"domain label collision: {} == {}",
labels[i].0, labels[j].0
);
}
}
let domain_bytes: Vec<(&str, u8)> = vec![
("MSG_KEY_DOMAIN_BYTE", MSG_KEY_DOMAIN_BYTE),
("CALL_KEY_A_BYTE", CALL_KEY_A_BYTE[0]),
("CALL_KEY_B_BYTE", CALL_KEY_B_BYTE[0]),
("CALL_CHAIN_ADV_BYTE", CALL_CHAIN_ADV_BYTE[0]),
];
for i in 0..domain_bytes.len() {
for j in (i + 1)..domain_bytes.len() {
assert_ne!(
domain_bytes[i].1, domain_bytes[j].1,
"domain byte collision: {} == {}",
domain_bytes[i].0, domain_bytes[j].0
);
}
}
}
}