Skip to content

Commit b8a869b

Browse files
authored
Merge pull request #185 from haydenroche5/aes_ctr_left
Fix AES-CTR leftover data issue.
2 parents 1201d52 + 3c4c136 commit b8a869b

File tree

4 files changed

+174
-28
lines changed

4 files changed

+174
-28
lines changed

src/we_aes_ctr.c

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
*/
3333
typedef struct we_AesCtr
3434
{
35-
/** The wolfSSL AES data object. */
36-
Aes aes;
35+
/* The wolfSSL AES data object. */
36+
Aes aes;
3737
} we_AesCtr;
3838

3939

@@ -52,6 +52,7 @@ static int we_aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
5252
int ret = 1;
5353
int rc;
5454
we_AesCtr *aes;
55+
const unsigned char* tmpIv = iv;
5556

5657
WOLFENGINE_ENTER(WE_LOG_CIPHER, "we_aes_ctr_init");
5758
WOLFENGINE_MSG_VERBOSE(WE_LOG_CIPHER, "ARGS [ctx = %p, key = %p, iv = %p, "
@@ -66,23 +67,40 @@ static int we_aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
6667
ret = 0;
6768
}
6869

69-
if ((ret == 1) && (key != NULL)) {
70+
if (ret == 1) {
7071
rc = wc_AesInit(&aes->aes, NULL, INVALID_DEVID);
7172
if (rc != 0) {
7273
WOLFENGINE_ERROR_FUNC(WE_LOG_CIPHER, "wc_AesInit", rc);
7374
ret = 0;
7475
}
7576
}
76-
7777
if ((ret == 1) && (key != NULL)) {
78-
/* No decryption for CTR. */
79-
rc = wc_AesSetKey(&aes->aes, key, EVP_CIPHER_CTX_key_length(ctx), iv,
78+
if (tmpIv == NULL) {
79+
/* If no IV given, attempt to use previously set ctx IV. */
80+
tmpIv = EVP_CIPHER_CTX_iv_noconst(ctx);
81+
}
82+
/* No decryption for CTR (hence AES_ENCRYPTION). */
83+
rc = wc_AesSetKey(&aes->aes, key, EVP_CIPHER_CTX_key_length(ctx), tmpIv,
8084
AES_ENCRYPTION);
8185
if (rc != 0) {
8286
WOLFENGINE_ERROR_FUNC(WE_LOG_CIPHER, "wc_AesSetKey", rc);
8387
ret = 0;
8488
}
8589
}
90+
if ((ret == 1) && (iv != NULL)) {
91+
rc = wc_AesSetIV(&aes->aes, iv);
92+
if (rc != 0) {
93+
WOLFENGINE_ERROR_FUNC(WE_LOG_CIPHER, "wc_AesSetIV", rc);
94+
ret = -1;
95+
}
96+
else {
97+
/*
98+
* wc_AesSetIV should clear this field, but it doesn't in some
99+
* wolfSSL versions.
100+
*/
101+
aes->aes.left = 0;
102+
}
103+
}
86104

87105
WOLFENGINE_LEAVE(WE_LOG_CIPHER, "we_aes_ctr_init", ret);
88106

@@ -99,7 +117,7 @@ static int we_aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
99117
* @param in [in] Data to encrypt/decrypt.
100118
* @param len [in] Length of data to encrypt/decrypt.
101119
* @return -1 on failure.
102-
* @return 1 on success.
120+
* @return Length of output data on success.
103121
*/
104122
static int we_aes_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
105123
const unsigned char *in, size_t len)
@@ -121,26 +139,27 @@ static int we_aes_ctr_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
121139
}
122140

