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?
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:
NULL
as IV in decrypt()
, the default IV for Key Wrap/Unwrap is used: 0xa6a6a6a6a6a6a6a6
.EVP_CIPHER_CTX_FLAG_WRAP_ALLOW
flag, s. this post.