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