123141
if (ret == 1) {
124-
rc = wc_AesSetIV(&aes->aes, EVP_CIPHER_CTX_iv_noconst(ctx));
125-
if (rc != 0) {
126-
WOLFENGINE_ERROR_FUNC(WE_LOG_CIPHER, "wc_AesSetIV", rc);
127-
ret = -1;
142+
if (in != NULL && len > 0) {
143+
rc = wc_AesCtrEncrypt(&aes->aes, out, in, (word32)len);
144+
if (rc != 0) {
145+
WOLFENGINE_ERROR_FUNC(WE_LOG_CIPHER, "wc_AesCtrEncrypt", rc);
146+
ret = -1;
147+
}
148+
else {
149+
unsigned int num = EVP_CIPHER_CTX_num(ctx);
150+
num = (num + len) % AES_128_KEY_SIZE;
151+
EVP_CIPHER_CTX_set_num(ctx, num);
152+
153+
XMEMCPY(EVP_CIPHER_CTX_iv_noconst(ctx), aes->aes.reg,
154+
AES_BLOCK_SIZE);
155+
156+
ret = (int)len;
157+
}
128158
}
129-
}
130-
if (ret == 1) {
131-
rc = wc_AesCtrEncrypt(&aes->aes, out, in, (word32)len);
132-
if (rc != 0) {
133-
WOLFENGINE_ERROR_FUNC(WE_LOG_CIPHER, "wc_AesCtrEncrypt", rc);
134-
ret = -1;
159+
else if (in == NULL) {
160+
ret = 0;
135161
}
136162
}
137-
if (ret == 1) {
138-
unsigned int num = EVP_CIPHER_CTX_num(ctx);
139-
num = (num + len) % AES_128_KEY_SIZE;
140-
EVP_CIPHER_CTX_set_num(ctx, num);
141-
142-
XMEMCPY(EVP_CIPHER_CTX_iv_noconst(ctx), aes->aes.reg, AES_BLOCK_SIZE);
143-
}
144163

145164
WOLFENGINE_LEAVE(WE_LOG_CIPHER, "we_aes_ctr_cipher", ret);
146165

@@ -196,7 +215,9 @@ static int we_aes_ctr_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
196215

197216
/** Flags for AES-CTR method. */
198217
#define AES_CTR_FLAGS \
199-
(EVP_CIPH_FLAG_DEFAULT_ASN1 | \
218+
(EVP_CIPH_ALWAYS_CALL_INIT | \
219+
EVP_CIPH_FLAG_CUSTOM_CIPHER | \
220+
EVP_CIPH_FLAG_DEFAULT_ASN1 | \
200221
EVP_CIPH_CTR_MODE)
201222

202223
/** AES128-CTR EVP cipher method. */

test/test_cipher.c

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,11 +514,134 @@ int test_aes256_ctr_stream(ENGINE *e, void *data)
514514
return err;
515515
}
516516

517-
/* OpenSSL allows the user to call EVP_CipherInit with NULL key or IV. In the
517+
/*
518+
* This test exercises a bug in some versions of wolfSSL where partial block
519+
* data (i.e. data that didn't align to the AES block size of 16 bytes) from a
520+
* previous AES-CTR operation would be mixed in with the next operation's input
521+
* data, even when the IV was changed in between. When the key or IV changes,
522+
* any partial block data should not be used.
523+
*/
524+
int test_aes_ctr_leftover_data_regression(ENGINE *e, void *data)
525+
{
526+
enum {
527+
NUM_PACKETS = 2,
528+
PACKET_SIZE = AES_BLOCK_SIZE + 1
529+
};
530+
int err = 0;
531+
const unsigned char key[AES_128_KEY_SIZE] = {
532+
0x37, 0xe8, 0x46, 0x48, 0xf4, 0xd6, 0xa7, 0x28, 0xc6, 0xd5, 0x2e, 0x3b,
533+
0xf4, 0xc4, 0x46, 0x66
534+
};
535+
const unsigned char ivs[NUM_PACKETS][AES_BLOCK_SIZE] = {
536+
{
537+
0x88, 0x93, 0x6f, 0xfd, 0x7d, 0x94, 0x21, 0xcc, 0x40, 0x64, 0xde,
538+
0x8a, 0xb9, 0xaf, 0xdd, 0xe4
539+
},
540+
{
541+
0x0f, 0x10, 0x9c, 0xc9, 0x25, 0x9a, 0x53, 0xf0, 0xd3, 0x92, 0xdf,
542+
0x35, 0xb2, 0x35, 0xa6, 0xd8
543+
}
544+
};
545+
const unsigned char packets[NUM_PACKETS][PACKET_SIZE] = {
546+
{
547+
0x3c, 0x89, 0x18, 0x76, 0xfc, 0xae, 0xdc, 0xee, 0xab, 0xf2, 0xf7,
548+
0x56, 0x47, 0x1f, 0xe9, 0x20, 0x67
549+
},
550+
{
551+
0xb5, 0x3b, 0x8d, 0xa1, 0xe7, 0x0a, 0x46, 0x56, 0xf6, 0xfd, 0xeb,
552+
0x85, 0x61, 0xd8, 0xaf, 0xac, 0xfb
553+
}
554+
};
555+
EVP_CIPHER_CTX* encCtx = NULL;
556+
EVP_CIPHER_CTX* decCtx = NULL;
557+
unsigned char encText[PACKET_SIZE];
558+
unsigned char decText[PACKET_SIZE];
559+
int i;
560+
561+
(void)data;
562+
563+
if (err == 0) {
564+
err = (encCtx = EVP_CIPHER_CTX_new()) == NULL;
565+
}
566+
if (err == 0) {
567+
err = (decCtx = EVP_CIPHER_CTX_new()) == NULL;
568+
}
569+
570+
/* Set key. */
571+
if (err == 0) {
572+
err = EVP_CipherInit_ex(encCtx, EVP_aes_128_ctr(), NULL, key,
573+
NULL, -1) != 1;
574+
}
575+
if (err == 0) {
576+
err = EVP_CipherInit_ex(decCtx, EVP_aes_128_ctr(), e, key,
577+
NULL, -1) != 1;
578+
}
579+
580+
for (i = 0; err == 0 && i < NUM_PACKETS; ++ i) {
581+
/* Set IV. */
582+
if (err == 0) {
583+
err = EVP_CipherInit_ex(encCtx, NULL, NULL, NULL, ivs[i], 1) != 1;
584+
}
585+
if (err == 0) {
586+
err = EVP_CipherInit_ex(decCtx, NULL, e, NULL, ivs[i], 0) != 1;
587+
}
588+
/* Encrypt. */
589+
if (err == 0) {
590+
err = EVP_Cipher(encCtx, encText, packets[i], PACKET_SIZE) < 0;
591+
}
592+
/* Decrypt. */
593+
if (err == 0) {
594+
err = EVP_Cipher(decCtx, decText, encText, PACKET_SIZE) < 0;
595+
}
596+
/* Ensure decrypted and plaintext match. */
597+
if (err == 0) {
598+
err = memcmp(decText, packets[i], PACKET_SIZE) != 0;
599+
}
600+
}
601+
602+
/* Try the other way, now. Encrypt with wolfEngine, decrypt with wolfSSL. */
603+
if (err == 0) {
604+
err = EVP_CipherInit_ex(encCtx, EVP_aes_128_ctr(), e, key,
605+
NULL, -1) != 1;
606+
}
607+
if (err == 0) {
608+
err = EVP_CipherInit_ex(decCtx, EVP_aes_128_ctr(), NULL, key,
609+
NULL, -1) != 1;
610+
}
611+
612+
for (i = 0; err == 0 && i < NUM_PACKETS; ++ i) {
613+
if (err == 0) {
614+
err = EVP_CipherInit_ex(encCtx, NULL, e, NULL, ivs[i], 1) != 1;
615+
}
616+
if (err == 0) {
617+
err = EVP_CipherInit_ex(decCtx, NULL, NULL, NULL, ivs[i], 0) != 1;
618+
}
619+
if (err == 0) {
620+
err = EVP_Cipher(encCtx, encText, packets[i], PACKET_SIZE) < 0;
621+
}
622+
if (err == 0) {
623+
err = EVP_Cipher(decCtx, decText, encText, PACKET_SIZE) < 0;
624+
}
625+
if (err == 0) {
626+
err = memcmp(decText, packets[i], PACKET_SIZE) != 0;
627+
}
628+
}
629+
630+
if (encCtx != NULL)
631+
EVP_CIPHER_CTX_free(encCtx);
632+
if (decCtx != NULL)
633+
EVP_CIPHER_CTX_free(decCtx);
634+
635+
return err;
636+
}
637+
638+
/*
639+
* OpenSSL allows the user to call EVP_CipherInit with NULL key or IV. In the
518640
* past, setting the IV first (with key NULL) with wolfEngine and then setting
519641
* the key (with IV NULL) would result in the IV getting set to 0s on the call
520642
* to set the key. This was discovered in testing with OpenSSH. This is a
521-
* regression test to ensure we preserve the IV in this scenario. */
643+
* regression test to ensure we preserve the IV in this scenario.
644+
*/
522645
int test_aes_ctr_iv_init_regression(ENGINE *e, void *data)
523646
{
524647
int err = 0;

test/unit.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ TEST_CASE test_case[] = {
117117
TEST_DECL(test_aes128_ctr_stream, NULL),
118118
TEST_DECL(test_aes192_ctr_stream, NULL),
119119
TEST_DECL(test_aes256_ctr_stream, NULL),
120+
TEST_DECL(test_aes_ctr_leftover_data_regression, NULL),
120121
TEST_DECL(test_aes_ctr_iv_init_regression, NULL),
121122
#endif
122123
#ifdef WE_HAVE_AESGCM

test/unit.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ int test_aes128_ctr_stream(ENGINE *e, void *data);
157157
int test_aes192_ctr_stream(ENGINE *e, void *data);
158158
int test_aes256_ctr_stream(ENGINE *e, void *data);
159159

160-
int test_aes_ctr_iv_init_regression(ENGINE *, void *);
160+
int test_aes_ctr_leftover_data_regression(ENGINE *e, void *data);
161+
int test_aes_ctr_iv_init_regression(ENGINE *e, void *data);
161162

162163
#endif
163164

0 commit comments

Comments
 (0)