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

Core functions for the lake-authz draft #110

Merged
merged 25 commits into from
Nov 3, 2023

Conversation

geonnave
Copy link
Collaborator

@geonnave geonnave commented Oct 4, 2023

This PR will implement the core functions for EAD message preparation and processing with focus on U and V:

  • prepare EAD_1 (U)
  • process EAD_1 (V)
  • prepare Voucher Request (V)
  • prepare Voucher Response (W)
  • prepare EAD_2 (U)
  • process EAD_2 (V)
  • handle VREQ and VRES in stateless and stateful operation modes
  • integration with the main crate
  • pass CI, a.k.a. works with all features and all crypto backends

Draft link: https://www.ietf.org/archive/id/draft-selander-lake-authz-03.html

Architecture:

U                           V                                       W
|                           |                                       |
|      EDHOC message_1      |                                       |
+-------------------------->|                                       |
|  (EAD_1 = LOC_W, ENC_ID)  |                                       |
|                           |                                       |
|                           |        Voucher Request (VREQ)         |
|                           +-------------------------------------->|
|                           |       (message_1, ?opaque_state)      |
|                           |                                       |
|                           |        Voucher Response (VRES)        |
|                           |<--------------------------------------+
|                           |  (message_1, Voucher, ?opaque_state)  |
|                           |                                       |
|      EDHOC message_2      |                                       |
|<--------------------------+                                       |
|     (EAD_2 = Voucher)     |                                       |
|                           |                                       |
|                           |                                       |
|      EDHOC message_3      |                                       |
+-------------------------->|                                       |
|                           |                                       |

@geonnave geonnave force-pushed the core-ead-zeroconf branch 6 times, most recently from 3fe2ddf to 656dced Compare October 5, 2023 15:38
@geonnave geonnave force-pushed the core-ead-zeroconf branch 4 times, most recently from e677de2 to ca45d49 Compare October 10, 2023 11:33
@geonnave geonnave force-pushed the core-ead-zeroconf branch 2 times, most recently from 56c83d1 to e756401 Compare October 12, 2023 10:32
- includes test vectors
- also updates test vectors to reuse keys from lake-traces-07
@geonnave geonnave marked this pull request as ready for review October 12, 2023 17:20
@geonnave
Copy link
Collaborator Author

@malishav it still needs polishing but all core functions of the draft work, so it could receive a first pass of review. Please focus on the edhoc-ead-zeroconf crate, since the integration with the rest of edhoc-rs is currently broken due to some API changes. (that is why the CI totally fails; on the other hand, cargo test for the edhoc-ead-zeroconf crate passes).

@geonnave
Copy link
Collaborator Author

Also, for reference, I used this notebook to create the traces / test vectors.

@geonnave
Copy link
Collaborator Author

geonnave commented Oct 13, 2023

It looks better now, but I think it will not be fully working with all features and possible configurations by the end of the day.

Since I will leave for PTO until 24 Oct, I would like to merge this today (if the review goes well), even if I have to adapt the CI with feature/exclude flags.

@geonnave geonnave requested a review from malishav October 13, 2023 11:11
@geonnave
Copy link
Collaborator Author

Wohoo, CI passing!

Copy link
Contributor

@malishav malishav 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 providing this PR @geonnave ! I left some inline comments, the major red flag that I see is parsing code which does not check the actual number of bytes received and may panic. Other than that, I think there is a misconception on the length of the voucher and some excessive allocations. See inline for more details.

}

// NOTE: can we import this from the edhoc-rs main crate?
fn edhoc_kdf(
Copy link
Contributor

Choose a reason for hiding this comment

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

yes, we should definitely have a way of calling the KDF function frm this code without code duplication

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps, we can do the same as with encode_enc_structure and move this to a helper module?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For the time being I moved just the logic for the info struct to a helpers module in consts.

Part of the reason is to avoid having consts depend on the cryptographic backends.
Another part is that this is now only a single small duplicated function, so in my view it still does not justify changing the dependencies in consts.
Nevertheless I added a TO-DO (🙃), that we should address as more cases like this arise.

(),
> {
let mut message_1 = EdhocMessageBuffer::new();
let mut voucher = EdhocMessageBuffer::new();
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an excessive allocation for a voucher. Voucher is always of size MAC_LENGTH which corresponds to the MAC length used in EDHOC. Since we only support a single cipher suite for now, this should use MAC_LENGTH.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I changed this for consistency, but note that since we need to put the voucher in an EADItem struct, we sill need to allocate one large buffer since the EADItem.value is an EdhocMessageBuffer.

return Err(());
}

message_1.len = voucher_response.content[2] as usize;
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that you are not checking anywhere how many bytes are actually in the received voucher_response, i.e. the value of voucher_response.len. This means that your code may panic when it receives a malformed Voucher Response or worse take values from the internal buffer at random. This code should be rewritten to check remaining voucher_response.len before actually accessing voucher_response.content array.

Copy link
Collaborator Author

@geonnave geonnave Oct 25, 2023

Choose a reason for hiding this comment

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

Added size verifications to all parse_* routines.

ead_1_value: &Option<EdhocMessageBuffer>,
) -> Result<(EdhocMessageBuffer, EdhocMessageBuffer), ()> {
let value = ead_1_value.unwrap();
let loc_w: EdhocMessageBuffer = value.content[4..4 + value.content[3] as usize]
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above: before accessing value.content, we need to check how long it is...

}

message_1.len = vreq.content[2] as usize;
message_1.content[..message_1.len].copy_from_slice(&vreq.content[3..3 + message_1.len]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment on accessing vreq.content[] as above

ead/edhoc-ead-zeroconf/src/lib.rs Show resolved Hide resolved

// IV_1 = EDHOC-Expand(PRK, info = (1, h'', AES_CCM_KEY_LEN), length)
let mut iv_1: BytesCcmIvLen = [0x00; AES_CCM_IV_LEN];
// NOTE (FIXME?): here we actually generate AES_CCM_KEY_LEN bytes, but then we only use AES_CCM_IV_LEN of them (next line)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is strange. Looking at draft-ietf-lake-authz:

The derivation of IV_1 = EDHOC-Expand(PRK, info, length) uses the following input to the info struct (see Section 4.2):

info_label = 1
context = h'' (the empty CBOR string)
length is length of nonce of the EDHOC AEAD algorithm in bytes

According to this, length should be set to AES_CCM_IV_LEN.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

ead/edhoc-ead-zeroconf/src/lib.rs Outdated Show resolved Hide resolved
}

const EAD_ENC_STRUCTURE_LEN: usize = 2 + 8 + 3;
fn encode_enc_structure(ss: u8) -> [u8; EAD_ENC_STRUCTURE_LEN] {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this the same code as in lib/src/edhoc.rs, function encode_enc_structure ? If so, why not move it like a helper function together with CBOR-related helpers? Jsut rename the module to something more appropriate

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is not the same, the input in this case is just ss: u8 while in lib/src/edhoc.rs the input is th_3: &BytesHashLen.

pub fn r_process_ead_1(ead_1: &EADItem, message_1: &BufferMessage1) -> Result<(), ()> {
let opaque_state: Option<EdhocMessageBuffer> = None; // TODO: receive as parameter

if ead_1.label != EAD_ZEROCONF_LABEL {
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this be checked somehow beforehand, i.e. in the library code invoking the EAD handler? If yes, this could be an replaced with an assertion

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is not checked before, since the library is agnostic of the EAD handlers. Thus I think we can keep the if check.

@geonnave
Copy link
Collaborator Author

From my side, this is ready to merge.

Copy link
Contributor

@malishav malishav left a comment

Choose a reason for hiding this comment

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

Partial review below, as discussed offline.

ead/edhoc-ead-zeroconf/src/lib.rs Show resolved Hide resolved
}

fn build_enc_id(
prk: &BytesHashLen, // ephemeral key of U
Copy link
Contributor

Choose a reason for hiding this comment

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

The comment here confused me. prk is not an ephemeral key, but the ECDH secret derived from the ephemeral key of U and static key of W. The code seems correct, the comment does not.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As discussed, was a reminiscent comment from before a refactoring. Removed.

let mut message_1 = EdhocMessageBuffer::new();
let mut voucher: BytesEncodedVoucher = Default::default();

let array_byte = voucher_response.content[0];
Copy link
Contributor

Choose a reason for hiding this comment

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

You are accessing voucher_response.content[0] here before checking whether the received length, voucher_response.len, is greater than zero. Are you ensuring that the function can be called only when voucher_response.len is >0? If yes, then you could have an assert assert!(voucher_response.len > 0). If not, the whole block of code lines 365-409 should be conditioned on if(voucher_reponse.len > 0) IMO.

return Err(());
}

let message_1_len = voucher_response.content[2] as usize;
Copy link
Contributor

Choose a reason for hiding this comment

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

Here, you accessed voucher_response.content[2] before having checked if there are enough bytes in the buffer according to voucher_response.len. That may panic if voucher_response.len is only 1 byte.

Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed, we will address this issue in a separate PR.

@malishav malishav merged commit 8fc2763 into openwsn-berkeley:main Nov 3, 2023
24 checks passed
@geonnave geonnave deleted the core-ead-zeroconf branch January 17, 2024 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants