Coverage Report

Created: 2026-05-18 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/git/checkouts/nss-rs-71e20fe79ef91440/9b94ca3/src/aead/mod.rs
Line
Count
Source
1
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4
// option. This file may not be copied, modified, or distributed
5
// except according to those terms.
6
7
use std::{os::raw::c_int, ptr::null_mut};
8
9
#[cfg(feature = "disable-encryption")]
10
pub use recprot::AEAD_NULL_TAG;
11
pub use recprot::RecordProtection;
12
13
use crate::{
14
    SECItemBorrowed, SymKey,
15
    err::{Error, Res},
16
    p11::{
17
        self, CK_ATTRIBUTE_TYPE, CK_GENERATOR_FUNCTION, CK_MECHANISM_TYPE, CKA_DECRYPT,
18
        CKA_ENCRYPT, CKA_NSS_MESSAGE, CKG_GENERATE_COUNTER_XOR, CKG_NO_GENERATE, CKM_AES_GCM,
19
        CKM_CHACHA20_POLY1305, Context, PK11_AEADOp, PK11_CreateContextBySymKey,
20
    },
21
    secstatus_to_res,
22
};
23
24
#[cfg_attr(feature = "disable-encryption", path = "recprot_null.rs")]
25
#[cfg_attr(not(feature = "disable-encryption"), path = "recprot.rs")]
26
mod recprot;
27
28
/// All the nonces are the same length.  Exploit that.
29
pub const NONCE_LEN: usize = 12;
30
31
/// The portion of the nonce that is a counter.
32
const COUNTER_LEN: usize = size_of::<SequenceNumber>();
33
34
4.06k
fn xor_nonce(base: &[u8; NONCE_LEN], count: SequenceNumber) -> [u8; NONCE_LEN] {
35
4.06k
    let mut nonce = *base;
36
32.4k
    for (n, &s) in nonce[NONCE_LEN - COUNTER_LEN..]
37
4.06k
        .iter_mut()
38
4.06k
        .zip(&count.to_be_bytes())
39
32.4k
    {
40
32.4k
        *n ^= s;
41
32.4k
    }
42
4.06k
    nonce
43
4.06k
}
44
45
/// The NSS API insists on us identifying the tag separately, which is awful.
46
/// All of the AEAD functions here have a tag of this length, so use a fixed offset.
47
const TAG_LEN: usize = 16;
48
49
pub type SequenceNumber = u64;
50
51
/// All the lengths used by `PK11_AEADOp` are signed.  This converts to that.
52
24.3k
fn c_int_len<T>(l: T) -> Res<c_int>
53
24.3k
where
54
24.3k
    T: TryInto<c_int>,
55
24.3k
    T::Error: std::error::Error,
