2929#include <wolfprovider/settings.h>
3030#include <wolfprovider/alg_funcs.h>
3131
32- #if defined(WP_HAVE_AESCTR ) || defined(WP_HAVE_AESCFB )
32+ #if defined(WP_HAVE_AESCTR ) || defined(WP_HAVE_AESCFB ) || defined( WP_HAVE_AESCTS )
3333
3434/**
3535 * Data structure for AES ciphers that are streaming.
@@ -38,7 +38,7 @@ typedef struct wp_AesStreamCtx {
3838 /** wolfSSL AES object. */
3939 Aes aes ;
4040
41- /** Cipher mode - CTR. */
41+ /** Cipher mode - CTR, CFB or CTS . */
4242 int mode ;
4343
4444 /** Length of key in bytes. */
@@ -53,6 +53,11 @@ typedef struct wp_AesStreamCtx {
5353 unsigned char iv [AES_BLOCK_SIZE ];
5454 /** Original IV. */
5555 unsigned char oiv [AES_BLOCK_SIZE ];
56+
57+ #if defined(WP_HAVE_AESCTS )
58+ /* Only single shot allowed */
59+ unsigned int updated :1 ;
60+ #endif
5661} wp_AesStreamCtx ;
5762
5863
@@ -194,6 +199,9 @@ static const OSSL_PARAM* wp_cipher_gettable_ctx_params(wp_AesStreamCtx* ctx,
194199 OSSL_PARAM_uint (OSSL_CIPHER_PARAM_NUM , NULL ),
195200 OSSL_PARAM_octet_string (OSSL_CIPHER_PARAM_IV , NULL , 0 ),
196201 OSSL_PARAM_octet_string (OSSL_CIPHER_PARAM_UPDATED_IV , NULL , 0 ),
202+ #ifdef WP_HAVE_AESCTS
203+ OSSL_PARAM_utf8_string (OSSL_CIPHER_PARAM_CTS_MODE , NULL , 0 ),
204+ #endif
197205 OSSL_PARAM_END
198206 };
199207 (void )ctx ;
@@ -217,6 +225,9 @@ static const OSSL_PARAM* wp_cipher_settable_ctx_params(wp_AesStreamCtx* ctx,
217225 static const OSSL_PARAM wp_cipher_supported_settable_ctx_params [] = {
218226 OSSL_PARAM_uint (OSSL_CIPHER_PARAM_NUM , NULL ),
219227 OSSL_PARAM_uint (OSSL_CIPHER_PARAM_USE_BITS , NULL ),
228+ #ifdef WP_HAVE_AESCTS
229+ OSSL_PARAM_utf8_string (OSSL_CIPHER_PARAM_CTS_MODE , NULL , 0 ),
230+ #endif
220231 OSSL_PARAM_END
221232 };
222233 (void )ctx ;
@@ -270,6 +281,8 @@ static int wp_aes_stream_init(wp_AesStreamCtx *ctx, const unsigned char *key,
270281 const OSSL_PARAM params [], int enc )
271282{
272283 int ok = 1 ;
284+ /* Decryption is the same as encryption with CTR mode. */
285+ int dir = AES_ENCRYPTION ;
273286
274287 ctx -> enc = enc ;
275288
@@ -286,9 +299,13 @@ static int wp_aes_stream_init(wp_AesStreamCtx *ctx, const unsigned char *key,
286299 ok = 0 ;
287300 }
288301 if (ok ) {
289- /* Decryption is the same as encryption with CTR mode. */
302+ #if defined(WP_HAVE_AESCTS )
303+ if (ctx -> mode == EVP_CIPH_CBC_MODE && !enc ) {
304+ dir = AES_DECRYPTION ;
305+ }
306+ #endif
290307 int rc = wc_AesSetKey (& ctx -> aes , key , (word32 )ctx -> keyLen , iv ,
291- AES_ENCRYPTION );
308+ dir );
292309 if (rc != 0 ) {
293310 ok = 0 ;
294311 }
@@ -302,6 +319,10 @@ static int wp_aes_stream_init(wp_AesStreamCtx *ctx, const unsigned char *key,
302319 }
303320
304321 if (ok ) {
322+ #if defined(WP_HAVE_AESCTS )
323+ /* We only allow one shot, always reset on init */
324+ ctx -> updated = 0 ;
325+ #endif
305326 ok = wp_aes_stream_set_ctx_params (ctx , params );
306327 }
307328
@@ -347,8 +368,121 @@ static int wp_aes_stream_dinit(wp_AesStreamCtx *ctx, const unsigned char *key,
347368 return wp_aes_stream_init (ctx , key , keyLen , iv , ivLen , params , 0 );
348369}
349370
371+ #ifdef WP_HAVE_AESCTS
372+
373+ static int wp_aes_cts_encrypt (wp_AesStreamCtx * ctx , unsigned char * out ,
374+ const unsigned char * in , size_t inLen )
375+ {
376+ int ok = 1 ;
377+ int rc ;
378+ int blocks ;
379+ byte ctsBlock [AES_BLOCK_SIZE * 2 ];
380+
381+ /* Since AES-CTS is not a FIPS approved algo, we will never be able to call
382+ * the existing wolfSSL AES_CTS APIs with FIPS, so the implementation is
383+ * effectively copied here from wolfSSL internals. */
384+
385+ blocks = (int )((inLen + (AES_BLOCK_SIZE - 1 )) / AES_BLOCK_SIZE );
386+ blocks -= 2 ;
387+ XMEMSET (ctsBlock , 0 , AES_BLOCK_SIZE * 2 );
388+ if (ok && blocks > 0 ) {
389+ XMEMCPY (& ctx -> aes .reg , ctx -> iv , ctx -> ivLen );
390+ rc = wc_AesCbcEncrypt (& ctx -> aes , out , in , blocks * AES_BLOCK_SIZE );
391+ if (rc != 0 ) {
392+ ok = 0 ;
393+ }
394+ if (ok ) {
395+ XMEMCPY (ctx -> iv , ctx -> aes .reg , ctx -> ivLen );
396+ in += blocks * AES_BLOCK_SIZE ;
397+ out += blocks * AES_BLOCK_SIZE ;
398+ inLen -= blocks * AES_BLOCK_SIZE ;
399+ }
400+ }
401+ if (ok ) {
402+ XMEMCPY (ctsBlock , in , inLen );
403+ rc = wc_AesCbcEncrypt (& ctx -> aes , ctsBlock , ctsBlock ,
404+ AES_BLOCK_SIZE * 2 );
405+ if (rc != 0 ) {
406+ ok = 0 ;
407+ }
408+ if (ok ) {
409+ XMEMCPY (ctx -> iv , ctx -> aes .reg , ctx -> ivLen );
410+ }
411+ }
412+ if (ok ) {
413+ XMEMCPY (out , ctsBlock + AES_BLOCK_SIZE , AES_BLOCK_SIZE );
414+ XMEMCPY (out + AES_BLOCK_SIZE , ctsBlock , AES_BLOCK_SIZE );
415+ }
416+
417+ return ok ;
418+ }
419+
420+ static int wp_aes_cts_decrypt (wp_AesStreamCtx * ctx , unsigned char * out ,
421+ const unsigned char * in , size_t inLen )
422+ {
423+ int ok = 1 ;
424+ int rc ;
425+ int blocks ;
426+ byte ctsBlock [AES_BLOCK_SIZE * 2 ];
427+ byte tmp [AES_BLOCK_SIZE ];
428+ word32 partialSz ;
429+ word32 padSz ;
430+
431+ /* Since AES-CTS is not a FIPS approved algo, we will never be able to call
432+ * the existing wolfSSL AES_CTS APIs with FIPS, so the implementation is
433+ * effectively copied here from wolfSSL internals. */
434+
435+ partialSz = inLen % AES_BLOCK_SIZE ;
436+ if (partialSz == 0 ) {
437+ partialSz = AES_BLOCK_SIZE ;
438+ }
439+ padSz = AES_BLOCK_SIZE - partialSz ;
440+
441+ blocks = (int )((inLen + (AES_BLOCK_SIZE - 1 )) / AES_BLOCK_SIZE );
442+ blocks -= 2 ;
443+ XMEMSET (ctsBlock , 0 , AES_BLOCK_SIZE * 2 );
444+ if (ok && blocks > 0 ) {
445+ XMEMCPY (& ctx -> aes .reg , ctx -> iv , ctx -> ivLen );
446+ rc = wc_AesCbcDecrypt (& ctx -> aes , out , in , blocks * AES_BLOCK_SIZE );
447+ if (rc != 0 ) {
448+ ok = 0 ;
449+ }
450+ if (ok ) {
451+ XMEMCPY (ctx -> iv , ctx -> aes .reg , ctx -> ivLen );
452+ in += blocks * AES_BLOCK_SIZE ;
453+ out += blocks * AES_BLOCK_SIZE ;
454+ inLen -= blocks * AES_BLOCK_SIZE ;
455+ }
456+ }
457+ if (ok ) {
458+ XMEMCPY (ctsBlock , in , inLen );
459+ XMEMCPY (& ctx -> aes .reg , ctsBlock + AES_BLOCK_SIZE , AES_BLOCK_SIZE );
460+ rc = wc_AesCbcDecrypt (& ctx -> aes , tmp , ctsBlock , AES_BLOCK_SIZE );
461+ if (rc != 0 ) {
462+ ok = 0 ;
463+ }
464+ }
465+ if (ok ) {
466+ XMEMCPY (out + AES_BLOCK_SIZE , tmp , partialSz );
467+ XMEMCPY (ctsBlock + inLen , tmp + partialSz , padSz );
468+ XMEMCPY (& ctx -> aes .reg , ctx -> iv , AES_BLOCK_SIZE );
469+ rc = wc_AesCbcDecrypt (& ctx -> aes , out , ctsBlock + AES_BLOCK_SIZE ,
470+ AES_BLOCK_SIZE );
471+ if (rc != 0 ) {
472+ ok = 0 ;
473+ }
474+ if (ok ) {
475+ XMEMCPY (ctx -> iv , ctx -> aes .reg , ctx -> ivLen );
476+ }
477+ }
478+
479+ return ok ;
480+ }
481+
482+ #endif /* ifdef WP_HAVE_AESCTS */
483+
350484/**
351- * Encrypt/decrypt using AES-CTR or AES-CFB with wolfSSL.
485+ * Encrypt/decrypt using AES-CTR, AES-CFB or AES-CTS with wolfSSL.
352486 *
353487 * Assumes out has inLen bytes available.
354488 * Assumes whole blocks only.
@@ -363,17 +497,19 @@ static int wp_aes_stream_dinit(wp_AesStreamCtx *ctx, const unsigned char *key,
363497static int wp_aes_stream_doit (wp_AesStreamCtx * ctx , unsigned char * out ,
364498 const unsigned char * in , size_t inLen )
365499{
366- int ok = 0 ;
500+ int ok = 1 ;
367501
368502#ifdef WP_HAVE_AESCTR
369503 if (ctx -> mode == EVP_CIPH_CTR_MODE ) {
370504 int rc ;
371505
372506 XMEMCPY (& ctx -> aes .reg , ctx -> iv , ctx -> ivLen );
373507 rc = wc_AesCtrEncrypt (& ctx -> aes , out , in , (word32 )inLen );
374- if (rc == 0 ) {
508+ if (rc != 0 ) {
509+ ok = 0 ;
510+ }
511+ if (ok ) {
375512 XMEMCPY (ctx -> iv , ctx -> aes .reg , ctx -> ivLen );
376- ok = 1 ;
377513 }
378514 }
379515 else
@@ -388,9 +524,33 @@ static int wp_aes_stream_doit(wp_AesStreamCtx *ctx, unsigned char *out,
388524 }else {
389525 rc = wc_AesCfbDecrypt (& ctx -> aes , out , in , (word32 )inLen );
390526 }
391- if (rc == 0 ) {
527+ if (rc != 0 ) {
528+ ok = 0 ;
529+ }
530+ if (ok ) {
392531 XMEMCPY (ctx -> iv , ctx -> aes .reg , ctx -> ivLen );
393- ok = 1 ;
532+ }
533+ }
534+ else
535+ #endif
536+ #ifdef WP_HAVE_AESCTS
537+ if (ctx -> mode == EVP_CIPH_CBC_MODE ) {
538+ if (ctx -> updated ) {
539+ ok = 0 ;
540+ }
541+ if (inLen < AES_BLOCK_SIZE ) {
542+ ok = 0 ;
543+ }
544+ if (ok ) {
545+ if (ctx -> enc ) {
546+ ok = wp_aes_cts_encrypt (ctx , out , in , inLen );
547+ }
548+ else {
549+ ok = wp_aes_cts_decrypt (ctx , out , in , inLen );
550+ }
551+ }
552+ if (ok ) {
553+ ctx -> updated = 1 ;
394554 }
395555 }
396556 else
@@ -534,6 +694,14 @@ static int wp_aes_stream_get_ctx_params(wp_AesStreamCtx* ctx,
534694 ok = 0 ;
535695 }
536696 }
697+ #ifdef WP_HAVE_AESCTS
698+ if (ok && ctx -> mode == EVP_CIPH_CBC_MODE ) {
699+ p = OSSL_PARAM_locate (params , OSSL_CIPHER_PARAM_CTS_MODE );
700+ if ((p != NULL ) && (!OSSL_PARAM_set_utf8_string (p , "CS3" ))) {
701+ ok = 0 ;
702+ }
703+ }
704+ #endif
537705
538706 WOLFPROV_LEAVE (WP_LOG_CIPHER , __FILE__ ":" WOLFPROV_STRINGIZE (__LINE__ ), ok );
539707 return ok ;
@@ -568,6 +736,25 @@ static int wp_aes_stream_set_ctx_params(wp_AesStreamCtx *ctx,
568736 ok = 0 ;
569737 }
570738 (void )val ;
739+ #ifdef WP_HAVE_AESCTS
740+ if (ok && ctx -> mode == EVP_CIPH_CBC_MODE ) {
741+ char cts_mode [4 ];
742+ char * pcts = cts_mode ;
743+ const OSSL_PARAM * p = NULL ;
744+ XMEMSET (cts_mode , 0 , sizeof (cts_mode ));
745+
746+ p = OSSL_PARAM_locate_const (params , OSSL_CIPHER_PARAM_CTS_MODE );
747+ if (p != NULL ) {
748+ if (!OSSL_PARAM_get_utf8_string (p , & pcts ,
749+ sizeof (cts_mode ))) {
750+ ok = 0 ;
751+ }
752+ if (ok && (XSTRCMP (cts_mode , "CS3" ) != 0 )) {
753+ ok = 0 ; /* Only CS3 supported */
754+ }
755+ }
756+ }
757+ #endif
571758 }
572759
573760 WOLFPROV_LEAVE (WP_LOG_CIPHER , __FILE__ ":" WOLFPROV_STRINGIZE (__LINE__ ), ok );
@@ -580,7 +767,7 @@ static int wp_aes_stream_set_ctx_params(wp_AesStreamCtx *ctx,
580767 * @param [in, out] ctx AES stream context object.
581768 * @param [in] kBits Number of bits in a valid key.
582769 * @param [in] ivBits Number of bits in a valid IV. 0 indicates no IV.
583- * @param [in] mode AES stream mode: CTR.
770+ * @param [in] mode AES stream mode: CTR, CFB or CTS .
584771 * @return 1 on success.
585772 * @return 0 on failure.
586773 */
@@ -685,5 +872,20 @@ IMPLEMENT_AES_STREAM(cfb, CFB, 192, 128)
685872IMPLEMENT_AES_STREAM (cfb , CFB , 128 , 128 )
686873#endif /* WP_HAVE_AESCFB */
687874
688- #endif /* WP_HAVE_AESCTR || WP_HAVE_AESCFB */
875+ /*
876+ * AES CTS
877+ *
878+ * Even though AES-CTS is a block cipher, since we will only be supporting a
879+ * single-shot mode it actually behaves more like a stream cipher.
880+ */
881+ #ifdef WP_HAVE_AESCTS
882+ /** wp_aes256cts_functions */
883+ IMPLEMENT_AES_STREAM (cts , CBC , 256 , 128 )
884+ /** wp_aes192cts_functions */
885+ IMPLEMENT_AES_STREAM (cts , CBC , 192 , 128 )
886+ /** wp_aes128cts_functions */
887+ IMPLEMENT_AES_STREAM (cts , CBC , 128 , 128 )
888+ #endif /* WP_HAVE_AESCTS */
889+
890+ #endif /* WP_HAVE_AESCTR || WP_HAVE_AESCFB || WP_HAVE_AESCTS */
689891
0 commit comments