Don't use the low level AES key wrap APIs in CMS
authorMatt Caswell <matt@openssl.org>
Thu, 5 Dec 2019 18:01:44 +0000 (18:01 +0000)
committerMatt Caswell <matt@openssl.org>
Mon, 6 Jan 2020 15:09:57 +0000 (15:09 +0000)
We should not be using the low level AES APIs in CMS. Instead we should
be using EVP. There was a small amount of use of the low level key
wrap APIs - so we convert that to EVP.

Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/10580)

crypto/cms/cms_env.c

index ecece98..ac34f3e 100644 (file)
@@ -13,7 +13,7 @@
 #include <openssl/x509v3.h>
 #include <openssl/err.h>
 #include <openssl/cms.h>
-#include <openssl/aes.h>
+#include <openssl/evp.h>
 #include "cms_local.h"
 #include "crypto/asn1.h"
 #include "crypto/evp.h"
@@ -625,6 +625,23 @@ int CMS_RecipientInfo_set0_key(CMS_RecipientInfo *ri,
     return 1;
 }
 
+static const EVP_CIPHER *cms_get_key_wrap_cipher(size_t keylen)
+{
+    switch(keylen) {
+    case 16:
+        return EVP_aes_128_wrap();
+
+    case 24:
+        return EVP_aes_192_wrap();
+
+    case 32:
+        return EVP_aes_256_wrap();
+    }
+
+    return NULL;
+}
+
+
 /* Encrypt content key in KEK recipient info */
 
 static int cms_RecipientInfo_kekri_encrypt(const CMS_ContentInfo *cms,
@@ -632,36 +649,50 @@ static int cms_RecipientInfo_kekri_encrypt(const CMS_ContentInfo *cms,
 {
     CMS_EncryptedContentInfo *ec;
     CMS_KEKRecipientInfo *kekri;
-    AES_KEY actx;
     unsigned char *wkey = NULL;
     int wkeylen;
     int r = 0;
+    const EVP_CIPHER *cipher = NULL;
+    int outlen = 0;
+    EVP_CIPHER_CTX *ctx = NULL;
 
     ec = cms->d.envelopedData->encryptedContentInfo;
 
     kekri = ri->d.kekri;
 
-    if (!kekri->key) {
+    if (kekri->key == NULL) {
         CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, CMS_R_NO_KEY);
         return 0;
     }
 
-    if (AES_set_encrypt_key(kekri->key, kekri->keylen << 3, &actx)) {
-        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT,
-               CMS_R_ERROR_SETTING_KEY);
+    cipher = cms_get_key_wrap_cipher(kekri->keylen);
+    if (cipher == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, CMS_R_INVALID_KEY_LENGTH);
         goto err;
     }
 
+    /* 8 byte prefix for AES wrap ciphers */
     wkey = OPENSSL_malloc(ec->keylen + 8);
-
     if (wkey == NULL) {
         CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, ERR_R_MALLOC_FAILURE);
         goto err;
     }
 
-    wkeylen = AES_wrap_key(&actx, NULL, wkey, ec->key, ec->keylen);
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
 
-    if (wkeylen <= 0) {
+    EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+    if (!EVP_EncryptInit_ex(ctx, cipher, NULL, kekri->key, NULL)
+            || !EVP_EncryptUpdate(ctx, wkey, &wkeylen, ec->key, ec->keylen)
+            || !EVP_EncryptFinal_ex(ctx, wkey + wkeylen, &outlen)) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, CMS_R_WRAP_ERROR);
+        goto err;
+    }
+    wkeylen += outlen;
+    if (!ossl_assert((size_t)wkeylen == ec->keylen + 8)) {
         CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT, CMS_R_WRAP_ERROR);
         goto err;
     }
@@ -671,10 +702,9 @@ static int cms_RecipientInfo_kekri_encrypt(const CMS_ContentInfo *cms,
     r = 1;
 
  err:
-
     if (!r)
         OPENSSL_free(wkey);
-    OPENSSL_cleanse(&actx, sizeof(actx));
+    EVP_CIPHER_CTX_free(ctx);
 
     return r;
 
@@ -687,10 +717,12 @@ static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms,
 {
     CMS_EncryptedContentInfo *ec;
     CMS_KEKRecipientInfo *kekri;
-    AES_KEY actx;
     unsigned char *ukey = NULL;
     int ukeylen;
     int r = 0, wrap_nid;
+    const EVP_CIPHER *cipher = NULL;
+    int outlen = 0;
+    EVP_CIPHER_CTX *ctx = NULL;
 
     ec = cms->d.envelopedData->encryptedContentInfo;
 
@@ -716,27 +748,33 @@ static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms,
         goto err;
     }
 
-    if (AES_set_decrypt_key(kekri->key, kekri->keylen << 3, &actx)) {
-        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT,
-               CMS_R_ERROR_SETTING_KEY);
+    cipher = cms_get_key_wrap_cipher(kekri->keylen);
+    if (cipher == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, CMS_R_INVALID_KEY_LENGTH);
         goto err;
     }
 
     ukey = OPENSSL_malloc(kekri->encryptedKey->length - 8);
-
     if (ukey == NULL) {
         CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, ERR_R_MALLOC_FAILURE);
         goto err;
     }
 
-    ukeylen = AES_unwrap_key(&actx, NULL, ukey,
-                             kekri->encryptedKey->data,
-                             kekri->encryptedKey->length);
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL) {
+        CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
 
-    if (ukeylen <= 0) {
+    if (!EVP_DecryptInit_ex(ctx, cipher, NULL, kekri->key, NULL)
+            || !EVP_DecryptUpdate(ctx, ukey, &ukeylen,
+                                  kekri->encryptedKey->data,
+                                  kekri->encryptedKey->length)
+            || !EVP_DecryptFinal_ex(ctx, ukey + ukeylen, &outlen)) {
         CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT, CMS_R_UNWRAP_ERROR);
         goto err;
     }
+    ukeylen += outlen;
 
     ec->key = ukey;
     ec->keylen = ukeylen;
@@ -744,10 +782,9 @@ static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms,
     r = 1;
 
  err:
-
     if (!r)
         OPENSSL_free(ukey);
-    OPENSSL_cleanse(&actx, sizeof(actx));
+    EVP_CIPHER_CTX_free(ctx);
 
     return r;