Skip to content
This repository has been archived by the owner on Mar 7, 2024. It is now read-only.

Code for AES-CCM in Hacspec Rust #139

Draft
wants to merge 22 commits into
base: master
Choose a base branch
from
Draft

Conversation

tanmay2004
Copy link
Contributor

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 in test_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.

Copy link
Member

@franziskuskiefer franziskuskiefer 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 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.

@tanmay2004
Copy link
Contributor Author

@franziskuskiefer I think I've fixed the issue now. I added aes-ccm to the master Cargo.toml. Also, my code failed on line 108 because I had an older version of aes.rs on my system. I updated it and my code, so it is working now. Please review.

Copy link
Member

@franziskuskiefer franziskuskiefer left a 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.

Cargo.toml Show resolved Hide resolved
Cargo.toml Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
Copy link
Member

@franziskuskiefer franziskuskiefer left a 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.

examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
examples/aes-ccm/src/ccm_mode.rs Outdated Show resolved Hide resolved
Copy link
Member

@franziskuskiefer franziskuskiefer left a 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;
Copy link
Member

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::*;
Copy link
Member

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 {
Copy link
Member

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 {
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 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();
Copy link
Member

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.

Copy link
Contributor Author

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 {
Copy link
Member

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();
Copy link
Member

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.)

Copy link
Member

@franziskuskiefer franziskuskiefer left a 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 {
Copy link
Member

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.");
Copy link
Member

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 {
Copy link
Member

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.

@franziskuskiefer
Copy link
Member

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.

@franziskuskiefer franziskuskiefer marked this pull request as draft February 15, 2022 13:41
@franziskuskiefer franziskuskiefer added the inactive Status: Inactive and waiting on the author. label Oct 12, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
inactive Status: Inactive and waiting on the author. spec 📜 A new hacspec
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants