Skip to content
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

[CryptoAuthLib provider] PsaCipherEncrypt and PsaCipherDecrypt implementation #563

Merged
merged 6 commits into from
Jan 14, 2022

Conversation

akazimierskigl
Copy link
Contributor

@akazimierskigl akazimierskigl commented Nov 30, 2021

Implementation for PsaCipherEncrypt and PsaCipherDecrypt operations.
Implementation of end-2-end test cases for Cipher operations.
Updated to newest parsec-client-rust which includes Cipher operations.

Signed-off-by: artur.kazimierski artur.kazimierski@globallogic.com

Copy link
Member

@ionut-arm ionut-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch!

One thing that would be nice to add is a test that imports an AES key, encrypts something with a crypto crate (maybe ring?) and then decrypts it with Parsec (or the other way around).


impl Provider {
/// Generate random non-zero initialization vector
pub fn generate_iv(&self) -> Result<zeroize::Zeroizing<Vec<u8>>> {
Copy link
Member

@ionut-arm ionut-arm Nov 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method isn't needed outside of the provider, is it? Would be better to limit it: pub(super)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I don't think there's a need to return a Zeroizing here, since the IV isn't sensitive, you could just return Vec<u8>. That makes it easier later in the function (and in cipher_encrypt_internal), if you do

let random_bytes = self.psa_generate_random_internal(random_op)?.random_bytes.to_vec();

Comment on lines 46 to 47
let random_bytes = self.psa_generate_random_internal(random_op)?.random_bytes;
match random_bytes.to_vec() != zero_iv {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused. If the RNG cannot generate random bytes, it should return an error. I'm not sure if this was discussed before, and it's probably partially our fault as the operation page in the Parsec book doesn't list "specific error codes". However, the spec does mention some. I guess the most appropriate would be PSA_ERROR_INSUFFICIENT_ENTROPY. Do you have any idea why the backend might return all zeroes?

Comment on lines 113 to 116
let mut op_iv: Vec<u8> = Vec::new();
op_iv.extend_from_slice(&op.ciphertext[..CIPHER_IV_SIZE]);
let mut ciphertext: Vec<u8> = Vec::new();
ciphertext.extend_from_slice(&op.ciphertext[CIPHER_IV_SIZE..]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to check that op.ciphertext is at least as long as CIPHER_IV_SIZE before you index into it, otherwise you might get a panic.

Also, getting the two parts in an easier way:

let mut op_iv = op.ciphertext.to_vec();
let ciphertext = op_iv.split_off(CIPHER_IV_SIZE);

@akazimierskigl akazimierskigl changed the title DRAFT: [CryptoAuthLib provider] PsaCipherEncrypt and PsaCipherDecrypt implementation [CryptoAuthLib provider] PsaCipherEncrypt and PsaCipherDecrypt implementation Dec 2, 2021
@akazimierskigl akazimierskigl force-pushed the calib-psa-cipher branch 2 times, most recently from d7c6ccf to a01f8e2 Compare December 3, 2021 08:54
@akazimierskigl
Copy link
Contributor Author

I've delivered fixes for comments to the code and now I will try figure out your proposition about decrypting something which was not encrypted by parsec.

@ionut-arm
Copy link
Member

Hi @akazimierskigl , I think the more pressing question is whether the psa_generate_random implementation from the CAL provider can actually return a vector of 0 bytes instead of completely random, while claiming that the operation was successful. If that's the case, I'd be inclined to move the retry and error-throwing into the generate_random implementation - we can hopefully include this in our 0.9.0 release.

@akazimierskigl
Copy link
Contributor Author

@ionut-arm I will clarify this with our CAL expert. Previously I completely ignored the fact that generate_random can check itself if vector is cryptographically secure and I added this checking and loop just in case.

Meanwhile I have a first proposition for decryption tests. Instead of encrypting message by external crate we could use already prepared and proven set of IVs, plaintexts and ciphertexts from publications. The same how unit tests were done for rust-cryptoauthlib. https://github.com/PelionIoT/rust-cryptoauthlib/blob/master/cryptoauthlib/src/unit_tests/hw_backend_aes_cipher_stream.rs

@ionut-arm
Copy link
Member

Sure, that sounds like a sensible plan. It's likely that if we expand the functionality to other providers in the future we'll add more stuff, but hardcoded values is enough for now.

@akazimierskigl
Copy link
Contributor Author

Ok so I have an answer and it is possible to generate vector of zeros by CAL without any error returned. So we need to make decision how cover this extremely rare case.

@ionut-arm
Copy link
Member

Ok so I have an answer and it is possible to generate vector of zeros by CAL without any error returned. So we need to make decision how cover this extremely rare case.

I'd say integrating the retry logic from this PR into psa_generate_random and sending back a PSA_ERROR_INSUFFICIENT_ENTROPY if that fails would work just fine.

@akazimierskigl
Copy link
Contributor Author

@ionut-arm Good idea with only decrypt test cases. One algorithm didn't work properly so implementation of operations is changed too. Code is ready for re-review.

Copy link
Member

@ionut-arm ionut-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update, and glad to hear a bug has been squashed before it was even born! 🐛

Comment on lines +9 to +14
// Test Vector from:
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CFB.pdf
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_OFB.pdf
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CTR.pdf
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_ECB.pdf
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CBC.pdf
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really nice, thank you for that!

if loop_count < 3 {
continue;
} else {
return Err(ResponseStatus::PsaErrorInsufficientEntropy);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also put an error! message here as well?

// internal loop for vector size greater than buffer size
for _i in 0..call_count {
let mut buffer = Vec::with_capacity(rust_cryptoauthlib::ATCA_RANDOM_BUFFER_SIZE);
let err = self.device.random(&mut buffer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I just realised something - since a bigger request (i.e. one asking for more than ATCA_RANDOM_BUFFER_SIZE) can end up calling this random more than once, is it possible that one individual call returns all zeroes, but the others return something else? I'm just wondering if the loop checking for zeroes should instead be after each random call, instead of the whole combined thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"is it possible that one individual call returns all zeroes, but the others return something else?"

Yes, every other call of random should return different result so it is near impossible to get vector ATCA_RANDOM_BUFFER_SIZE * 2 of zeros. This is why I think checking only one time at the end if returned vector is all zeros is needed. Also I think when returned vector has at least one non zero value then is no longer considered "unsafe".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @akazimierskigl - I am substituting for @ionut-arm here because he is now on leave until the new year. I am slightly lacking some context, so forgive me if I'm misunderstanding. To resolve this question I think I need to understand the underlying cause of a vector of zeroes from the back-end HRNG. Is it purely a probabilistic thing? Is a sequence of 32 zeroes as likely as any other result? If so, then I'm not sure why it needs any special handling, because you might just as well worry about about any other specific set of values. However, if the source of the zeroes is due to some sort of known transient failure or unready state in the HRNG, then I would agree with Ionut that we really should be checking it on every call to the hardware, because otherwise it is compromising the overall entropy of the result. I hope this makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Is it purely a probabilistic thing? Is a sequence of 32 zeroes as likely as any other result?"
Yes.
"However, if the source of the zeroes is due to some sort of known transient failure or unready state in the HRNG"
It's not.
I've added handling in a first place only because it was required by cipher algorithm to use random non-zero initialization vector.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, thanks. In that case it strikes me that we're worrying over nothing, really. It's a vanishingly-unlikely case. A single check on the collected final result is probably good enough, and for all practical purposes that check would never fail anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @paulhowardarm, @akazimierskigl ! I misunderstood what the initial cause for the checks for 0 was, that's a lesson in communication for me :) I thought they'd result from an error in the CAL chip. Shall review once again, apologies for the massive delay this has caused.

@akazimierskigl akazimierskigl force-pushed the calib-psa-cipher branch 2 times, most recently from 63bba0c to 4911584 Compare December 20, 2021 08:26
Implementation of end-2-end test cases for Cipher operations.
New parsec-client-rust commit.

Signed-off-by: artur.kazimierski <artur.kazimierski@globallogic.com>
Signed-off-by: artur.kazimierski <artur.kazimierski@globallogic.com>
…r fixes

Signed-off-by: artur.kazimierski <artur.kazimierski@globallogic.com>
…nerate_random error handling

Signed-off-by: artur.kazimierski <artur.kazimierski@globallogic.com>
…essage

Signed-off-by: artur.kazimierski <artur.kazimierski@globallogic.com>
Signed-off-by: artur.kazimierski <artur.kazimierski@globallogic.com>
Copy link
Member

@ionut-arm ionut-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻 thanks!

@ionut-arm ionut-arm merged commit e8a2c4e into parallaxsecond:main Jan 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants