-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add AES-CTR and AES-CBC #265
Add AES-CTR and AES-CBC #265
Conversation
The test cases are generated by python-cwt with this script. #!/usr/bin/env python3
import subprocess
import sys
from cwt import COSE, COSEKey, Recipient
plaintext = b"This is the payload"
a128kw_key = COSEKey.from_symmetric_key("128-bit key xxxx", alg="A128KW")
#print(f"AES-KW key = {{'kty': {a128kw_key.kty}, 'k': h'{a128kw_key.key.hex().upper()}'}}")
print("# AES-CBC Examples")
a128cbc_key = COSEKey.from_symmetric_key(bytes.fromhex("50A2F79CD8028E9256DE13EA7AB27D31"), alg="A128CBC")
#a128cbc_key = COSEKey.from_symmetric_key(alg="A128CBC")
print(f"AES-CBC key = {{'kty': {a128cbc_key.kty}, 'k': h'{a128cbc_key.key.hex().upper()}'}}")
a128cbc_iv = bytes.fromhex("8262A8D72040A5E09A314586395D5750")
#a128cbc_iv = a128cbc_key.generate_nonce()
r = Recipient.new(unprotected={"alg": "A128KW"}, sender_key=a128kw_key)
sender = COSE.new()
encoded = sender.encode_and_encrypt(
plaintext,
a128cbc_key,
protected={"alg": "A128CBC"},
unprotected={
"iv": a128cbc_iv,
},
recipients=[r],
)
# The recipient side:
recipient = COSE.new()
decrypted_payload = recipient.decode(encoded, keys = [a128kw_key])
assert plaintext == decrypted_payload
output = subprocess.check_output(f"echo {encoded.hex().upper()} | pretty2diag.rb -e", shell=True).decode(sys.stdout.encoding).rstrip("\n")
print(f"COSE_Encrypt: {output}")
print(f"in hex: {encoded.hex().upper()}")
print(f"Decrypted payload: {decrypted_payload}")
print()
print("# AES-CTR Examples")
a128ctr_key = COSEKey.from_symmetric_key(bytes.fromhex("29D535405CB0E83994524F7F30D34DAC"), alg="A128CTR")
#a128ctr_key = COSEKey.from_symmetric_key(alg="A128CTR")
print(f"AES-CTR key = {{'kty': {a128ctr_key.kty}, 'k': h'{a128ctr_key.key.hex().upper()}'}}")
a128ctr_iv = bytes.fromhex("77D35242A191E9F9FD26104AB308564E")
#a128ctr_iv = a128ctr_key.geerate_nonce()
r = Recipient.new(unprotected={"alg": "A128KW"}, sender_key=a128kw_key)
sender = COSE.new()
encoded = sender.encode_and_encrypt(
plaintext,
a128ctr_key,
protected={"alg": "A128CTR"},
unprotected={
"iv": a128ctr_iv,
},
recipients=[r],
)
# The recipient side:
recipient = COSE.new()
decrypted_payload = recipient.decode(encoded, keys = [a128kw_key])
assert plaintext == decrypted_payload
output = subprocess.check_output(f"echo {encoded.hex().upper()} | pretty2diag.rb -e", shell=True).decode(sys.stdout.encoding).rstrip("\n")
print(f"COSE_Encrypt: {output}")
print(f"in hex: {encoded.hex().upper()}")
print(f"Decrypted payload: {decrypted_payload}")
print() and its output is
|
Hi. Good idea! Thank you. Will look at it to merge soon. |
There's a MUST here that I think needs implementing: https://www.rfc-editor.org/rfc/rfc9459.html#name-implementation-consideratio Also, this can work with ESDH, right? There's a few uses of key handle that need a cast to suppress warnings. You can probably see them with "make -f Makefile.psa warn". Still checking but generally looks really good. :-) |
@laurencelundblade
|
…TH_NON_AEAD for AES-CTR and AES-CBC
One concern is that t_cose encodes the algorithm id into protected header, but it is not actually protected if we use AES-CTR or AES-CBC because they cannot handle Enc_structure as AAD. NOTE: Example COSE_Encrypt binary generated by t_cose.
in hex
|
RFC 9459 says "The 'protected' header MUST be a zero-length byte string.", so we're going to have to move the alg ID to unprotected and check that protected is empty. That will be a bit of work... It doesn't actually say anything about where the alg ID parameter goes, but it can't go in the protected header. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appreciate the thorough job, especially the OSSL stuff. Mostly these are minor. The big one is the protected headers.
See you at the hackathon? I hope. :-)
} | ||
|
||
buffer_bytes_used = 0; | ||
/* This assumes a stream cipher and no need for handling blocks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AES-CBC is not a stream cipher, so the comment is partly wrong.
I would also feel much more comfortable with this if there was a test for for to verify this code works correctly for various sizes that are not on block boundaries. I'm more worried about OpenSSL than PSA.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did more checking and this seems OK. The esdh_enc_dec() test successfully encrypts with a payload of 19 bytes for both PSA and OpenSSL.
PSA clearly uses the PKCS7 padding mode of AES CBC which clearly is for this purpose. I couldn't find documentation for OSSL for this, but it tests correctly in esdh_enc_dec().
So just up the comment to say that it works with streaming and with block modes that have a padding scheme like PKCS7 padding.
Yes, update esdh_enc_dec_test() to take the symmetric cipher too. |
cb46b8a
to
01b9086
Compare
@laurencelundblade
It seems to be updated encoding protected header. |
Yes, working on fix for empty prot header encoding now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really close...
ce_alg.cose_alg_id = me->payload_cose_algorithm_id; | ||
ce_alg.bits_in_key = bits_in_crypto_alg(ce_alg.cose_alg_id); | ||
ce_alg.bits_iv = bits_iv_alg(ce_alg.cose_alg_id); | ||
if(ce_alg.bits_in_key == UINT32_MAX) { | ||
return T_COSE_ERR_UNSUPPORTED_CIPHER_ALG; | ||
} | ||
params[0] = t_cose_param_make_alg_id(ce_alg.cose_alg_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use t_cose_param_make_unprot_alg_id(). Then you can get rid of the loop below.
&nonce); | ||
params[1] = t_cose_param_make_iv(nonce); | ||
|
||
params[0].next = ¶ms[1]; | ||
params[1].next = me->added_body_parameters; | ||
/* At this point all the header parameters to be encoded are in a | ||
* linked list the head of which is params[0]. */ | ||
if(is_none_aead_cipher) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be removed. See above.
} | ||
|
||
buffer_bytes_used = 0; | ||
/* This assumes a stream cipher and no need for handling blocks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did more checking and this seems OK. The esdh_enc_dec() test successfully encrypts with a payload of 19 bytes for both PSA and OpenSSL.
PSA clearly uses the PKCS7 padding mode of AES CBC which clearly is for this purpose. I couldn't find documentation for OSSL for this, but it tests correctly in esdh_enc_dec().
So just up the comment to say that it works with streaming and with block modes that have a padding scheme like PKCS7 padding.
@@ -193,6 +193,16 @@ t_cose_encrypt_dec_detached(struct t_cose_encrypt_dec_ctx* me, | |||
|
|||
nonce_cbor = t_cose_param_find_iv(body_params_list); | |||
ce_alg.cose_alg_id = t_cose_param_find_alg_id_prot(body_params_list); | |||
if(ce_alg.cose_alg_id == T_COSE_ALGORITHM_NONE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can use t_cose_param_find_alg_id() here instead. It will return the protection and you can check it.
The problem is there is still a check needed that there are no other protected headers. I guess you just iterate over the linked list. Was hoping to use t_cose_headers_decode(,,true,,,) for that, but it doesn't work.
I was completely dead to the world... |
This PR adds COSE AES-CTR and AES-CBC defined in RFC9459.
Though they are non AEAD cipher and registered as "Deprecated", they are leveraged in SUIT Encrypted Payloads.