@@ -396,33 +396,38 @@ 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 )
426431
427432 def set_aad (self , data ):
428433 """
@@ -446,11 +451,11 @@ def encrypt(self, data):
446451 aad = self ._aad
447452 elif self ._mode == _DECRYPTION :
448453 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 ))
454+ buf = _ffi .new ("byte[%d]" % (len (data )))
455+ ret = _lib .wc_AesGcmEncryptUpdate (self ._native_object , buf , data , len (data ), aad , len (aad ))
451456 if ret < 0 :
452457 raise WolfCryptError ("Encryption error (%d)" % ret )
453- return bytes (self . _buf )
458+ return bytes (buf )
454459
455460 def decrypt (self , data ):
456461 """
@@ -463,11 +468,11 @@ def decrypt(self, data):
463468 aad = self ._aad
464469 elif self ._mode == _ENCRYPTION :
465470 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 ))
471+ buf = _ffi .new ("byte[%d]" % (len (data )))
472+ ret = _lib .wc_AesGcmDecryptUpdate (self ._native_object , buf , data , len (data ), aad , len (aad ))
468473 if ret < 0 :
469474 raise WolfCryptError ("Decryption error (%d)" % ret )
470- return bytes (self . _buf )
475+ return bytes (buf )
471476
472477 def final (self , authTag = None ):
473478 """
@@ -505,7 +510,9 @@ class ChaCha(_Cipher):
505510 _IV_nonce = b""
506511 _IV_counter = 0
507512
508- def __init__ (self , key = "" , size = 32 ):
513+ def __init__ (self , key = "" , size = 32 ): # pylint: disable=unused-argument
514+ # size is kept for backwards compatibility; key length is now
515+ # derived from the actual key and validated against _key_sizes.
509516 self ._native_object = _ffi .new (self ._native_type )
510517 self ._enc = None
511518 self ._dec = None
@@ -552,7 +559,9 @@ def set_iv(self, nonce, counter = 0):
552559 raise ValueError ("nonce must be %d bytes, got %d" %
553560 (self ._NONCE_SIZE , len (self ._IV_nonce )))
554561 self ._IV_counter = counter
555- self ._set_key (0 )
562+ ret = self ._set_key (0 )
563+ if ret < 0 :
564+ raise WolfCryptError ("ChaCha set_iv error (%d)" % ret )
556565
557566if _lib .CHACHA20_POLY1305_ENABLED :
558567 class ChaCha20Poly1305 (object ):
@@ -1231,7 +1240,11 @@ def make_key(cls, size, rng=None):
12311240 ret = _lib .wc_ecc_set_rng (ecc .native_object , rng .native_object )
12321241 if ret < 0 :
12331242 raise WolfCryptError ("Error setting ECC RNG (%d)" % ret )
1234- ecc ._rng = rng
1243+
1244+ # Retain the RNG so it outlives the ECC key. Even outside the
1245+ # timing-resistance path, wolfSSL internals may retain a pointer
1246+ # to the RNG; keeping the reference avoids any UAF risk.
1247+ ecc ._rng = rng
12351248
12361249 return ecc
12371250
@@ -1504,6 +1517,10 @@ def make_key(cls, size, rng=None):
15041517 if ret < 0 :
15051518 raise WolfCryptError ("Key generation error (%d)" % ret )
15061519
1520+ # Retain RNG reference defensively; wolfSSL may retain a pointer
1521+ # internally on some builds.
1522+ ed25519 ._rng = rng
1523+
15071524 return ed25519
15081525
15091526 def decode_key (self , key , pub = None ):
@@ -1706,6 +1723,10 @@ def make_key(cls, size, rng=None):
17061723 if ret < 0 :
17071724 raise WolfCryptError ("Key generation error (%d)" % ret )
17081725
1726+ # Retain RNG reference defensively; wolfSSL may retain a pointer
1727+ # internally on some builds.
1728+ ed448 ._rng = rng
1729+
17091730 return ed448
17101731
17111732 def decode_key (self , key , pub = None ):
@@ -1979,6 +2000,9 @@ def make_key(cls, mlkem_type, rng=None):
19792000 if ret < 0 : # pragma: no cover
19802001 raise WolfCryptError ("wc_KyberKey_MakeKey() error (%d)" % ret )
19812002
2003+ # Retain RNG reference defensively.
2004+ mlkem_priv ._rng = rng
2005+
19822006 return mlkem_priv
19832007
19842008 @classmethod
@@ -2226,6 +2250,9 @@ def make_key(cls, mldsa_type, rng=None):
22262250 if ret < 0 : # pragma: no cover
22272251 raise WolfCryptError ("wc_dilithium_make_key() error (%d)" % ret )
22282252
2253+ # Retain RNG reference defensively.
2254+ mldsa_priv ._rng = rng
2255+
22292256 return mldsa_priv
22302257
22312258 @property
0 commit comments