Skip to content

Commit 951f26b

Browse files
authored
Merge pull request #189 from ColtonWilley/wp_aes_cts
Implement AES-CTS
2 parents 43cf24a + 64a234d commit 951f26b

7 files changed

Lines changed: 748 additions & 14 deletions

File tree

include/wolfprovider/alg_funcs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ typedef void (*DFUNC)(void);
120120
#define WP_NAMES_AES_128_WRAP \
121121
"AES-128-WRAP:id-aes128-wrap:AES128-WRAP:2.16.840.1.101.3.4.1.5"
122122

123+
#define WP_NAMES_AES_256_CTS "AES-256-CBC-CTS"
124+
#define WP_NAMES_AES_192_CTS "AES-192-CBC-CTS"
125+
#define WP_NAMES_AES_128_CTS "AES-128-CBC-CTS"
126+
123127
#define WP_NAMES_DES_EDE3_CBC "DES-EDE3-CBC:DES3:1.2.840.113549.3.7"
124128

125129
/* Internal cipher flags. */
@@ -288,6 +292,10 @@ extern const OSSL_DISPATCH wp_aes256wrap_functions[];
288292
extern const OSSL_DISPATCH wp_aes192wrap_functions[];
289293
extern const OSSL_DISPATCH wp_aes128wrap_functions[];
290294

295+
extern const OSSL_DISPATCH wp_aes256cts_functions[];
296+
extern const OSSL_DISPATCH wp_aes192cts_functions[];
297+
extern const OSSL_DISPATCH wp_aes128cts_functions[];
298+
291299
extern const OSSL_DISPATCH wp_des3cbc_functions[];
292300

293301
/* MAC implementations. */

include/wolfprovider/settings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
#endif
8282
#ifndef NO_AES_CBC
8383
#define WP_HAVE_AESCBC
84+
#define WP_HAVE_AESCTS
8485
#endif
8586
#ifndef NO_DES3
8687
#define WP_HAVE_DES3CBC

src/wp_aes_stream.c

Lines changed: 214 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
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,
363497
static 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)
685872
IMPLEMENT_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

src/wp_wolfprov.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,16 @@ static const OSSL_ALGORITHM wolfprov_ciphers[] = {
482482
"" },
483483
#endif
484484

485+
#ifdef WP_HAVE_AESCTS
486+
/* AES-CTS */
487+
{ WP_NAMES_AES_256_CTS, WOLFPROV_PROPERTIES, wp_aes256cts_functions,
488+
"" },
489+
{ WP_NAMES_AES_192_CTS, WOLFPROV_PROPERTIES, wp_aes192cts_functions,
490+
"" },
491+
{ WP_NAMES_AES_128_CTS, WOLFPROV_PROPERTIES, wp_aes128cts_functions,
492+
"" },
493+
#endif
494+
485495
#ifdef HAVE_AES_KEYWRAP
486496
/* AES Kwy Wrap - unpadded */
487497
{ WP_NAMES_AES_256_WRAP, WOLFPROV_PROPERTIES, wp_aes256wrap_functions,

0 commit comments

Comments
 (0)