56
{
57
24.3k
    l.try_into().map_err(|_| Error::IntegerOverflow)
58
24.3k
}
59
60
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61
pub enum Mode {
62
    Encrypt,
63
    Decrypt,
64
}
65
66
impl Mode {
67
0
    fn p11mode(self) -> CK_ATTRIBUTE_TYPE {
68
0
        CK_ATTRIBUTE_TYPE::from(
69
            CKA_NSS_MESSAGE
70
0
                | match self {
71
0
                    Self::Encrypt => CKA_ENCRYPT,
72
0
                    Self::Decrypt => CKA_DECRYPT,
73
                },
74
        )
75
0
    }
76
}
77
78
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
79
pub enum AeadAlgorithms {
80
    Aes128Gcm,
81
    Aes256Gcm,
82
    ChaCha20Poly1305,
83
}
84
85
pub struct Aead {
86
    mode: Mode,
87
    ctx: Context,
88
    nonce_base: [u8; NONCE_LEN],
89
}
90
91
impl Aead {
92
0
    fn mech(algorithm: AeadAlgorithms) -> CK_MECHANISM_TYPE {
93
0
        CK_MECHANISM_TYPE::from(match algorithm {
94
0
            AeadAlgorithms::Aes128Gcm | AeadAlgorithms::Aes256Gcm => CKM_AES_GCM,
95
0
            AeadAlgorithms::ChaCha20Poly1305 => CKM_CHACHA20_POLY1305,
96
        })
97
0
    }
98
99
0
    pub fn import_key(algorithm: AeadAlgorithms, key: &[u8]) -> Result<SymKey, Error> {
100
0
        let slot = p11::Slot::internal().map_err(|_| Error::Internal)?;
101
102
0
        let key_item = SECItemBorrowed::wrap(key)?;
103
0
        let key_item_ptr = std::ptr::from_ref(key_item.as_ref()).cast_mut();
104
105
0
        let ptr = unsafe {
106
0
            p11::PK11_ImportSymKey(
107
0
                *slot,
108
0
                Self::mech(algorithm),
109
                p11::PK11Origin::PK11_OriginUnwrap,
110
0
                CK_ATTRIBUTE_TYPE::from(CKA_ENCRYPT | CKA_DECRYPT),
111
0
                key_item_ptr,
112
0
                null_mut(),
113
            )
114
        };
115
0
        SymKey::from_ptr(ptr)
116
0
    }
117
118
0
    pub fn new(
119
0
        mode: Mode,
120
0
        algorithm: AeadAlgorithms,
121
0
        key: &SymKey,
122
0
        nonce_base: [u8; NONCE_LEN],
123
0
    ) -> Result<Self, Error> {
124
0
        crate::init()?;
125
126
0
        let ptr = unsafe {
127
0
            PK11_CreateContextBySymKey(
128
0
                Self::mech(algorithm),
129
0
                mode.p11mode(),
130
0
                **key,
131
0
                SECItemBorrowed::wrap(&nonce_base[..])?.as_ref(),
132
            )
133
        };
134
        Ok(Self {
135
0
            mode,
136
0
            ctx: Context::from_ptr(ptr)?,
137
0
            nonce_base,
138
        })
139
0
    }
140
141
0
    pub fn encrypt(&mut self, aad: &[u8], pt: &[u8]) -> Result<Vec<u8>, Error> {
142
0
        crate::init()?;
143
144
0
        assert_eq!(self.mode, Mode::Encrypt);
145
        // A copy for the nonce generator to write into.  But we don't use the value.
146
0
        let mut nonce = self.nonce_base;
147
        // Ciphertext with enough space for the tag.
148
        // Even though we give the operation a separate buffer for the tag,
149
        // reserve the capacity on allocation.
150
0
        let mut ct = vec![0; pt.len() + TAG_LEN];
151
0
        let mut ct_len: c_int = 0;
152
0
        let mut tag = vec![0; TAG_LEN];
153
0
        secstatus_to_res(unsafe {
154
0
            PK11_AEADOp(
155
0
                *self.ctx,
156
0
                CK_GENERATOR_FUNCTION::from(CKG_GENERATE_COUNTER_XOR),
157
0
                c_int_len(NONCE_LEN - COUNTER_LEN)?, // Fixed portion of the nonce.
158
0
                nonce.as_mut_ptr(),
159
0
                c_int_len(nonce.len())?,
160
0
                aad.as_ptr(),
161
0
                c_int_len(aad.len())?,
162
0
                ct.as_mut_ptr(),
163
0
                &raw mut ct_len,
164
0
                c_int_len(ct.len())?, // signed :(
165
0
                tag.as_mut_ptr(),
166
0
                c_int_len(tag.len())?,
167
0
                pt.as_ptr(),
168
0
                c_int_len(pt.len())?,
169
            )
170
0
        })?;
171
0
        ct.truncate(usize::try_from(ct_len).map_err(|_| Error::IntegerOverflow)?);
172
0
        debug_assert_eq!(ct.len(), pt.len());
173
0
        ct.append(&mut tag);
174
0
        Ok(ct)
175
0
    }
176
177
    /// Encrypt with an explicit sequence number. Mirrors `decrypt`'s nonce
178
    /// construction: the final nonce is `nonce_base XOR encode_be(seq)` over
179
    /// the trailing 8 bytes. The NSS PKCS#11 context's internal counter is
180
    /// not used (`CKG_NO_GENERATE`). The caller must never reuse
181
    /// `(nonce_base, seq)` with the same key.
182
0
    pub fn encrypt_with_seq(
183
0
        &mut self,
184
0
        aad: &[u8],
185
0
        seq: SequenceNumber,
186
0
        pt: &[u8],
187
0
    ) -> Result<Vec<u8>, Error> {
188
0
        crate::init()?;
189
190
0
        assert_eq!(self.mode, Mode::Encrypt);
191
0
        let mut nonce = xor_nonce(&self.nonce_base, seq);
192
0
        let mut ct = vec![0; pt.len() + TAG_LEN];
193
0
        let mut ct_len: c_int = 0;
194
0
        let mut tag = vec![0; TAG_LEN];
195
0
        secstatus_to_res(unsafe {
196
0
            PK11_AEADOp(
197
0
                *self.ctx,
198
0
                CK_GENERATOR_FUNCTION::from(CKG_NO_GENERATE),
199
0
                c_int_len(NONCE_LEN - COUNTER_LEN)?,
200
0
                nonce.as_mut_ptr(),
201
0
                c_int_len(nonce.len())?,
202
0
                aad.as_ptr(),
203
0
                c_int_len(aad.len())?,
204
0
                ct.as_mut_ptr(),
205
0
                &raw mut ct_len,
206
0
                c_int_len(ct.len())?,
207
0
                tag.as_mut_ptr(),
208
0
                c_int_len(tag.len())?,
209
0
                pt.as_ptr(),
210
0
                c_int_len(pt.len())?,
211
            )
212
0
        })?;
213
0
        ct.truncate(usize::try_from(ct_len).map_err(|_| Error::IntegerOverflow)?);
214
0
        debug_assert_eq!(ct.len(), pt.len());
215
0
        ct.append(&mut tag);
216
0
        Ok(ct)
217
0
    }
218
219
0
    pub fn decrypt(
220
0
        &mut self,
221
0
        aad: &[u8],
222
0
        seq: SequenceNumber,
223
0
        ct: &[u8],
224
0
    ) -> Result<Vec<u8>, Error> {
225
0
        crate::init()?;
226
227
0
        assert_eq!(self.mode, Mode::Decrypt);
228
0
        let mut nonce = xor_nonce(&self.nonce_base, seq);
229
0
        let mut pt = vec![0; ct.len()]; // NSS needs more space than it uses for plaintext.
230
0
        let mut pt_len: c_int = 0;
231
0
        let pt_expected = ct.len().checked_sub(TAG_LEN).ok_or(Error::AeadTruncated)?;
232
0
        secstatus_to_res(unsafe {
233
0
            PK11_AEADOp(
234
0
                *self.ctx,
235
0
                CK_GENERATOR_FUNCTION::from(CKG_NO_GENERATE),
236
0
                c_int_len(NONCE_LEN - COUNTER_LEN)?, // Fixed portion of the nonce.
237
0
                nonce.as_mut_ptr(),
238
0
                c_int_len(nonce.len())?,
239
0
                aad.as_ptr(),
240
0
                c_int_len(aad.len())?,
241
0
                pt.as_mut_ptr(),
242
0
                &raw mut pt_len,
243
0
                c_int_len(pt.len())?,
244
0
                ct.as_ptr().add(pt_expected).cast_mut(),
245
0
                c_int_len(TAG_LEN)?,
246
0
                ct.as_ptr(),
247
0
                c_int_len(pt_expected)?,
248
            )
249
0
        })?;
250
0
        let len = usize::try_from(pt_len).map_err(|_| Error::IntegerOverflow)?;
251
0
        debug_assert_eq!(len, pt_expected);
252
0
        pt.truncate(len);
253
0
        Ok(pt)
254
0
    }
255
}
256
257
#[cfg(test)]
258
mod test {
259
    use test_fixture::fixture_init;
260
261
    use crate::aead::{Aead, AeadAlgorithms, Mode, NONCE_LEN, SequenceNumber};
262
263
    /// Check that the first invocation of encryption matches expected values.
264
    /// Also check decryption of the same.
265
    fn check0(
266
        algorithm: AeadAlgorithms,
267
        key: &[u8],
268
        nonce: &[u8; NONCE_LEN],
269
        aad: &[u8],
270
        pt: &[u8],
271
        ct: &[u8],
272
    ) {
273
        fixture_init();
274
        let k = Aead::import_key(algorithm, key).unwrap();
275
276
        let mut enc = Aead::new(Mode::Encrypt, algorithm, &k, *nonce).unwrap();
277
        let ciphertext = enc.encrypt(aad, pt).unwrap();
278
        assert_eq!(&ciphertext[..], ct);
279
280
        let mut dec = Aead::new(Mode::Decrypt, algorithm, &k, *nonce).unwrap();
281
        let plaintext = dec.decrypt(aad, 0, ct).unwrap();
282
        assert_eq!(&plaintext[..], pt);
283
    }
284
285
    fn decrypt(
286
        algorithm: AeadAlgorithms,
287
        key: &[u8],
288
        nonce: &[u8; NONCE_LEN],
289
        seq: SequenceNumber,
290
        aad: &[u8],
291
        pt: &[u8],
292
        ct: &[u8],
293
    ) {
294
        let k = Aead::import_key(algorithm, key).unwrap();
295
        let mut dec = Aead::new(Mode::Decrypt, algorithm, &k, *nonce).unwrap();
296
        let plaintext = dec.decrypt(aad, seq, ct).unwrap();
297
        assert_eq!(&plaintext[..], pt);
298
    }
299
300
    /// This tests the AEAD in QUIC in combination with the HKDF code.
301
    /// This is an AEAD-only example.
302
    #[test]
303
    fn quic_retry() {
304
        const KEY: &[u8] = &[
305
            0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68,
306
            0xc8, 0x4e,
307
        ];
308
        const NONCE: &[u8; NONCE_LEN] = &[
309
            0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb,
310
        ];
311
        const AAD: &[u8] = &[
312
            0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0xff, 0x00, 0x00, 0x00, 0x01,
313
            0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65,
314
            0x6e,
315
        ];
316
        const CT: &[u8] = &[
317
            0x04, 0xa2, 0x65, 0xba, 0x2e, 0xff, 0x4d, 0x82, 0x90, 0x58, 0xfb, 0x3f, 0x0f, 0x24,
318
            0x96, 0xba,
319
        ];
320
        check0(AeadAlgorithms::Aes128Gcm, KEY, NONCE, AAD, &[], CT);
321
    }
322
323
    #[test]
324
    fn quic_server_initial() {
325
        const ALG: AeadAlgorithms = AeadAlgorithms::Aes128Gcm;
326
        const KEY: &[u8] = &[
327
            0xcf, 0x3a, 0x53, 0x31, 0x65, 0x3c, 0x36, 0x4c, 0x88, 0xf0, 0xf3, 0x79, 0xb6, 0x06,
328
            0x7e, 0x37,
329
        ];
330
        const NONCE_BASE: &[u8; NONCE_LEN] = &[
331
            0x0a, 0xc1, 0x49, 0x3c, 0xa1, 0x90, 0x58, 0x53, 0xb0, 0xbb, 0xa0, 0x3e,
332
        ];
333
        // Note that this integrates the sequence number of 1 from the example,
334
        // otherwise we can't use a sequence number of 0 to encrypt.
335
        const NONCE: &[u8; NONCE_LEN] = &[
336
            0x0a, 0xc1, 0x49, 0x3c, 0xa1, 0x90, 0x58, 0x53, 0xb0, 0xbb, 0xa0, 0x3f,
337
        ];
338
        const AAD: &[u8] = &[
339
            0xc1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62,
340
            0xb5, 0x00, 0x40, 0x75, 0x00, 0x01,
341
        ];
342
        const PT: &[u8] = &[
343
            0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, 0x00, 0x56, 0x03,
344
            0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1, 0x63, 0x2e, 0x96, 0x67, 0x78,
345
            0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf, 0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43,
346
            0x0b, 0x9a, 0x04, 0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00,
347
            0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69, 0x0b, 0x84, 0xd0,
348
            0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83,
349
            0x4d, 0x53, 0x11, 0xbc, 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03,
350
            0x04,
351
        ];
352
        const CT: &[u8] = &[
353
            0x5a, 0x48, 0x2c, 0xd0, 0x99, 0x1c, 0xd2, 0x5b, 0x0a, 0xac, 0x40, 0x6a, 0x58, 0x16,
354
            0xb6, 0x39, 0x41, 0x00, 0xf3, 0x7a, 0x1c, 0x69, 0x79, 0x75, 0x54, 0x78, 0x0b, 0xb3,
355
            0x8c, 0xc5, 0xa9, 0x9f, 0x5e, 0xde, 0x4c, 0xf7, 0x3c, 0x3e, 0xc2, 0x49, 0x3a, 0x18,
356
            0x39, 0xb3, 0xdb, 0xcb, 0xa3, 0xf6, 0xea, 0x46, 0xc5, 0xb7, 0x68, 0x4d, 0xf3, 0x54,
357
            0x8e, 0x7d, 0xde, 0xb9, 0xc3, 0xbf, 0x9c, 0x73, 0xcc, 0x3f, 0x3b, 0xde, 0xd7, 0x4b,
358
            0x56, 0x2b, 0xfb, 0x19, 0xfb, 0x84, 0x02, 0x2f, 0x8e, 0xf4, 0xcd, 0xd9, 0x37, 0x95,
359
            0xd7, 0x7d, 0x06, 0xed, 0xbb, 0x7a, 0xaf, 0x2f, 0x58, 0x89, 0x18, 0x50, 0xab, 0xbd,
360
            0xca, 0x3d, 0x20, 0x39, 0x8c, 0x27, 0x64, 0x56, 0xcb, 0xc4, 0x21, 0x58, 0x40, 0x7d,
361
            0xd0, 0x74, 0xee,
362
        ];
363
        check0(ALG, KEY, NONCE, AAD, PT, CT);
364
        decrypt(ALG, KEY, NONCE_BASE, 1, AAD, PT, CT);
365
    }
366
367
    #[test]
368
    fn quic_chacha() {
369
        const ALG: AeadAlgorithms = AeadAlgorithms::ChaCha20Poly1305;
370
        const KEY: &[u8] = &[
371
            0xc6, 0xd9, 0x8f, 0xf3, 0x44, 0x1c, 0x3f, 0xe1, 0xb2, 0x18, 0x20, 0x94, 0xf6, 0x9c,
372
            0xaa, 0x2e, 0xd4, 0xb7, 0x16, 0xb6, 0x54, 0x88, 0x96, 0x0a, 0x7a, 0x98, 0x49, 0x79,
373
            0xfb, 0x23, 0xe1, 0xc8,
374
        ];
375
        const NONCE_BASE: &[u8; NONCE_LEN] = &[
376
            0xe0, 0x45, 0x9b, 0x34, 0x74, 0xbd, 0xd0, 0xe4, 0x4a, 0x41, 0xc1, 0x44,
377
        ];
378
        // Note that this integrates the sequence number of 654360564 from the example,
379
        // otherwise we can't use a sequence number of 0 to encrypt.
380
        const NONCE: &[u8; NONCE_LEN] = &[
381
            0xe0, 0x45, 0x9b, 0x34, 0x74, 0xbd, 0xd0, 0xe4, 0x6d, 0x41, 0x7e, 0xb0,
382
        ];
383
        const AAD: &[u8] = &[0x42, 0x00, 0xbf, 0xf4];
384
        const PT: &[u8] = &[0x01];
385
        const CT: &[u8] = &[
386
            0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6, 0x90, 0x80, 0x57, 0x5d, 0x79, 0x99, 0xc2,
387
            0x5a, 0x5b, 0xfb,
388
        ];
389
        check0(ALG, KEY, NONCE, AAD, PT, CT);
390
        // Now use the real nonce and sequence number from the example.
391
        decrypt(ALG, KEY, NONCE_BASE, 654_360_564, AAD, PT, CT);
392
    }
393
394
    fn roundtrip_encrypt_with_seq(algorithm: AeadAlgorithms, key: &[u8]) {
395
        const NONCE_BASE: [u8; NONCE_LEN] = [0; NONCE_LEN];
396
        const AAD: &[u8] = b"associated";
397
        const PT: &[u8] = b"hello sframe";
398
        const SEQ: SequenceNumber = 0x0123_4567_89ab;
399
400
        fixture_init();
401
402
        let k = Aead::import_key(algorithm, key).unwrap();
403
        let mut enc = Aead::new(Mode::Encrypt, algorithm, &k, NONCE_BASE).unwrap();
404
        let ct = enc.encrypt_with_seq(AAD, SEQ, PT).unwrap();
405
406
        let mut dec = Aead::new(Mode::Decrypt, algorithm, &k, NONCE_BASE).unwrap();
407
        let pt = dec.decrypt(AAD, SEQ, &ct).unwrap();
408
        assert_eq!(&pt[..], PT);
409
    }
410
411
    #[test]
412
    fn encrypt_with_seq_aes128gcm() {
413
        const KEY: &[u8] = &[0x42; 16];
414
        roundtrip_encrypt_with_seq(AeadAlgorithms::Aes128Gcm, KEY);
415
    }
416
417
    #[test]
418
    fn encrypt_with_seq_aes256gcm() {
419
        const KEY: &[u8] = &[0x42; 32];
420
        roundtrip_encrypt_with_seq(AeadAlgorithms::Aes256Gcm, KEY);
421
    }
422
423
    #[test]
424
    fn encrypt_with_seq_chacha20poly1305() {
425
        const KEY: &[u8] = &[0x42; 32];
426
        roundtrip_encrypt_with_seq(AeadAlgorithms::ChaCha20Poly1305, KEY);
427
    }
428
}