Skip to content

CCM key unwrapping does not validate tag and returns 16 bytes of garbage header #36

@marcan

Description

@marcan

The entire world apparently calls what BitLocker uses to wrap keys "AES-CCM". Unfortunately, that's not what is actually implemented. The algorithm implemented by libcaes_crypt_ccm isn't CCM: it doesn't have a MAC, it doesn't have associated data, and it doesn't use the first keystream block for that. That makes it just AES-CTR.

The only thing "CCM" about it is that the nonce is prepended with a byte of value 15 - (uint8_t) nonce_size - 1 (which CCM does, and is not inherent to CTR mode). There's none of the other bits that would make it compliant with the CCM specification (RFC3610). Crucially, BitLocker key unwrapping can be implemented using a standard AES-CTR implementation (just prepend 0x02 to the nonce), but cannot be implemented using a standard AES-CCM implementation (because there is no way to disable the whole MAC machinery).

I would recommend renaming all mentions of CCM to CTR, to avoid confusion. I just spent a few hours wondering why using PyCryptodome yielded incorrect decryptions of BitLocker wrapped keys. Instead, CTR mode is what you want. In PyCryptodome:

python
def decrypt(p, k):
nonce = p[:12]
nonce = bytes([15 - len(nonce) - 1]) + nonce
aes = AES.new(k, AES.MODE_CTR, nonce=nonce)
a = aes.decrypt(p[12:])
return a

~~
is the code you'd want to unwrap a BitLocker wrapped key, with MODE_CTR, not MODE_CCM.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions