-
Notifications
You must be signed in to change notification settings - Fork 43
Code for AES-CCM in Hacspec Rust #139
base: master
Are you sure you want to change the base?
Conversation
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.
Thanks for the PR!
Before starting the review of the code we need to fix a couple things.
- Please add the new crate to the top level workspace (the
Cargo.toml
in the project root). Otherwise it won't work. - The code as is doesn't compile. After adding the crate to the workspace it will be tested on CI and we would see this here.
error[E0609]: no field `1` on type `hacspec_aes::Block`
--> examples/aes-ccm/src/ccm_mode.rs:108:56
|
108 | let mut y_curr = (aes128_encrypt_block(key, bloc)).1;
| ^ help: a field with a similar name exists: `0`
Let's make sure this actually builds and typechecks before we start looking at the code.
@franziskuskiefer I think I've fixed the issue now. I added aes-ccm to the master |
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.
Ok this fixes the build. But we still need to get the typechecker running on CI and the spec passing it. To do that you have to add it to the typecheck_exacmples.sh
. Please also run the typechecker locally before to find any issues. There are a number of issues. I mentioned some in this review.
Ping me if you still have issues running the typechecker locally.
Remove while loops
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.
Thanks! We're getting there :)
I didn't look at the actual code yet for correctness. Just code quality and hacspec issues so far.
- Please add comments to functions (at the top and within) to explain what the functions are doing.
- All the
as
integer casts need comments explaining why they are safe, or (better) have checks somewhere that they are safe. - I filed Add bitwise operations on Seq to typechecker #143 to track the bitwise operation issue.
- To improve test coverage can you add the wycheproof test vectors? You can look at the aes128-gcm test for a template how to read a json test vector like this. https://github.com/google/wycheproof/blob/master/testvectors/aes_ccm_test.json
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.
Looks like I forgot to hit submit here.
This is only a partial review but there are some todos in here. Please address them while I look at the rest.
use hacspec_aes::*; | ||
|
||
pub type AesCcmResult = Result<ByteSeq, u8>; | ||
pub const INVALID: u8 = 0u8; |
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 know this isn't done in gcm but you can use enums for errors, which is a little nicer. Something like
#[derive(Debug)]
pub enum AesCcmError {
Invalid
}
pub type AesCcmResult = Result<ByteSeq, AesCcmError>;
@@ -0,0 +1,210 @@ | |||
use hacspec_lib::*; |
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.
Please add a comment on the top with a short description and link to the spec you used (NIST)
pub type AesCcmResult = Result<ByteSeq, u8>; | ||
pub const INVALID: u8 = 0u8; | ||
|
||
fn format_func(a: &ByteSeq, n: &ByteSeq, p: &ByteSeq, t: usize, alen: usize, nlen: usize, plen: usize) -> ByteSeq { |
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 function is doing a lot without explaining anything. Please add some comments.
|
||
k = k + plen; | ||
|
||
for _t in 0..16 { |
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 think I asked for the comments here before.
} | ||
|
||
pub fn encrypt_ccm(a: ByteSeq, n: ByteSeq, pay: ByteSeq, key: Key128, tlen: usize) -> ByteSeq { | ||
let key_copy = key.clone(); |
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.
You don't need to have the copy here. Just clone when calling the function that needs a clone.
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.
It wasn't working that way, because I'm using the key multiple times in a loop for encrypting each block one by one. I had tried many variations of the cloning for the key
and the tests ran successfully but the typechecker failed for all my attempts other than the way I've done it right now.
(s0, s) | ||
} | ||
|
||
pub fn encrypt_ccm(a: ByteSeq, n: ByteSeq, pay: ByteSeq, key: Key128, tlen: usize) -> ByteSeq { |
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.
Single character variable names are always a bad idea. Pleas use variable names that tell the reader what they are looking at.
|
||
pub fn encrypt_ccm(a: ByteSeq, n: ByteSeq, pay: ByteSeq, key: Key128, tlen: usize) -> ByteSeq { | ||
let key_copy = key.clone(); | ||
let plen = pay.len(); |
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.
So here you get the lengths that you convert later to u8
. But you never check that they actually fit in u8
. All casts to smaller types require explicit checks. (The typechecker also tells you where they are and warns you about them.)
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.
Generally looks good. The most important points are still the readability, i.e. adding comments and using speaking variable names.
I'll see that we get the typechecker fixed for this to pass.
pub type AesCcmResult = Result<ByteSeq, u8>; | ||
pub const INVALID: u8 = 0u8; | ||
|
||
fn format_func(a: &ByteSeq, n: &ByteSeq, p: &ByteSeq, t: usize, alen: usize, nlen: usize, plen: usize) -> ByteSeq { |
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.
Why do you pass in the lengths here? You can just get them from the ByteSeq
input.
I'd expect this to be something like
fn format_func(aad: &ByteSeq, nonce: &ByteSeq, plaintext: &ByteSeq, tag_len: usize) -> ByteSeq {
continue; | ||
}; | ||
if testGroup.tagSize < 32 { | ||
println!("MAC lengths < 32 are not allowed for AES CCM."); |
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.
When they are not allowed this shouldn't happen, right? So can you just add an assert?
b | ||
} | ||
|
||
fn get_t(b: &ByteSeq, key: Key128, num: usize) -> ByteSeq { |
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.
Using num
here for tlen
is pretty confusing.
Hi @tanmay2004 what's the state of this PR? I think most typechecker related issues that we ran into here should be fixed by now. |
I have worked on the Rust implementation of the AES-CCM algorithm. I've created both the encryption and decryption functions and they can be found in
ccm_mode.rs
. I've used the specification provided here. The document also provides a few tests at the end which I've included intest_ccm_mode.rs
.Currently, the decryption function is slightly incomplete. The doc asks to
RETURN INVALID
in a few cases but I didn't know how to do that in Rust. Hopefully, the rest of it is satisfactory.