1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Sizes, labels, and limits for the quantum-shield v2 wire format.
//!
//! All sizes are fixed by the algorithm suite (suite id 1): X25519 +
//! ML-KEM-1024 for key establishment, AES-256-GCM for payload encryption,
//! and Ed25519 + ML-DSA-87 for signatures.
/// Wire format version produced and accepted by this crate.
pub const WIRE_VERSION: u8 = 2;
/// Cipher suite id 1: X25519 + ML-KEM-1024, AES-256-GCM, Ed25519 + ML-DSA-87.
pub const SUITE_ID: u8 = 1;
/// Length of the common wire header: magic (4) + version (1) + suite (1).
pub const HEADER_LEN: usize = 6;
/// Magic prefix of a serialized [`Envelope`](crate::Envelope).
pub const MAGIC_ENVELOPE: = *b"QSE2";
/// Magic prefix of a serialized [`HybridSignature`](crate::HybridSignature).
pub const MAGIC_SIGNATURE: = *b"QSS2";
/// Magic prefix of a serialized [`PublicKeyBundle`](crate::PublicKeyBundle).
pub const MAGIC_PUBLIC_BUNDLE: = *b"QSP2";
/// Magic prefix of a serialized secret-key bundle.
pub const MAGIC_SECRET_BUNDLE: = *b"QSK2";
/// Magic prefix of a serialized multi-recipient envelope.
pub const MAGIC_MULTI: = *b"QSM2";
/// Magic prefix of a serialized streaming header.
pub const MAGIC_STREAM: = *b"QST2";
/// Magic prefix of a serialized rotation attestation.
pub const MAGIC_ROTATION: = *b"QSR2";
/// X25519 public key length in bytes.
pub const X25519_PK_LEN: usize = 32;
/// X25519 secret key length in bytes.
pub const X25519_SK_LEN: usize = 32;
/// ML-KEM-1024 encapsulation (public) key length in bytes.
pub const MLKEM1024_EK_LEN: usize = 1568;
/// ML-KEM-1024 ciphertext length in bytes.
pub const MLKEM1024_CT_LEN: usize = 1568;
/// ML-KEM (d,z) seed length in bytes (FIPS 203 private key seed form).
pub const MLKEM_SEED_LEN: usize = 64;
/// Ed25519 public key length in bytes.
pub const ED25519_PK_LEN: usize = 32;
/// Ed25519 private key seed length in bytes.
pub const ED25519_SEED_LEN: usize = 32;
/// Ed25519 signature length in bytes.
pub const ED25519_SIG_LEN: usize = 64;
/// ML-DSA-87 verifying (public) key length in bytes.
pub const MLDSA87_VK_LEN: usize = 2592;
/// ML-DSA-87 signature length in bytes.
pub const MLDSA87_SIG_LEN: usize = 4627;
/// ML-DSA private key seed (xi) length in bytes (FIPS 204 Algorithm 6).
pub const MLDSA_SEED_LEN: usize = 32;
/// AES-256-GCM nonce length in bytes.
pub const NONCE_LEN: usize = 12;
/// AES-256-GCM authentication tag length in bytes.
pub const TAG_LEN: usize = 16;
/// Length of the authenticated envelope header (everything before the AEAD
/// ciphertext): header + ephemeral X25519 key + ML-KEM ciphertext + nonce.
/// This entire prefix is bound into the AEAD tag as associated data.
pub const ENVELOPE_AAD_LEN: usize = HEADER_LEN + X25519_PK_LEN + MLKEM1024_CT_LEN + NONCE_LEN;
/// Total envelope overhead on top of the plaintext length.
pub const ENVELOPE_OVERHEAD: usize = ENVELOPE_AAD_LEN + TAG_LEN;
/// Serialized [`HybridSignature`](crate::HybridSignature) length in bytes.
pub const SIGNATURE_LEN: usize = HEADER_LEN + ED25519_SIG_LEN + MLDSA87_SIG_LEN;
/// Serialized [`PublicKeyBundle`](crate::PublicKeyBundle) length in bytes.
pub const PUBLIC_BUNDLE_LEN: usize =
HEADER_LEN + X25519_PK_LEN + MLKEM1024_EK_LEN + ED25519_PK_LEN + MLDSA87_VK_LEN;
/// Serialized secret-key bundle length in bytes (seeds only).
pub const SECRET_BUNDLE_LEN: usize =
HEADER_LEN + X25519_SK_LEN + MLKEM_SEED_LEN + ED25519_SEED_LEN + MLDSA_SEED_LEN;
/// Maximum plaintext length accepted by [`seal`](crate::seal) (64 MiB).
pub const MAX_PLAINTEXT_LEN: usize = 64 * 1024 * 1024;
/// Content-encryption key length for multi-recipient envelopes (bytes).
pub const CEK_LEN: usize = 32;
/// Length of the CEK commitment in a multi-recipient envelope (bytes).
pub const CEK_COMMIT_LEN: usize = 32;
/// Per-recipient wrap length in a multi-recipient envelope: ephemeral X25519
/// key + ML-KEM ciphertext + wrap nonce + wrapped CEK (CEK + AEAD tag).
pub const WRAP_LEN: usize = X25519_PK_LEN + MLKEM1024_CT_LEN + NONCE_LEN + CEK_LEN + TAG_LEN;
/// Maximum recipients per multi-recipient envelope (DoS bound; enforced at
/// both seal and parse time, since `open` trial-decrypts every wrap).
pub const MAX_RECIPIENTS: usize = 1024;
/// Plaintext bytes per chunk in a streaming envelope (64 KiB).
pub const STREAM_CHUNK_SIZE: usize = 64 * 1024;
/// Length of the random nonce prefix in a streaming envelope. The 12-byte
/// AES-GCM nonce is `prefix (7) || u32 chunk counter (4) || last-flag (1)`.
pub const STREAM_NONCE_PREFIX_LEN: usize = 7;
/// Length of a streaming header: header + ephemeral X25519 key + ML-KEM
/// ciphertext + nonce prefix.
pub const STREAM_HEADER_LEN: usize =
HEADER_LEN + X25519_PK_LEN + MLKEM1024_CT_LEN + STREAM_NONCE_PREFIX_LEN;
/// Length of a truncated key identifier (`SHA3-256(QSP2)[..16]`).
pub const KEY_ID_LEN: usize = 16;
/// Maximum signing/verification context length in bytes (mirrors the FIPS 204
/// context-string limit).
pub const MAX_CONTEXT_LEN: usize = 255;
/// Domain-separation label for the hybrid KEM shared-secret combiner.
pub const KEM_COMBINER_LABEL: & = b"quantum-shield/v2/kem:X25519+ML-KEM-1024\0";
/// Domain-separation label prepended to every signed message.
pub const SIG_DOMAIN_LABEL: & = b"quantum-shield/v2/sig:Ed25519+ML-DSA-87\0";
/// Signing context for rotation attestations.
pub const ROTATION_CONTEXT: & = b"quantum-shield/v2/rotate\0";
/// Domain label for the multi-recipient CEK commitment.
pub const MULTI_CEK_COMMIT_LABEL: & = b"quantum-shield/v2/multi:cek-commit\0";