Add AES Key Wrap mode. This is needed in order to support Web Crypto. https://code.google.com/p/chromium/issues/detail?id=396407 Change-Id: I900d8cad2716c2e3341eeae153659502326c9173 Reviewed-on: https://boringssl-review.googlesource.com/1335 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/cipher/aead_test.c b/crypto/cipher/aead_test.c index 516ec31..f0f3cf4 100644 --- a/crypto/cipher/aead_test.c +++ b/crypto/cipher/aead_test.c
@@ -168,6 +168,10 @@ aead = EVP_aead_chacha20_poly1305(); } else if (strcmp(argv[1], "rc4-md5") == 0) { aead = EVP_aead_rc4_md5_tls(); + } else if (strcmp(argv[1], "aes-128-key-wrap") == 0) { + aead = EVP_aead_aes_128_key_wrap(); + } else if (strcmp(argv[1], "aes-256-key-wrap") == 0) { + aead = EVP_aead_aes_256_key_wrap(); } else { fprintf(stderr, "Unknown AEAD: %s\n", argv[1]); return 2;
diff --git a/crypto/cipher/aes_128_key_wrap_tests.txt b/crypto/cipher/aes_128_key_wrap_tests.txt new file mode 100644 index 0000000..561ec90 --- /dev/null +++ b/crypto/cipher/aes_128_key_wrap_tests.txt
@@ -0,0 +1,9 @@ +# These test vectors have been taken from +# http://csrc.nist.gov/groups/ST/toolkit/documents/kms/key-wrap.pdf + +KEY: 000102030405060708090A0B0C0D0E0F +NONCE: +IN: 00112233445566778899AABBCCDDEEFF +AD: +CT: 1FA68B0A8112B447AEF34BD8FB5A7B82 +TAG: 9D3E862371D2CFE5
diff --git a/crypto/cipher/aes_256_key_wrap_tests.txt b/crypto/cipher/aes_256_key_wrap_tests.txt new file mode 100644 index 0000000..92d3a04 --- /dev/null +++ b/crypto/cipher/aes_256_key_wrap_tests.txt
@@ -0,0 +1,23 @@ +# These test vectors have been taken from +# http://csrc.nist.gov/groups/ST/toolkit/documents/kms/key-wrap.pdf + +KEY: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +NONCE: +IN: 00112233445566778899AABBCCDDEEFF +AD: +CT: 64E8C3F9CE0F5BA263E9777905818A2A +TAG: 93C8191E7D6E8AE7 + +KEY: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +NONCE: +IN: 00112233445566778899AABBCCDDEEFF0001020304050607 +AD: +CT: A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB895 +TAG: 8CD5D17D6B254DA1 + +KEY: 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F +NONCE: +IN: 00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F +AD: +CT: 28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43B +TAG: FB988B9B7A02DD21
diff --git a/crypto/cipher/cipher_error.c b/crypto/cipher/cipher_error.c index fa28f63..ed72436 100644 --- a/crypto/cipher/cipher_error.c +++ b/crypto/cipher/cipher_error.c
@@ -28,6 +28,9 @@ {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_aes_gcm_init, 0), "aead_aes_gcm_init"}, {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_aes_gcm_open, 0), "aead_aes_gcm_open"}, {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_aes_gcm_seal, 0), "aead_aes_gcm_seal"}, + {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_aes_key_wrap_init, 0), "aead_aes_key_wrap_init"}, + {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_aes_key_wrap_open, 0), "aead_aes_key_wrap_open"}, + {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_aes_key_wrap_seal, 0), "aead_aes_key_wrap_seal"}, {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_chacha20_poly1305_init, 0), "aead_chacha20_poly1305_init"}, {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_chacha20_poly1305_open, 0), "aead_chacha20_poly1305_open"}, {ERR_PACK(ERR_LIB_CIPHER, CIPHER_F_aead_chacha20_poly1305_seal, 0), "aead_chacha20_poly1305_seal"}, @@ -52,7 +55,11 @@ {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_OUTPUT_ALIASES_INPUT), "OUTPUT_ALIASES_INPUT"}, {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_TAG_TOO_LARGE), "TAG_TOO_LARGE"}, {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_TOO_LARGE), "TOO_LARGE"}, + {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_UNSUPPORTED_AD_SIZE), "UNSUPPORTED_AD_SIZE"}, + {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_UNSUPPORTED_INPUT_SIZE), "UNSUPPORTED_INPUT_SIZE"}, {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_UNSUPPORTED_KEY_SIZE), "UNSUPPORTED_KEY_SIZE"}, + {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_UNSUPPORTED_NONCE_SIZE), "UNSUPPORTED_NONCE_SIZE"}, + {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_UNSUPPORTED_TAG_SIZE), "UNSUPPORTED_TAG_SIZE"}, {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_WRAP_MODE_NOT_ALLOWED), "WRAP_MODE_NOT_ALLOWED"}, {ERR_PACK(ERR_LIB_CIPHER, 0, CIPHER_R_WRONG_FINAL_BLOCK_LENGTH), "WRONG_FINAL_BLOCK_LENGTH"}, {0, NULL},
diff --git a/crypto/cipher/e_aes.c b/crypto/cipher/e_aes.c index 741fd01..6e3fafb 100644 --- a/crypto/cipher/e_aes.c +++ b/crypto/cipher/e_aes.c
@@ -986,3 +986,267 @@ const EVP_AEAD *EVP_aead_aes_128_gcm() { return &aead_aes_128_gcm; } const EVP_AEAD *EVP_aead_aes_256_gcm() { return &aead_aes_256_gcm; } + + +/* AES Key Wrap is specified in + * http://csrc.nist.gov/groups/ST/toolkit/documents/kms/key-wrap.pdf + * or https://tools.ietf.org/html/rfc3394 */ + +struct aead_aes_key_wrap_ctx { + uint8_t key[32]; + unsigned key_bits; +}; + +static int aead_aes_key_wrap_init(EVP_AEAD_CTX *ctx, const uint8_t *key, + size_t key_len, size_t tag_len) { + struct aead_aes_key_wrap_ctx *kw_ctx; + const size_t key_bits = key_len * 8; + + if (key_bits != 128 && key_bits != 256) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_init, CIPHER_R_BAD_KEY_LENGTH); + return 0; /* EVP_AEAD_CTX_init should catch this. */ + } + + if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) { + tag_len = 8; + } + + if (tag_len != 8) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_init, + CIPHER_R_UNSUPPORTED_TAG_SIZE); + return 0; + } + + kw_ctx = OPENSSL_malloc(sizeof(struct aead_aes_key_wrap_ctx)); + if (kw_ctx == NULL) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_init, ERR_R_MALLOC_FAILURE); + return 0; + } + + memcpy(kw_ctx->key, key, key_len); + kw_ctx->key_bits = key_bits; + + ctx->aead_state = kw_ctx; + return 1; +} + +static void aead_aes_key_wrap_cleanup(EVP_AEAD_CTX *ctx) { + struct aead_aes_key_wrap_ctx *kw_ctx = ctx->aead_state; + OPENSSL_cleanse(kw_ctx, sizeof(struct aead_aes_key_wrap_ctx)); + OPENSSL_free(kw_ctx); +} + +/* kDefaultAESKeyWrapNonce is the default nonce value given in 2.2.3.1. */ +static const uint8_t kDefaultAESKeyWrapNonce[8] = {0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6}; + + +static int aead_aes_key_wrap_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len, + const uint8_t *nonce, size_t nonce_len, + const uint8_t *in, size_t in_len, + const uint8_t *ad, size_t ad_len) { + const struct aead_aes_key_wrap_ctx *kw_ctx = ctx->aead_state; + union { + double align; + AES_KEY ks; + } ks; + /* Variables in this function match up with the variables in the second half + * of section 2.2.1. */ + unsigned i, j, n; + uint8_t A[AES_BLOCK_SIZE]; + + if (ad_len != 0) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_seal, + CIPHER_R_UNSUPPORTED_AD_SIZE); + return 0; + } + + if (nonce_len == 0) { + nonce = kDefaultAESKeyWrapNonce; + nonce_len = sizeof(kDefaultAESKeyWrapNonce); + } + + if (nonce_len != 8) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_seal, + CIPHER_R_UNSUPPORTED_NONCE_SIZE); + return 0; + } + + if (in_len % 8 != 0) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_seal, + CIPHER_R_UNSUPPORTED_INPUT_SIZE); + return 0; + } + + /* The code below only handles a 32-bit |t| thus 6*|n| must be less than + * 2^32, where |n| is |in_len| / 8. So in_len < 4/3 * 2^32 and we + * conservatively cap it to 2^32-16 to stop 32-bit platforms complaining that + * a comparision is always true. */ + if (in_len > 0xfffffff0) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_seal, CIPHER_R_TOO_LARGE); + return 0; + } + + n = in_len / 8; + + if (n < 2) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_seal, + CIPHER_R_UNSUPPORTED_INPUT_SIZE); + return 0; + } + + if (in_len + 8 < in_len) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_seal, CIPHER_R_TOO_LARGE); + return 0; + } + + if (max_out_len < in_len + 8) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_seal, + CIPHER_R_BUFFER_TOO_SMALL); + return 0; + } + + if (AES_set_encrypt_key(kw_ctx->key, kw_ctx->key_bits, &ks.ks) < 0) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_seal, + CIPHER_R_AES_KEY_SETUP_FAILED); + return 0; + } + + memmove(out + 8, in, in_len); + memcpy(A, nonce, 8); + + for (j = 0; j < 6; j++) { + for (i = 1; i <= n; i++) { + uint32_t t; + + memcpy(A + 8, out + 8 * i, 8); + AES_encrypt(A, A, &ks.ks); + t = n * j + i; + A[7] ^= t & 0xff; + A[6] ^= (t >> 8) & 0xff; + A[5] ^= (t >> 16) & 0xff; + A[4] ^= (t >> 24) & 0xff; + memcpy(out + 8 * i, A + 8, 8); + } + } + + memcpy(out, A, 8); + *out_len = in_len + 8; + return 1; +} + +static int aead_aes_key_wrap_open(const EVP_AEAD_CTX *ctx, uint8_t *out, + size_t *out_len, size_t max_out_len, + const uint8_t *nonce, size_t nonce_len, + const uint8_t *in, size_t in_len, + const uint8_t *ad, size_t ad_len) { + const struct aead_aes_key_wrap_ctx *kw_ctx = ctx->aead_state; + union { + double align; + AES_KEY ks; + } ks; + /* Variables in this function match up with the variables in the second half + * of section 2.2.1. */ + unsigned i, j, n; + uint8_t A[AES_BLOCK_SIZE]; + + if (ad_len != 0) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_open, + CIPHER_R_UNSUPPORTED_AD_SIZE); + return 0; + } + + if (nonce_len == 0) { + nonce = kDefaultAESKeyWrapNonce; + nonce_len = sizeof(kDefaultAESKeyWrapNonce); + } + + if (nonce_len != 8) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_open, + CIPHER_R_UNSUPPORTED_NONCE_SIZE); + return 0; + } + + if (in_len % 8 != 0) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_open, + CIPHER_R_UNSUPPORTED_INPUT_SIZE); + return 0; + } + + /* The code below only handles a 32-bit |t| thus 6*|n| must be less than + * 2^32, where |n| is |in_len| / 8. So in_len < 4/3 * 2^32 and we + * conservatively cap it to 2^32-8 to stop 32-bit platforms complaining that + * a comparision is always true. */ + if (in_len > 0xfffffff8) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_open, CIPHER_R_TOO_LARGE); + return 0; + } + + if (in_len < 24) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_gcm_open, CIPHER_R_BAD_DECRYPT); + return 0; + } + + n = (in_len / 8) - 1; + + if (max_out_len < in_len - 8) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_open, + CIPHER_R_BUFFER_TOO_SMALL); + return 0; + } + + if (AES_set_decrypt_key(kw_ctx->key, kw_ctx->key_bits, &ks.ks) < 0) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_key_wrap_open, + CIPHER_R_AES_KEY_SETUP_FAILED); + return 0; + } + + memcpy(A, in, 8); + memmove(out, in + 8, in_len); + + for (j = 5; j < 6; j--) { + for (i = n; i > 0; i--) { + uint32_t t; + + t = n * j + i; + A[7] ^= t & 0xff; + A[6] ^= (t >> 8) & 0xff; + A[5] ^= (t >> 16) & 0xff; + A[4] ^= (t >> 24) & 0xff; + memcpy(A + 8, out + 8 * (i - 1), 8); + AES_decrypt(A, A, &ks.ks); + memcpy(out + 8 * (i - 1), A + 8, 8); + } + } + + if (CRYPTO_memcmp(A, nonce, 8) != 0) { + OPENSSL_PUT_ERROR(CIPHER, aead_aes_gcm_open, CIPHER_R_BAD_DECRYPT); + return 0; + } + + *out_len = in_len - 8; + return 1; +} + +static const EVP_AEAD aead_aes_128_key_wrap = { + 16, /* key len */ + 8, /* nonce len */ + 8, /* overhead */ + 8, /* max tag length */ + aead_aes_key_wrap_init, aead_aes_key_wrap_cleanup, + aead_aes_key_wrap_seal, aead_aes_key_wrap_open, +}; + +static const EVP_AEAD aead_aes_256_key_wrap = { + 32, /* key len */ + 8, /* nonce len */ + 8, /* overhead */ + 8, /* max tag length */ + aead_aes_key_wrap_init, aead_aes_key_wrap_cleanup, + aead_aes_key_wrap_seal, aead_aes_key_wrap_open, +}; + +const EVP_AEAD *EVP_aead_aes_128_key_wrap() { return &aead_aes_128_key_wrap; } + +const EVP_AEAD *EVP_aead_aes_256_key_wrap() { return &aead_aes_256_key_wrap; }
diff --git a/include/openssl/aead.h b/include/openssl/aead.h index 767ab78..0531ad9 100644 --- a/include/openssl/aead.h +++ b/include/openssl/aead.h
@@ -101,6 +101,20 @@ /* EVP_aead_chacha20_poly1305 is an AEAD built from ChaCha20 and Poly1305. */ const EVP_AEAD *EVP_aead_chacha20_poly1305(); +/* EVP_aead_aes_128_key_wrap is AES-128 Key Wrap mode. This should never be + * used except to interoperate with existing systems that use this mode. + * + * If the nonce is emtpy then the default nonce will be used, otherwise it must + * be eight bytes long. The input must be a multiple of eight bytes long. No + * additional data can be given to this mode. */ +const EVP_AEAD *EVP_aead_aes_128_key_wrap(); + +/* EVP_aead_aes_256_key_wrap is AES-256 in Key Wrap mode. This should never be + * used except to interoperate with existing systems that use this mode. + * + * See |EVP_aead_aes_128_key_wrap| for details. */ +const EVP_AEAD *EVP_aead_aes_256_key_wrap(); + /* TLS specific AEAD algorithms. *
diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h index 7d25005..191d44a 100644 --- a/include/openssl/cipher.h +++ b/include/openssl/cipher.h
@@ -439,6 +439,9 @@ #define CIPHER_F_aead_rc4_md5_tls_init 116 #define CIPHER_F_aead_rc4_md5_tls_seal 117 #define CIPHER_F_aead_rc4_md5_tls_open 118 +#define CIPHER_F_aead_aes_key_wrap_seal 119 +#define CIPHER_F_aead_aes_key_wrap_init 120 +#define CIPHER_F_aead_aes_key_wrap_open 121 #define CIPHER_R_WRAP_MODE_NOT_ALLOWED 100 #define CIPHER_R_AES_KEY_SETUP_FAILED 101 #define CIPHER_R_INPUT_NOT_INITIALIZED 102 @@ -458,5 +461,9 @@ #define CIPHER_R_IV_TOO_LARGE 116 #define CIPHER_R_INVALID_AD_SIZE 117 #define CIPHER_R_INVALID_AD 118 +#define CIPHER_R_UNSUPPORTED_TAG_SIZE 119 +#define CIPHER_R_UNSUPPORTED_INPUT_SIZE 120 +#define CIPHER_R_UNSUPPORTED_AD_SIZE 121 +#define CIPHER_R_UNSUPPORTED_NONCE_SIZE 122 #endif /* OPENSSL_HEADER_CIPHER_H */
diff --git a/util/all_tests.sh b/util/all_tests.sh index a7e961e..27405fb 100644 --- a/util/all_tests.sh +++ b/util/all_tests.sh
@@ -19,6 +19,8 @@ ./crypto/cipher/aead_test aes-256-gcm ../crypto/cipher/aes_256_gcm_tests.txt ./crypto/cipher/aead_test chacha20-poly1305 ../crypto/cipher/chacha20_poly1305_tests.txt ./crypto/cipher/aead_test rc4-md5 ../crypto/cipher/rc4_md5_tests.txt +./crypto/cipher/aead_test aes-128-key-wrap ../crypto/cipher/aes_128_key_wrap_tests.txt +./crypto/cipher/aead_test aes-256-key-wrap ../crypto/cipher/aes_256_key_wrap_tests.txt ./crypto/base64/base64_test ./crypto/bio/bio_test ./crypto/bn/bn_test