@@ -49,33 +49,21 @@ fn make_aes_ctx(key: &SymKey) -> Res<Context> {
4949 SECItemBorrowed :: make_empty ( ) . as_ref ( ) ,
5050 )
5151 } )
52- . or ( Err ( Error :: CipherInit ) )
52+ . map_err ( |_| Error :: CipherInit )
5353}
5454
5555pub enum Key {
5656 /// AES-ECB header-protection context. `PK11_CloneContext` is not supported for
57- /// AES-ECB, so we store the `SymKey` and recreate `ctx` lazily: `mask` initialises
58- /// it on first use when `ctx` is `None `.
57+ /// AES-ECB, so the `SymKey` is stored alongside `ctx` to enable duplication via
58+ /// `try_clone `.
5959 #[ non_exhaustive]
60- Aes { ctx : Option < Context > , key : SymKey } ,
60+ Aes { ctx : Context , key : SymKey } ,
6161 /// The `ChaCha20` mask invokes `PK11_Encrypt` on each call because the counter
6262 /// and nonce change per invocation.
6363 #[ non_exhaustive]
6464 Chacha ( SymKey ) ,
6565}
6666
67- impl Clone for Key {
68- fn clone ( & self ) -> Self {
69- match self {
70- Self :: Aes { key, .. } => Self :: Aes {
71- ctx : None ,
72- key : key. clone ( ) ,
73- } ,
74- Self :: Chacha ( k) => Self :: Chacha ( k. clone ( ) ) ,
75- }
76- }
77- }
78-
7967impl Debug for Key {
8068 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
8169 write ! ( f, "hp::Key" )
@@ -125,7 +113,7 @@ impl Key {
125113
126114 let res = match cipher {
127115 TLS_AES_128_GCM_SHA256 | TLS_AES_256_GCM_SHA384 => {
128- let ctx = Some ( make_aes_ctx ( & key) ?) ;
116+ let ctx = make_aes_ctx ( & key) ?;
129117 Self :: Aes { ctx, key }
130118 }
131119 TLS_CHACHA20_POLY1305_SHA256 => Self :: Chacha ( key) ,
@@ -146,6 +134,22 @@ impl Key {
146134 }
147135 }
148136
137+ /// Duplicate this key, creating a new independent instance.
138+ ///
139+ /// # Errors
140+ ///
141+ /// Errors if NSS context creation fails for AES keys.
142+ pub fn try_clone ( & self ) -> Res < Self > {
143+ match self {
144+ Self :: Aes { key, .. } => {
145+ let key = key. clone ( ) ;
146+ let ctx = make_aes_ctx ( & key) ?;
147+ Ok ( Self :: Aes { ctx, key } )
148+ }
149+ Self :: Chacha ( k) => Ok ( Self :: Chacha ( k. clone ( ) ) ) ,
150+ }
151+ }
152+
149153 /// Generate a header protection mask for QUIC.
150154 ///
151155 /// # Errors
@@ -154,21 +158,17 @@ impl Key {
154158 ///
155159 /// # Panics
156160 ///
157- /// When the mechanism for our key is not supported .
158- pub fn mask ( & mut self , sample : & [ u8 ; Self :: SAMPLE_SIZE ] ) -> Res < [ u8 ; Self :: SAMPLE_SIZE ] > {
161+ /// In debug builds, if NSS returns an unexpected output length .
162+ pub fn mask ( & self , sample : & [ u8 ; Self :: SAMPLE_SIZE ] ) -> Res < [ u8 ; Self :: SAMPLE_SIZE ] > {
159163 let mut output = [ 0 ; Self :: SAMPLE_SIZE ] ;
160164
161165 match self {
162- Self :: Aes { ctx, key } => {
163- let ctx = if let Some ( c) = ctx {
164- c
165- } else {
166- ctx. insert ( make_aes_ctx ( key) ?)
167- } ;
166+ Self :: Aes { ctx, .. } => {
168167 let mut output_len: c_int = 0 ;
169- // SAFETY: `Deref` on `Context` yields a copy of the raw `*mut PK11Context`;
170- // no Rust reference to the pointee is created, and `&mut self` guarantees
171- // exclusive access to this `Key`.
168+ // SAFETY: `Deref` on `Context` copies the raw `*mut PK11Context` pointer
169+ // value; no Rust reference to the pointee is created. `Key` contains raw
170+ // pointers (`!Sync`), so concurrent invocations are impossible, and
171+ // AES-ECB full-block operations retain no inter-call state in the context.
172172 secstatus_to_res ( unsafe {
173173 PK11_CipherOp (
174174 * * ctx,
0 commit comments