@@ -396,33 +396,39 @@ class AesGcmStream(object):
396396 block_size = 16
397397 _key_sizes = [16 , 24 , 32 ]
398398 _native_type = "Aes *"
399- _aad = bytes ()
400- _tag_bytes = 16
401- _mode = None
399+ _delete = _lib .wc_AesFree
402400
403401 def __init__ (self , key , IV , tag_bytes = 16 ):
404402 """
405403 tag_bytes is the number of bytes to use for the authentication tag during encryption
406404 """
407405 key = t2b (key )
408406 IV = t2b (IV )
409- if tag_bytes < 4 or tag_bytes > 16 :
410- raise ValueError ("tag_bytes must be between 4 and 16" )
407+ # NIST SP 800-38D valid GCM tag lengths: 16, 15, 14, 13, 12, 8, 4 bytes.
408+ if tag_bytes not in (4 , 8 , 12 , 13 , 14 , 15 , 16 ):
409+ raise ValueError (
410+ "tag_bytes must be one of 4, 8, 12, 13, 14, 15, or 16" )
411+ # Per-instance state: AAD, tag length, and current mode (enc/dec).
412+ self ._aad = bytes ()
411413 self ._tag_bytes = tag_bytes
414+ self ._mode = None
412415 if len (key ) not in self ._key_sizes :
413416 raise ValueError ("key must be %s in length, not %d" %
414417 (self ._key_sizes , len (key )))
418+ self ._init_done = False
415419 self ._native_object = _ffi .new (self ._native_type )
416420 ret = _lib .wc_AesInit (self ._native_object , _ffi .NULL , - 2 )
417421 if ret < 0 :
418422 raise WolfCryptError ("AES init error (%d)" % ret )
423+ self ._init_done = True
419424 ret = _lib .wc_AesGcmInit (self ._native_object , key , len (key ), IV , len (IV ))
420425 if ret < 0 :
421426 raise WolfCryptError ("Init error (%d)" % ret )
422427
423428 def __del__ (self ):
424- if hasattr (self , '_native_object' ):
425- _lib .wc_AesFree (self ._native_object )
429+ if getattr (self , '_init_done' , False ):
430+ self ._delete (self ._native_object )
431+ self ._init_done = False
426432
427433 def set_aad (self , data ):
428434 """
@@ -446,11 +452,11 @@ def encrypt(self, data):
446452 aad = self ._aad
447453 elif self ._mode == _DECRYPTION :
448454 raise WolfCryptError ("Class instance already in use for decryption" )
449- self . _buf = _ffi .new ("byte[%d]" % (len (data )))
450- ret = _lib .wc_AesGcmEncryptUpdate (self ._native_object , self . _buf , data , len (data ), aad , len (aad ))
455+ buf = _ffi .new ("byte[%d]" % (len (data )))
456+ ret = _lib .wc_AesGcmEncryptUpdate (self ._native_object , buf , data , len (data ), aad , len (aad ))
451457 if ret < 0 :
452458 raise WolfCryptError ("Encryption error (%d)" % ret )
453- return bytes (self . _buf )
459+ return bytes (buf )
454460
455461 def decrypt (self , data ):
456462 """
@@ -463,11 +469,11 @@ def decrypt(self, data):
463469 aad = self ._aad
464470 elif self ._mode == _ENCRYPTION :
465471 raise WolfCryptError ("Class instance already in use for encryption" )
466- self . _buf = _ffi .new ("byte[%d]" % (len (data )))
467- ret = _lib .wc_AesGcmDecryptUpdate (self ._native_object , self . _buf , data , len (data ), aad , len (aad ))
472+ buf = _ffi .new ("byte[%d]" % (len (data )))
473+ ret = _lib .wc_AesGcmDecryptUpdate (self ._native_object , buf , data , len (data ), aad , len (aad ))
468474 if ret < 0 :
469475 raise WolfCryptError ("Decryption error (%d)" % ret )
470- return bytes (self . _buf )
476+ return bytes (buf )
471477
472478 def final (self , authTag = None ):
473479 """
@@ -505,7 +511,9 @@ class ChaCha(_Cipher):
505511 _IV_nonce = b""
506512 _IV_counter = 0
507513
508- def __init__ (self , key = "" , size = 32 ):
514+ def __init__ (self , key = "" , size = 32 ): # pylint: disable=unused-argument
515+ # size is kept for backwards compatibility; key length is now
516+ # derived from the actual key and validated against _key_sizes.
509517 self ._native_object = _ffi .new (self ._native_type )
510518 self ._enc = None
511519 self ._dec = None
@@ -552,7 +560,9 @@ def set_iv(self, nonce, counter = 0):
552560 raise ValueError ("nonce must be %d bytes, got %d" %
553561 (self ._NONCE_SIZE , len (self ._IV_nonce )))
554562 self ._IV_counter = counter
555- self ._set_key (0 )
563+ ret = self ._set_key (0 )
564+ if ret < 0 :
565+ raise WolfCryptError ("ChaCha set_iv error (%d)" % ret )
556566
557567if _lib .CHACHA20_POLY1305_ENABLED :
558568 class ChaCha20Poly1305 (object ):
@@ -864,6 +874,9 @@ def make_key(cls, size, rng=None, hash_type=None):
864874 if rsa .output_size <= 0 : # pragma: no cover
865875 raise WolfCryptError ("Invalid key size error (%d)" % ret )
866876
877+ # Retain RNG reference defensively.
878+ rsa ._rng = rng
879+
867880 return rsa
868881
869882 def __init__ (self , key = None , hash_type = None ): # pylint: disable=super-init-not-called
@@ -1231,7 +1244,11 @@ def make_key(cls, size, rng=None):
12311244 ret = _lib .wc_ecc_set_rng (ecc .native_object , rng .native_object )
12321245 if ret < 0 :
12331246 raise WolfCryptError ("Error setting ECC RNG (%d)" % ret )
1234- ecc ._rng = rng
1247+
1248+ # Retain the RNG so it outlives the ECC key. Even outside the
1249+ # timing-resistance path, wolfSSL internals may retain a pointer
1250+ # to the RNG; keeping the reference avoids any UAF risk.
1251+ ecc ._rng = rng
12351252
12361253 return ecc
12371254
@@ -1504,6 +1521,10 @@ def make_key(cls, size, rng=None):
15041521 if ret < 0 :
15051522 raise WolfCryptError ("Key generation error (%d)" % ret )
15061523
1524+ # Retain RNG reference defensively; wolfSSL may retain a pointer
1525+ # internally on some builds.
1526+ ed25519 ._rng = rng
1527+
15071528 return ed25519
15081529
15091530 def decode_key (self , key , pub = None ):
@@ -1706,6 +1727,10 @@ def make_key(cls, size, rng=None):
17061727 if ret < 0 :
17071728 raise WolfCryptError ("Key generation error (%d)" % ret )
17081729
1730+ # Retain RNG reference defensively; wolfSSL may retain a pointer
1731+ # internally on some builds.
1732+ ed448 ._rng = rng
1733+
17091734 return ed448
17101735
17111736 def decode_key (self , key , pub = None ):
@@ -1979,6 +2004,9 @@ def make_key(cls, mlkem_type, rng=None):
19792004 if ret < 0 : # pragma: no cover
19802005 raise WolfCryptError ("wc_KyberKey_MakeKey() error (%d)" % ret )
19812006
2007+ # Retain RNG reference defensively.
2008+ mlkem_priv ._rng = rng
2009+
19822010 return mlkem_priv
19832011
19842012 @classmethod
@@ -2226,6 +2254,9 @@ def make_key(cls, mldsa_type, rng=None):
22262254 if ret < 0 : # pragma: no cover
22272255 raise WolfCryptError ("wc_dilithium_make_key() error (%d)" % ret )
22282256
2257+ # Retain RNG reference defensively.
2258+ mldsa_priv ._rng = rng
2259+
22292260 return mldsa_priv
22302261
22312262 @property
0 commit comments