@@ -10,35 +10,37 @@ use crate::types::{ContentType, Sequence};
1010/// Explicit nonce length for DTLS AEAD records.
1111///
1212/// The explicit nonce is transmitted with each record.
13+ #[ cfg( test) ]
1314pub ( crate ) const DTLS_EXPLICIT_NONCE_LEN : usize = 8 ;
1415
1516/// GCM authentication tag length.
1617///
1718/// The tag is appended to the ciphertext.
19+ #[ cfg( test) ]
1820pub ( crate ) const GCM_TAG_LEN : usize = 16 ;
1921
20- /// Overhead per AEAD record (explicit nonce + tag).
22+ /// Overhead per DTLS 1.2 AES-GCM record (explicit nonce + tag).
2123///
2224/// This equals 24 bytes for DTLS AES-GCM.
25+ #[ cfg( test) ]
2326pub ( crate ) const DTLS_AEAD_OVERHEAD : usize = DTLS_EXPLICIT_NONCE_LEN + GCM_TAG_LEN ; // 24
2427
25- /// Compute AAD length from plaintext length for AEAD records.
26- /// For DTLS AEAD this is the plaintext length.
28+ /// Compute AAD length from plaintext length for DTLS 1.2 AES-GCM records.
2729#[ inline]
2830#[ cfg( test) ]
2931pub fn aad_len_from_plaintext_len ( plaintext_len : u16 ) -> u16 {
3032 plaintext_len
3133}
3234
33- /// Compute fragment length from plaintext length for AEAD records.
35+ /// Compute fragment length from plaintext length for DTLS 1.2 AES-GCM records.
3436/// fragment_len = explicit_nonce(8) + ciphertext(plaintext_len + 16 tag)
3537#[ inline]
3638#[ cfg( test) ]
3739pub fn fragment_len_from_plaintext_len ( plaintext_len : usize ) -> usize {
3840 DTLS_EXPLICIT_NONCE_LEN + plaintext_len + GCM_TAG_LEN
3941}
4042
41- /// Compute plaintext length from fragment length, if large enough .
43+ /// Compute plaintext length from fragment length for DTLS 1.2 AES-GCM records .
4244/// Returns None if the fragment is smaller than the mandatory AEAD overhead.
4345#[ inline]
4446#[ cfg( test) ]
@@ -47,13 +49,51 @@ pub fn plaintext_len_from_fragment_len(fragment_len: usize) -> Option<usize> {
4749}
4850
4951/// Fixed IV portion for DTLS AEAD.
52+ ///
53+ /// DTLS 1.2 uses:
54+ /// - AES-GCM: 4-byte fixed IV + 8-byte explicit nonce (per record)
55+ /// - ChaCha20-Poly1305: 12-byte fixed IV + 0-byte explicit nonce
5056#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
51- pub ( crate ) struct Iv ( pub [ u8 ; 4 ] ) ;
57+ pub ( crate ) struct Iv {
58+ bytes : [ u8 ; 12 ] ,
59+ len : u8 ,
60+ }
5261
5362impl Iv {
5463 pub ( crate ) fn new ( iv : & [ u8 ] ) -> Self {
55- // invariant: the iv is 4 bytes.
56- Self ( iv. try_into ( ) . unwrap ( ) )
64+ assert ! (
65+ iv. len( ) <= 12 ,
66+ "invalid IV length: expected <= 12, got {}" ,
67+ iv. len( )
68+ ) ;
69+ let mut bytes = [ 0u8 ; 12 ] ;
70+ bytes[ ..iv. len ( ) ] . copy_from_slice ( iv) ;
71+ Self {
72+ bytes,
73+ len : iv. len ( ) as u8 ,
74+ }
75+ }
76+
77+ pub ( crate ) fn len ( & self ) -> usize {
78+ self . len as usize
79+ }
80+
81+ pub ( crate ) fn as_slice ( & self ) -> & [ u8 ] {
82+ & self . bytes [ ..self . len ( ) ]
83+ }
84+
85+ /// Returns the full 12-byte backing array.
86+ ///
87+ /// Only valid for 12-byte IVs (ChaCha20-Poly1305). For 4-byte IVs
88+ /// (AES-GCM), use [`as_slice`] instead.
89+ pub ( crate ) fn as_12_bytes ( & self ) -> & [ u8 ; 12 ] {
90+ debug_assert_eq ! (
91+ self . len( ) ,
92+ 12 ,
93+ "as_12_bytes called on {}-byte IV" ,
94+ self . len( )
95+ ) ;
96+ & self . bytes
5797 }
5898}
5999
@@ -64,15 +104,25 @@ pub struct Nonce(pub [u8; 12]);
64104impl Nonce {
65105 /// Create a new AEAD nonce by combining fixed IV and explicit nonce (DTLS 1.2).
66106 pub ( crate ) fn new ( iv : Iv , explicit_nonce : & [ u8 ] ) -> Self {
107+ assert_eq ! (
108+ iv. len( ) + explicit_nonce. len( ) ,
109+ 12 ,
110+ "invalid DTLS 1.2 nonce parts: iv_len={}, explicit_nonce_len={}" ,
111+ iv. len( ) ,
112+ explicit_nonce. len( )
113+ ) ;
67114 let mut nonce = [ 0u8 ; 12 ] ;
68- nonce[ ..4 ] . copy_from_slice ( & iv. 0 ) ;
69- nonce[ 4 ..] . copy_from_slice ( explicit_nonce) ;
115+ let iv_len = iv. len ( ) ;
116+ nonce[ ..iv_len] . copy_from_slice ( iv. as_slice ( ) ) ;
117+ nonce[ iv_len..] . copy_from_slice ( explicit_nonce) ;
70118 Self ( nonce)
71119 }
72120
73- /// Create a DTLS 1.3 nonce by XORing the IV with the padded sequence number.
121+ /// Create a nonce by XORing the IV with the padded sequence number.
74122 ///
75- /// Per RFC 8446 Section 5.3: nonce = iv XOR pad_left(seq, iv_len)
123+ /// Used by both DTLS 1.2 (ChaCha20-Poly1305) and DTLS 1.3:
124+ /// nonce = iv XOR pad_left(sequence_number, 12)
125+ /// See RFC 8446 Section 5.3 / RFC 7905.
76126 pub ( crate ) fn xor ( iv : & [ u8 ; 12 ] , seq : u64 ) -> Self {
77127 let mut nonce = * iv;
78128 let seq_bytes = seq. to_be_bytes ( ) ; // 8 bytes
0 commit comments