Skip to content

Commit f829678

Browse files
authored
feat: Add AEAD encryption function with explicit sequence number (#40)
* Add AEAD encryption function with explicit sequence number * Fixes * formatting * Fixes
1 parent 58849c0 commit f829678

1 file changed

Lines changed: 88 additions & 3 deletions

File tree

src/aead.rs

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,15 @@ impl Aead {
512512
})
513513
}
514514

515+
fn make_nonce(nonce: &mut [u8; NONCE_LEN], seq: SequenceNumber) {
516+
for (n, &s) in nonce[NONCE_LEN - COUNTER_LEN..]
517+
.iter_mut()
518+
.zip(&seq.to_be_bytes())
519+
{
520+
*n ^= s;
521+
}
522+
}
523+
515524
pub fn import_key(algorithm: AeadAlgorithms, key: &[u8]) -> Result<SymKey, Error> {
516525
let slot = p11::Slot::internal().map_err(|_| Error::Internal)?;
517526

@@ -590,6 +599,49 @@ impl Aead {
590599
Ok(ct)
591600
}
592601

602+
/// Encrypt with an explicit sequence number. Mirrors `decrypt`'s nonce
603+
/// construction: the final nonce is `nonce_base XOR encode_be(seq)` over
604+
/// the trailing 8 bytes. The NSS PKCS#11 context's internal counter is
605+
/// not used (`CKG_NO_GENERATE`). The caller must never reuse
606+
/// `(nonce_base, seq)` with the same key.
607+
pub fn encrypt_with_seq(
608+
&mut self,
609+
aad: &[u8],
610+
seq: SequenceNumber,
611+
pt: &[u8],
612+
) -> Result<Vec<u8>, Error> {
613+
crate::init()?;
614+
615+
assert_eq!(self.mode, Mode::Encrypt);
616+
let mut nonce = self.nonce_base;
617+
Self::make_nonce(&mut nonce, seq);
618+
let mut ct = vec![0; pt.len() + TAG_LEN];
619+
let mut ct_len: c_int = 0;
620+
let mut tag = vec![0; TAG_LEN];
621+
secstatus_to_res(unsafe {
622+
PK11_AEADOp(
623+
*self.ctx,
624+
CK_GENERATOR_FUNCTION::from(CKG_NO_GENERATE),
625+
c_int_len(NONCE_LEN - COUNTER_LEN)?,
626+
nonce.as_mut_ptr(),
627+
c_int_len(nonce.len())?,
628+
aad.as_ptr(),
629+
c_int_len(aad.len())?,
630+
ct.as_mut_ptr(),
631+
&raw mut ct_len,
632+
c_int_len(ct.len())?,
633+
tag.as_mut_ptr(),
634+
c_int_len(tag.len())?,
635+
pt.as_ptr(),
636+
c_int_len(pt.len())?,
637+
)
638+
})?;
639+
ct.truncate(usize::try_from(ct_len).map_err(|_| Error::IntegerOverflow)?);
640+
debug_assert_eq!(ct.len(), pt.len());
641+
ct.append(&mut tag);
642+
Ok(ct)
643+
}
644+
593645
pub fn decrypt(
594646
&mut self,
595647
aad: &[u8],
@@ -600,9 +652,7 @@ impl Aead {
600652

601653
assert_eq!(self.mode, Mode::Decrypt);
602654
let mut nonce = self.nonce_base;
603-
for (i, n) in nonce.iter_mut().rev().take(COUNTER_LEN).enumerate() {
604-
*n ^= u8::try_from((seq >> (8 * i)) & 0xff).map_err(|_| Error::IntegerOverflow)?;
605-
}
655+
Self::make_nonce(&mut nonce, seq);
606656
let mut pt = vec![0; ct.len()]; // NSS needs more space than it uses for plaintext.
607657
let mut pt_len: c_int = 0;
608658
let pt_expected = ct.len().checked_sub(TAG_LEN).ok_or(Error::AeadTruncated)?;
@@ -767,4 +817,39 @@ mod test {
767817
// Now use the real nonce and sequence number from the example.
768818
decrypt(ALG, KEY, NONCE_BASE, 654_360_564, AAD, PT, CT);
769819
}
820+
821+
fn roundtrip_encrypt_with_seq(algorithm: AeadAlgorithms, key: &[u8]) {
822+
const NONCE_BASE: [u8; NONCE_LEN] = [0; NONCE_LEN];
823+
const AAD: &[u8] = b"associated";
824+
const PT: &[u8] = b"hello sframe";
825+
const SEQ: SequenceNumber = 0x0123_4567_89ab;
826+
827+
fixture_init();
828+
829+
let k = Aead::import_key(algorithm, key).unwrap();
830+
let mut enc = Aead::new(Mode::Encrypt, algorithm, &k, NONCE_BASE).unwrap();
831+
let ct = enc.encrypt_with_seq(AAD, SEQ, PT).unwrap();
832+
833+
let mut dec = Aead::new(Mode::Decrypt, algorithm, &k, NONCE_BASE).unwrap();
834+
let pt = dec.decrypt(AAD, SEQ, &ct).unwrap();
835+
assert_eq!(&pt[..], PT);
836+
}
837+
838+
#[test]
839+
fn encrypt_with_seq_aes128gcm() {
840+
const KEY: &[u8] = &[0x42; 16];
841+
roundtrip_encrypt_with_seq(AeadAlgorithms::Aes128Gcm, KEY);
842+
}
843+
844+
#[test]
845+
fn encrypt_with_seq_aes256gcm() {
846+
const KEY: &[u8] = &[0x42; 32];
847+
roundtrip_encrypt_with_seq(AeadAlgorithms::Aes256Gcm, KEY);
848+
}
849+
850+
#[test]
851+
fn encrypt_with_seq_chacha20poly1305() {
852+
const KEY: &[u8] = &[0x42; 32];
853+
roundtrip_encrypt_with_seq(AeadAlgorithms::ChaCha20Poly1305, KEY);
854+
}
770855
}

0 commit comments

Comments
 (0)