Skip to content

Instantly share code, notes, and snippets.

@kurtbrose
Last active December 28, 2022 13:15
Show Gist options
  • Save kurtbrose/4243633 to your computer and use it in GitHub Desktop.
Save kurtbrose/4243633 to your computer and use it in GitHub Desktop.
implementation of RFC 3394 AES key wrapping/unwrapping
'''
Key wrapping and unwrapping as defined in RFC 3394.
Also a padding mechanism that was used in openssl at one time.
The purpose of this algorithm is to encrypt a key multiple times to add an extra layer of security.
Personally, I wouldn't recommend using this for most applications.
Just use AES/mode CTR to encrypt your keys, the same as you would any other data.
The time to use this code is when you need compatibility with another system that implements the RFC.
(For example, these functions are compatible with the openssl functions of the same name.)
Performance should be reasonable, since the heavy lifting is all done in PyCrypto's AES.
'''
import struct
from Crypto.Cipher import AES
QUAD = struct.Struct('>Q')
def aes_unwrap_key_and_iv(kek, wrapped):
n = len(wrapped)/8 - 1
#NOTE: R[0] is never accessed, left in for consistency with RFC indices
R = [None]+[wrapped[i*8:i*8+8] for i in range(1, n+1)]
A = QUAD.unpack(wrapped[:8])[0]
decrypt = AES.new(kek).decrypt
for j in range(5,-1,-1): #counting down
for i in range(n, 0, -1): #(n, n-1, ..., 1)
ciphertext = QUAD.pack(A^(n*j+i)) + R[i]
B = decrypt(ciphertext)
A = QUAD.unpack(B[:8])[0]
R[i] = B[8:]
return "".join(R[1:]), A
#key wrapping as defined in RFC 3394
#http://www.ietf.org/rfc/rfc3394.txt
def aes_unwrap_key(kek, wrapped, iv=0xa6a6a6a6a6a6a6a6):
key, key_iv = aes_unwrap_key_and_iv(kek, wrapped)
if key_iv != iv:
raise ValueError("Integrity Check Failed: "+hex(key_iv)+" (expected "+hex(iv)+")")
return key
#alternate initial value for aes key wrapping, as defined in RFC 5649 section 3
#http://www.ietf.org/rfc/rfc5649.txt
def aes_unwrap_key_withpad(kek, wrapped):
if len(wrapped) == 16:
plaintext = AES.new(kek).decrypt(wrapped)
key, key_iv = plaintext[:8], plaintext[8:]
else:
key, key_iv = aes_unwrap_key_and_iv(kek, wrapped)
key_iv = "{0:016X}".format(key_iv)
if key_iv[:8] != "A65959A6":
raise ValueError("Integrity Check Failed: "+key_iv[:8]+" (expected A65959A6)")
key_len = int(key_iv[8:], 16)
return key[:key_len]
def aes_wrap_key(kek, plaintext, iv=0xa6a6a6a6a6a6a6a6):
n = len(plaintext)/8
R = [None]+[plaintext[i*8:i*8+8] for i in range(0, n)]
A = iv
encrypt = AES.new(kek).encrypt
for j in range(6):
for i in range(1, n+1):
B = encrypt(QUAD.pack(A) + R[i])
A = QUAD.unpack(B[:8])[0] ^ (n*j + i)
R[i] = B[8:]
return QUAD.pack(A) + "".join(R[1:])
def aes_wrap_key_withpad(kek, plaintext):
iv = 0xA65959A600000000 + len(plaintext)
plaintext = plaintext + "\0" * ((8 - len(plaintext)) % 8)
if len(plaintext) == 8:
return AES.new(kek).encrypt(QUAD.pack[iv] + plaintext)
return aes_wrap_key(kek, plaintext, iv)
def test():
#test vector from RFC 3394
import binascii
KEK = binascii.unhexlify("000102030405060708090A0B0C0D0E0F")
CIPHER = binascii.unhexlify("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5")
PLAIN = binascii.unhexlify("00112233445566778899AABBCCDDEEFF")
assert aes_unwrap_key(KEK, CIPHER) == PLAIN
assert aes_wrap_key(KEK, PLAIN) == CIPHER
@someshi
Copy link

someshi commented Sep 17, 2018

Super useful. Thank you!

Note about the code comment: since keywrapping is commonly used to encrypt sensitive key material, it is recommended that an authenticated encryption mode be used instead of CTR mode. AES KeyWrap is considered such a mode even though it doesn't have a formal security proof. The other well known mechanism is AES-GCM.

@melskalkee
Copy link

melskalkee commented Jan 21, 2021

I have tried running this example but it is giving me following error:
File "**********\Programs\Python\Python39\lib\site-packages\aes_keywrap.py", line 15, in aes_unwrap_key_and_iv
R = [None]+[wrapped[i
8:i
8+8] for i in range(1, n+1)]
TypeError: 'float' object cannot be interpreted as an integer

I believe that the module is not compatible with Python v3. Even after resolving float issue above, more issues are popped up subsequently.

Do you have a module which can work with Python 3?

@psvz
Copy link

psvz commented Dec 28, 2022

Of Great help @kurtbrose - with IoT key management in Secure Element chips this work is instrumental!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment