@@ -293,16 +293,24 @@ def encrypt(self, associated_data, nonce, plaintext):
293293 """
294294 Encrypt plaintext data using the nonce provided. The associated
295295 data is not encrypted but is included in the authentication tag.
296-
296+
297+ Associated data may be provided as single str or bytes, or as a
298+ list of str or bytes in case of multiple blocks.
299+
297300 Returns a tuple of the IV and ciphertext.
298301 """
299- associated_data = t2b (associated_data )
302+ # Prepare the associated data blocks. Make sure to hold on to the
303+ # returned references until the C function has been called in order
304+ # to prevent garbage collection of them until the function is done.
305+ associated_data , _refs = (
306+ AesSiv ._prepare_associated_data (associated_data ))
300307 nonce = t2b (nonce )
301308 plaintext = t2b (plaintext )
302309 siv = _ffi .new ("byte[%d]" % AesSiv .block_size )
303310 ciphertext = _ffi .new ("byte[%d]" % len (plaintext ))
304- ret = _lib .wc_AesSivEncrypt (self ._key , len (self ._key ), associated_data , len (associated_data ),
305- nonce , len (nonce ), plaintext , len (plaintext ), siv , ciphertext )
311+ ret = _lib .wc_AesSivEncrypt_ex (self ._key , len (self ._key ),
312+ associated_data , len (associated_data ), nonce , len (nonce ),
313+ plaintext , len (plaintext ), siv , ciphertext )
306314 if ret < 0 : # pragma: no cover
307315 raise WolfCryptError ("AES-SIV encryption error (%d)" % ret )
308316 return _ffi .buffer (siv )[:], _ffi .buffer (ciphertext )[:]
@@ -312,22 +320,68 @@ def decrypt(self, associated_data, nonce, siv, ciphertext):
312320 Decrypt the ciphertext using the nonce and SIV provided.
313321 The integrity of the associated data is checked.
314322
323+ Associated data may be provided as single str or bytes, or as a
324+ list of str or bytes in case of multiple blocks.
325+
315326 Returns the decrypted plaintext.
316327 """
317- associated_data = t2b (associated_data )
328+ # Prepare the associated data blocks. Make sure to hold on to the
329+ # returned references until the C function has been called in order
330+ # to prevent garbage collection of them until the function is done.
331+ associated_data , _refs = (
332+ AesSiv ._prepare_associated_data (associated_data ))
318333 nonce = t2b (nonce )
319334 siv = t2b (siv )
320335 if len (siv ) != AesSiv .block_size :
321336 raise ValueError ("SIV must be %s in length, not %d" %
322337 (AesSiv .block_size , len (siv )))
323338 ciphertext = t2b (ciphertext )
324339 plaintext = _ffi .new ("byte[%d]" % len (ciphertext ))
325- ref = _lib .wc_AesSivDecrypt (self ._key , len (self ._key ), associated_data , len (associated_data ),
326- nonce , len (nonce ), ciphertext , len (ciphertext ), siv , plaintext )
340+ ref = _lib .wc_AesSivDecrypt_ex (self ._key , len (self ._key ),
341+ associated_data , len (associated_data ), nonce , len (nonce ),
342+ ciphertext , len (ciphertext ), siv , plaintext )
327343 if ref < 0 :
328344 raise WolfCryptError ("AES-SIV decryption error (%d)" % ref )
329345 return _ffi .buffer (plaintext )[:]
330346
347+ @staticmethod
348+ def _prepare_associated_data (associated_data ):
349+ """
350+ Prepare associated data for sending to C library.
351+
352+ Associated data may be provided as single str or bytes, or as a
353+ list of str or bytes in case of multiple blocks.
354+
355+ The result is a tuple of the list of cffi cdata pointers to
356+ AesSivAssoc structures, as well as the converted associated
357+ data blocks. The caller **must** hold on to these until the
358+ C function has been called, in order to make sure that the memory
359+ is not freed by the FFI garbage collector before the data is read.
360+ """
361+ if (isinstance (associated_data , str ) or isinstance (associated_data , bytes )):
362+ # A single block is provided.
363+ # Make sure we have bytes.
364+ associated_data = t2b (associated_data )
365+ result = _ffi .new ("AesSivAssoc[1]" )
366+ result [0 ].assoc = _ffi .from_buffer (associated_data )
367+ result [0 ].assocSz = len (associated_data )
368+ else :
369+ # It is assumed that a list is provided.
370+ num_blocks = len (associated_data )
371+ if (num_blocks > 126 ):
372+ raise WolfCryptError ("AES-SIV does not support more than 126 blocks "
373+ "of associated data, got: %d" % num_blocks )
374+ # Make sure we have bytes.
375+ associated_data = [t2b (block ) for block in associated_data ]
376+ result = _ffi .new ("AesSivAssoc[]" , num_blocks )
377+ for index , block in enumerate (associated_data ):
378+ result [index ].assoc = _ffi .from_buffer (block )
379+ result [index ].assocSz = len (block )
380+ # Return the converted associated data blocks so the caller can
381+ # hold on to them until the function has been called.
382+ return result , associated_data
383+
384+
331385if _lib .AESGCM_STREAM_ENABLED :
332386 class AesGcmStream (object ):
333387 """
0 commit comments