44
55use pyo3:: types:: { PyAnyMethods , PyBytesMethods } ;
66
7- use crate :: backend:: aead:: AesGcm ;
7+ use crate :: backend:: aead:: { AesGcm , ChaCha20Poly1305 } ;
88use crate :: backend:: kdf:: { hkdf_extract, HkdfExpand } ;
99use crate :: backend:: x25519;
1010use crate :: buf:: CffiBuf ;
@@ -29,6 +29,11 @@ mod aead_params {
2929 pub const AES_128_GCM_NK : usize = 16 ;
3030 pub const AES_128_GCM_NN : usize = 12 ;
3131 pub const AES_128_GCM_NT : usize = 16 ;
32+
33+ pub const CHACHA20_POLY1305_ID : u16 = 0x0003 ;
34+ pub const CHACHA20_POLY1305_NK : usize = 32 ;
35+ pub const CHACHA20_POLY1305_NN : usize = 12 ;
36+ pub const CHACHA20_POLY1305_NT : usize = 16 ;
3237}
3338
3439#[ allow( clippy:: upper_case_acronyms) ]
@@ -70,10 +75,42 @@ pub(crate) enum KDF {
7075#[ derive( Clone , PartialEq , Eq , Hash ) ]
7176pub ( crate ) enum AEAD {
7277 AES_128_GCM ,
78+ CHACHA20_POLY1305 ,
79+ }
80+
81+ impl AEAD {
82+ fn id ( & self ) -> u16 {
83+ match self {
84+ AEAD :: AES_128_GCM => aead_params:: AES_128_GCM_ID ,
85+ AEAD :: CHACHA20_POLY1305 => aead_params:: CHACHA20_POLY1305_ID ,
86+ }
87+ }
88+
89+ fn key_length ( & self ) -> usize {
90+ match self {
91+ AEAD :: AES_128_GCM => aead_params:: AES_128_GCM_NK ,
92+ AEAD :: CHACHA20_POLY1305 => aead_params:: CHACHA20_POLY1305_NK ,
93+ }
94+ }
95+
96+ fn nonce_length ( & self ) -> usize {
97+ match self {
98+ AEAD :: AES_128_GCM => aead_params:: AES_128_GCM_NN ,
99+ AEAD :: CHACHA20_POLY1305 => aead_params:: CHACHA20_POLY1305_NN ,
100+ }
101+ }
102+
103+ fn tag_length ( & self ) -> usize {
104+ match self {
105+ AEAD :: AES_128_GCM => aead_params:: AES_128_GCM_NT ,
106+ AEAD :: CHACHA20_POLY1305 => aead_params:: CHACHA20_POLY1305_NT ,
107+ }
108+ }
73109}
74110
75111#[ pyo3:: pyclass( frozen, module = "cryptography.hazmat.bindings._rust.openssl.hpke" ) ]
76112pub ( crate ) struct Suite {
113+ aead : AEAD ,
77114 kem_suite_id : [ u8 ; 5 ] ,
78115 hpke_suite_id : [ u8 ; 10 ] ,
79116}
@@ -235,6 +272,50 @@ impl Suite {
235272 self . hkdf_expand ( py, prk, & labeled_info, length)
236273 }
237274
275+ fn aead_encrypt < ' p > (
276+ & self ,
277+ py : pyo3:: Python < ' p > ,
278+ key : & pyo3:: Bound < ' _ , pyo3:: types:: PyBytes > ,
279+ nonce : & pyo3:: Bound < ' _ , pyo3:: types:: PyBytes > ,
280+ plaintext : CffiBuf < ' _ > ,
281+ aad : Option < CffiBuf < ' _ > > ,
282+ ) -> CryptographyResult < pyo3:: Bound < ' p , pyo3:: types:: PyBytes > > {
283+ let key_obj = key. clone ( ) . unbind ( ) . into_any ( ) ;
284+ let nonce_buf = CffiBuf :: from_bytes ( py, nonce. as_bytes ( ) ) ;
285+ match & self . aead {
286+ AEAD :: AES_128_GCM => {
287+ let cipher = AesGcm :: new ( py, key_obj) ?;
288+ cipher. encrypt ( py, nonce_buf, plaintext, aad)
289+ }
290+ AEAD :: CHACHA20_POLY1305 => {
291+ let cipher = ChaCha20Poly1305 :: new ( py, key_obj) ?;
292+ cipher. encrypt ( py, nonce_buf, plaintext, aad)
293+ }
294+ }
295+ }
296+
297+ fn aead_decrypt < ' p > (
298+ & self ,
299+ py : pyo3:: Python < ' p > ,
300+ key : & pyo3:: Bound < ' _ , pyo3:: types:: PyBytes > ,
301+ nonce : & pyo3:: Bound < ' _ , pyo3:: types:: PyBytes > ,
302+ ciphertext : & [ u8 ] ,
303+ aad : Option < CffiBuf < ' _ > > ,
304+ ) -> CryptographyResult < pyo3:: Bound < ' p , pyo3:: types:: PyBytes > > {
305+ let key_obj = key. clone ( ) . unbind ( ) . into_any ( ) ;
306+ let nonce_buf = CffiBuf :: from_bytes ( py, nonce. as_bytes ( ) ) ;
307+ match & self . aead {
308+ AEAD :: AES_128_GCM => {
309+ let cipher = AesGcm :: new ( py, key_obj) ?;
310+ cipher. decrypt ( py, nonce_buf, CffiBuf :: from_bytes ( py, ciphertext) , aad)
311+ }
312+ AEAD :: CHACHA20_POLY1305 => {
313+ let cipher = ChaCha20Poly1305 :: new ( py, key_obj) ?;
314+ cipher. decrypt ( py, nonce_buf, CffiBuf :: from_bytes ( py, ciphertext) , aad)
315+ }
316+ }
317+ }
318+
238319 fn encrypt_inner < ' p > (
239320 & self ,
240321 py : pyo3:: Python < ' p > ,
@@ -248,8 +329,7 @@ impl Suite {
248329 let ( shared_secret, enc) = self . encap ( py, public_key) ?;
249330 let ( key, base_nonce) = self . key_schedule ( py, shared_secret. as_bytes ( ) , info_bytes) ?;
250331
251- let aesgcm = AesGcm :: new ( py, pyo3:: types:: PyBytes :: new ( py, & key) . unbind ( ) . into_any ( ) ) ?;
252- let ct = aesgcm. encrypt ( py, CffiBuf :: from_bytes ( py, & base_nonce) , plaintext, aad) ?;
332+ let ct = self . aead_encrypt ( py, & key, & base_nonce, plaintext, aad) ?;
253333
254334 let enc_bytes = enc. as_bytes ( ) ;
255335 let ct_bytes = ct. as_bytes ( ) ;
@@ -273,7 +353,7 @@ impl Suite {
273353 aad : Option < CffiBuf < ' _ > > ,
274354 ) -> CryptographyResult < pyo3:: Bound < ' p , pyo3:: types:: PyBytes > > {
275355 let ct_bytes = ciphertext. as_bytes ( ) ;
276- if ct_bytes. len ( ) < kem_params:: X25519_NENC + aead_params :: AES_128_GCM_NT {
356+ if ct_bytes. len ( ) < kem_params:: X25519_NENC + self . aead . tag_length ( ) {
277357 return Err ( CryptographyError :: from ( exceptions:: InvalidTag :: new_err ( ( ) ) ) ) ;
278358 }
279359
@@ -286,23 +366,17 @@ impl Suite {
286366 . map_err ( |_| CryptographyError :: from ( exceptions:: InvalidTag :: new_err ( ( ) ) ) ) ?;
287367 let ( key, base_nonce) = self . key_schedule ( py, shared_secret. as_bytes ( ) , info_bytes) ?;
288368
289- let aesgcm = AesGcm :: new ( py, pyo3:: types:: PyBytes :: new ( py, & key) . unbind ( ) . into_any ( ) ) ?;
290- aesgcm. decrypt (
291- py,
292- CffiBuf :: from_bytes ( py, & base_nonce) ,
293- CffiBuf :: from_bytes ( py, ct) ,
294- aad,
295- )
369+ self . aead_decrypt ( py, & key, & base_nonce, ct, aad)
296370 }
297371
298- fn key_schedule (
372+ fn key_schedule < ' p > (
299373 & self ,
300- py : pyo3:: Python < ' _ > ,
374+ py : pyo3:: Python < ' p > ,
301375 shared_secret : & [ u8 ] ,
302376 info : & [ u8 ] ,
303377 ) -> CryptographyResult < (
304- [ u8 ; aead_params :: AES_128_GCM_NK ] ,
305- [ u8 ; aead_params :: AES_128_GCM_NN ] ,
378+ pyo3 :: Bound < ' p , pyo3 :: types :: PyBytes > ,
379+ pyo3 :: Bound < ' p , pyo3 :: types :: PyBytes > ,
306380 ) > {
307381 let psk_id_hash = self . hpke_labeled_extract ( py, None , b"psk_id_hash" , b"" ) ?;
308382 let info_hash = self . hpke_labeled_extract ( py, None , b"info_hash" , info) ?;
@@ -312,34 +386,29 @@ impl Suite {
312386
313387 let secret = self . hpke_labeled_extract ( py, Some ( shared_secret) , b"secret" , b"" ) ?;
314388
315- let key_vec = self . hpke_labeled_expand (
389+ let key = self . hpke_labeled_expand (
316390 py,
317391 & secret,
318392 b"key" ,
319393 & key_schedule_context,
320- aead_params :: AES_128_GCM_NK ,
394+ self . aead . key_length ( ) ,
321395 ) ?;
322- let nonce_vec = self . hpke_labeled_expand (
396+ let base_nonce = self . hpke_labeled_expand (
323397 py,
324398 & secret,
325399 b"base_nonce" ,
326400 & key_schedule_context,
327- aead_params :: AES_128_GCM_NN ,
401+ self . aead . nonce_length ( ) ,
328402 ) ?;
329403
330- let mut key = [ 0u8 ; aead_params:: AES_128_GCM_NK ] ;
331- let mut base_nonce = [ 0u8 ; aead_params:: AES_128_GCM_NN ] ;
332- key. copy_from_slice ( key_vec. as_bytes ( ) ) ;
333- base_nonce. copy_from_slice ( nonce_vec. as_bytes ( ) ) ;
334-
335404 Ok ( ( key, base_nonce) )
336405 }
337406}
338407
339408#[ pyo3:: pymethods]
340409impl Suite {
341410 #[ new]
342- fn new ( _kem : KEM , _kdf : KDF , _aead : AEAD ) -> CryptographyResult < Suite > {
411+ fn new ( _kem : KEM , _kdf : KDF , aead : AEAD ) -> CryptographyResult < Suite > {
343412 // Build suite IDs
344413 let mut kem_suite_id = [ 0u8 ; 5 ] ;
345414 kem_suite_id[ ..3 ] . copy_from_slice ( b"KEM" ) ;
@@ -349,9 +418,10 @@ impl Suite {
349418 hpke_suite_id[ ..4 ] . copy_from_slice ( b"HPKE" ) ;
350419 hpke_suite_id[ 4 ..6 ] . copy_from_slice ( & kem_params:: X25519_ID . to_be_bytes ( ) ) ;
351420 hpke_suite_id[ 6 ..8 ] . copy_from_slice ( & kdf_params:: HKDF_SHA256_ID . to_be_bytes ( ) ) ;
352- hpke_suite_id[ 8 ..10 ] . copy_from_slice ( & aead_params :: AES_128_GCM_ID . to_be_bytes ( ) ) ;
421+ hpke_suite_id[ 8 ..10 ] . copy_from_slice ( & aead . id ( ) . to_be_bytes ( ) ) ;
353422
354423 Ok ( Suite {
424+ aead,
355425 kem_suite_id,
356426 hpke_suite_id,
357427 } )
0 commit comments