c++copensslaes

What replaces AES_set_decrypt_key and AES_unwrap_key in OpenSSL 3?


The AES_set_decrypt_key and AES_unwrap_key functions are deprecated in OpenSSL 3. I'm maintaining a function which uses them that I'd like to update to use non-deprecated functions:

std::unique_ptr<uint8_t[]> rfc3394_key_unwrap(const uint8_t* key, size_t key_len,
    const void *input, size_t input_len, const void *iv) noexcept 
{
  AES_KEY aes_key;
  AES_set_decrypt_key(key, key_len * 8, &aes_key);

  const int output_len = input_len - 8;

  auto out = std::make_unique<uint8_t[]>(output_len);

  const auto ret = AES_unwrap_key(&aes_key, (const uint8_t*)iv, out.get(),
                                  (const uint8_t*)input, input_len);

  if (ret != output_len) {
    return nullptr;
  }

  return out;
}

Or, in C if you prefer:

uint8_t* rfc3394_key_unwrap(const uint8_t *key, size_t key_len, const void *input,
    size_t input_len, const void *iv)
{
  AES_KEY aes_key;
  AES_set_decrypt_key(key, key_len * 8, &aes_key);

  const int output_len = input_len - 8;

  uint8_t* out = (uint8_t*) malloc(output_len);

  const int ret = AES_unwrap_key(&aes_key, (const uint8_t*)iv, out,
                                (const uint8_t*)input, input_len);

  if (ret != output_len) {
    free(out);
    return NULL;
  }

  return out;
}

I haven't been able to determine what the replacements are (possibly amongst the EVP_CIPHER functions?). What should I be looking for?


Solution

  • The AES Key Unwrap (described in RFC 3394, section 2.2.2) can be implemented with the high level functions (EVP_...) like an ordinary decryption, e.g. as described here in the OpenSSL Wiki, where only instead of EVP_aes_256_cbc() (for decryption with AES-256/CBC) one of the Key Wrap ciphers, e.g. EVP_aes_256_wrap() (for an AES-256 Key Unwrap) is to be used, as shown below (for the sake of simplicity without exception handling):

    int decrypt(unsigned char* ciphertext, int ciphertext_len, unsigned char* key, unsigned char* iv, unsigned char* plaintext)
    {
        EVP_CIPHER_CTX* ctx;
        int len;
        int plaintext_len;
    
        ctx = EVP_CIPHER_CTX_new();
        EVP_DecryptInit_ex(ctx, EVP_aes_256_wrap(), NULL, key, iv);
    
        EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
        plaintext_len = len;
    
        EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
        plaintext_len += len;
    
        EVP_CIPHER_CTX_free(ctx);
        return plaintext_len;
    }
    

    Here, ciphertext is the wrapped (= encrypted) key and plaintext is the original (= unwrapped/decrypted) key.

    Example with the test vector from RFC 3394, 4.3:

    unsigned char wrapping_key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };
    unsigned char wrapped_key[] = { 0x64, 0xe8, 0xc3, 0xf9, 0xce, 0x0f, 0x5b, 0xa2, 0x63, 0xe9, 0x77, 0x79, 0x05, 0x81, 0x8a, 0x2a, 0x93, 0xc8, 0x19, 0x1e, 0x7d, 0x6e, 0x8a, 0xe7 };
    unsigned char iv[] = { 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6 }; // default IV for key wrap
    
    unsigned char unwrapped_key[16];
    int unwrapped_key_len = decrypt(wrapped_key, sizeof(wrapped_key), wrapping_key, iv, unwrapped_key);
    // hex dump of unwrapped_key: 00112233445566778899aabbccddeeff
    

    The rfc3394_key_unwrap() function you posted gives the same result for the same input data!

    The following also applies: