Skip to content

Refactoring to support future methods#406

Open
ElsaLopez133 wants to merge 13 commits intolake-rs:mainfrom
ElsaLopez133:refactor-multi-method-support
Open

Refactoring to support future methods#406
ElsaLopez133 wants to merge 13 commits intolake-rs:mainfrom
ElsaLopez133:refactor-multi-method-support

Conversation

@ElsaLopez133
Copy link
Collaborator

@ElsaLopez133 ElsaLopez133 commented Feb 23, 2026

Refactoring current code to add additional methods in the future (PSK)

  1. Added method-specific states (ProcessingM2StatStat, ProcessingM3StatStat), that diverge from PSK, to avoid using Option<> variables.
  2. Included method in some states to be able to match on it.
  3. Included helper functions to avoid Hax errors (DirectAndMut) (r_verify_message_3_statstat, i_verify_message_2_statsat)

@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch 2 times, most recently from 7204516 to af4dbe6 Compare February 23, 2026 11:51
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch from af4dbe6 to 97097f9 Compare February 23, 2026 11:55
For each function we match on state and create method-specific
fiunctions. For example, we have r_prepare_message_2 and we
match based on emthod to either use t_prepare_message_2_statstat
or r_prepare_message_2_psk
@ElsaLopez133 ElsaLopez133 marked this pull request as ready for review February 23, 2026 15:53
Copy link
Collaborator

@WilliamTakeshi WilliamTakeshi left a comment

Choose a reason for hiding this comment

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

Everything looks great to me.

Just a suggestion for the next PR (that you probably know). create a file for StatStat and one for PSK to organize it better the functions foo_statstat and foo_psk

Approved 🚀

@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch 2 times, most recently from 9976f2b to f54f4df Compare February 23, 2026 16:52
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch from f54f4df to 69792a9 Compare February 23, 2026 17:01
Comment on lines +518 to 534
#[derive(Debug)]
pub enum ProcessingM2 {
StatStat(ProcessingM2StatStat), // PSK(PProcessingM2PSK) // To be added later
}
#[derive(Debug)]
#[repr(C)]
pub struct ProcessingM2 {
pub struct ProcessingM2StatStat {
pub mac_2: BytesMac2,
pub prk_2e: BytesHashLen,
pub th_2: BytesHashLen,
pub x: BytesP256ElemLen,
pub g_y: BytesP256ElemLen,
pub plaintext_2: BufferPlaintext2,
pub c_r: ConnId,
pub id_cred_r: IdCred,
pub ead_2: EadItems,
}
Copy link
Member

Choose a reason for hiding this comment

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

This makes all the state of ProcessingM2 & co dependent on the method, rather than (as in the last call) just the parts that differ in other methods. Which of those fields are actually different in PSK?

I'd have expected something more like

#[derive(Debug)]
pub struct ProcessingM2 {
    pub method_specifics: ProcessingM2MethodSpecifics,
    pub prk_2e: BytesHashLen,
    pub th_2: BytesHashLen,
    pub x: BytesP256ElemLen,
    pub g_y: BytesP256ElemLen,
    pub plaintext_2: BufferPlaintext2,
    pub c_r: ConnId,
    pub ead_2: EadItems,
    
}
pub enum ProcessingM2MethodSpecifics {
    StatStat {
        pub mac_2: BytesMac2,
        pub id_cred_r: IdCred,
    },
    // PSK, -- is empty, but in other stages it might have fields that StatStat has not.
}

so that the 90% of the code that would be duplicated can still reach for the common items.

@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch 2 times, most recently from a18f9ce to a5de4f7 Compare February 24, 2026 08:55
We avoid repetition of shared-values among methods
This affects ProcessingM2 and ProcessingM3
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch from a5de4f7 to 35820e5 Compare February 24, 2026 09:10
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch 8 times, most recently from bc90672 to dd223be Compare February 27, 2026 14:11
- Made static keys I and R method dependednt (not needed ofr PSK)
- Added high level identity enums
- Made i_verify_message_2 take optional I
- Added ProcessedM2MethodSpecifics and ParsedMessage2Detials
- Updated i_parse_message_2 and high level initiaotr API
- Adapted lakers-python and lakers-c
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch from dd223be to ee44c6e Compare February 27, 2026 14:15
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch from 7dc5c68 to 46e2ecc Compare March 4, 2026 07:38
@ElsaLopez133 ElsaLopez133 mentioned this pull request Mar 4, 2026
Copy link
Member

@chrysn chrysn left a comment

Choose a reason for hiding this comment

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

(I'm a bit sloppy in the review a bit on the C side, treating that as "if the test pass it will be fine", but I don't treat that as a high-priority feature.)

Doing the changes (including back-and-forth on how much is in the structs) and file-to-file moves makes this rather hard to review. Nothing sensible to be done here though -- these kinds of refactor are known to be hard, and there are no good generic solutions. (Moving things into different files only after all other changes are understood to be good would have helped, but that's hard to roll back again w/o starting over).

Not for other reviewers: --ignore-all-space is your friend.

pub plaintext_2: BufferPlaintext2,
pub c_r: ConnId,
pub id_cred_r: IdCred,
pub ead_2: EadItems,
Copy link
Member

Choose a reason for hiding this comment

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

Most of these are actually method independent (ead_2 and c_r for sure, probably also th_2 and g_y) – AIU this does get changed back later in the same PR.

pub ead_3: EadItems,
}

#[derive(Debug)]
Copy link
Member

Choose a reason for hiding this comment

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

This was a dead code struct, right? (No need to split it out, let's just confirm this so that if we wonder, we'll find our considerations.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes, no longer there

lib/src/edhoc.rs Outdated
ProcessingM3::StatStat(inner) => r_verify_message_3_statstat(inner, crypto, valid_cred_i), // ProcessingM3::Psk(inner) => r_verify_message_3_psk(inner, crypto, valid_cred_i),
}
}
pub fn r_verify_message_3_statstat(
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't need to be pub. (Or if so, document why.).

Copy link
Collaborator Author

@ElsaLopez133 ElsaLopez133 Mar 5, 2026

Choose a reason for hiding this comment

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

they are pub because now they are inside edhoc/statstat.rs, otherwise they are not accessible. Changed to pub(crate)

lib/src/edhoc.rs Outdated
}
}

pub fn r_prepare_message_2_statstat(
Copy link
Member

Choose a reason for hiding this comment

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

Why pub?

lib/src/edhoc.rs Outdated
_ => Err(EDHOCError::UnsupportedMethod),
}
}
pub fn r_parse_message_3_statstat(
Copy link
Member

Choose a reason for hiding this comment

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

Why pub?

lib/src/edhoc.rs Outdated
_ => Err(EDHOCError::UnsupportedMethod),
}
}
// returns c_r
Copy link
Member

Choose a reason for hiding this comment

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

Is this comment even still accurate? (And if so, it'll still belong to i_parse_message_2, and should be a more specific doc comment)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

old comment, will remove

@@ -94,36 +97,44 @@ pub fn r_parse_message_3_statstat(
}

pub fn r_verify_message_3_statstat(
Copy link
Member

Choose a reason for hiding this comment

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

Now this is not statstat specific any more, as it does the switching internally just again.

(And r_verify_message_3 just forwards into here)

Copy link
Collaborator Author

@ElsaLopez133 ElsaLopez133 Mar 5, 2026

Choose a reason for hiding this comment

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

True, I can directly pass the arguments in the r_verify_message_3 and do the match in the outer function, rather than doing it twice!

}
// EDHOCMethod::PSK => r_prepare_message_2_psk()
// (EDHOCMethod::PSK, PrepareMessage2Details::Psk) =>
_ => Err(EDHOCError::UnsupportedMethod),
Copy link
Member

Choose a reason for hiding this comment

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

That is not necessarily the error here: The error is "the details you provided do not match what we agreed on with the peer". This should at least be a comment here until we find a good plan.

lib/src/lib.rs Outdated
r: BytesP256ElemLen, // private authentication key of R
cred_r: Credential, // R's full credential
state: ResponderStart, // opaque state
r: Option<BytesP256ElemLen>, // private authentication key of R when required by method
Copy link
Member

Choose a reason for hiding this comment

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

Why is this deviating from the method-enum path?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Before message_1, the Responder does not yet know the selected EDHOC method, so it must carry identity material. For StatStat, that includes the private r key.
You’re right that using Option plus an identity enum is redundant (I did have the RepsonderIdentity enum). I’ll switch to carrying ResponderIdentity directly and derive method-specific details at prepare_message_2 (once state.method is known), which removes the extra Option adn keeps the enum style.


#[derive(Debug)]
pub enum ParsedMessage2Details {
StatStat { id_cred_r: IdCred, ead_2: EadItems },
Copy link
Member

Choose a reason for hiding this comment

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

EAD items will be in any message 2 details. (No need for a struct I think: they can be returned side by side in the multi-value return that returns PasedMessage2Details).

@WilliamTakeshi WilliamTakeshi self-requested a review March 5, 2026 09:20
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch from d218a10 to 0af8c8b Compare March 5, 2026 13:22
1. Remove old comments (returns c_r)
2. Made r_verify_message_3 method distinction in the outer level
   (so r_verify_message_3_statstat does not do repeated matches)
3. Add a FIXMEE on the error type.
4. Modified r: Option<> to be method-enum, matching the current sytle and avoiding
  repetition (ResponderIdentity is the enum)
5. ParsedMessage2Detials: removed ead_2
6. Changed pub to pub(crate) in statstat.rs
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch from 0af8c8b to c63cd51 Compare March 5, 2026 13:51
Copy link
Collaborator

@WilliamTakeshi WilliamTakeshi left a comment

Choose a reason for hiding this comment

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

Heyyy, I really like how the code is shaping up. Just wrote some ocmments, the ones that are nitpick, feel free to skip them :))

pub kind: ProcessingM2MethodSpecificsKindC,
pub mac_2: BytesMac2,
pub id_cred_r: IdCred,
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think here its supposed to be

#[derive(Debug)]
#[repr(C)]
pub enum ProcessingM2MethodSpecificsKindC {
    Pm2StatStat {
		pub mac_2: BytesMac2,
    	pub id_cred_r: IdCred,
	},
}

because mac2 and id_cred_r will only exists for the StatStat, right?

If not, then mac_2 and id_cred_r shouldn't be inside the method_specifics field.

maybe its different because is the C version? I dont know

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I am not sure how to do it in C. The problem is that in C there is no tagged enum with payload, as in Rust. Check commit 3e405f8 where i am trying to replicate it using the union + kind.

1. Follow the same pattern for InitiatorIdentity (similar to
   RepsonderIdentity) instead of making i: Option<>
2. Fix in lakers-python/src/repsonder.rs: minimize the use of ?
   by extracting the value only once
3. Avoid wildcard imports
4. Add FIXME for the error type. Need to find a good one
5. Removing unnecesary method in ProcessedM2
@ElsaLopez133 ElsaLopez133 force-pushed the refactor-multi-method-support branch from 57596ab to c93fffd Compare March 5, 2026 20:11
Using struct+union to replicate the enum style in Rust
method_specifics: ProcessedM2MethodSpecificsC {
kind: ProcessedM2MethodSpecificsKindC::Prm2StatStat,
data: ProcessedM2MethodSpecificsDataC {
statstat: core::mem::ManuallyDrop::new(ProcessedM2StatStatC {}),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I only see ManuallyDrop::new, but no ManuallyDrop::drop. As far as I know, when using ManuallyDrop, the inner value must eventually be dropped manually (e.g., with ManuallyDrop::drop or ptr::drop_in_place); otherwise, it will leak.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In this specific case, ProcessedM2StatStatC {} is an empty type with no destructor, so there is nothing to drop and no leak from that field. The reason why ManuallyDrop is used is that it's the safe way to put fields in a union, especially for future non-trivial payloads, as in PSK.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am still not sure about using ManuallyDrop. In this case its fine because is a zero-sized field, but on the PSK will have to manually drop it, right?

I think the right path is to have something that derives Copy. @chrysn can you check this and these new changes? It would be great to have this (and the other one) merged soon.

Copy link
Collaborator Author

@ElsaLopez133 ElsaLopez133 Mar 12, 2026

Choose a reason for hiding this comment

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

I think that if we want to have Copy then we need CredentialC to also implement Copy.
We could also avoid using the union, but I am not sure how to do the enum-style in C otherwise

Copy link
Collaborator

@WilliamTakeshi WilliamTakeshi left a comment

Choose a reason for hiding this comment

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

I didn't take a look on the C code much, because I know there is some discussion if we pause the C bindings for now.

@WilliamTakeshi WilliamTakeshi self-requested a review March 11, 2026 08:45
- Adding a SAFETY comment in lakers-c/src/lib
- Adding a FIXME to define a new error type (method_specifics
   doesnt match with the what was agreed)
- Correcting prk_4e3m and prk_3e2m to avoid matching on symmetric
  credential in statstat (since now it is only the stat)
- Remove _ead_2, since it is used
Copy link
Member

@chrysn chrysn left a comment

Choose a reason for hiding this comment

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

We're converging in many areas, but I still think that the bulk move into edhoc/statstat.rs is excessive, especially seeing that it just leads to duplication later in #415.

Minimizing that duplication is not just something we should aim for because we strive for code quality, but because given that we're the first and AFAIK so far only implementation that has a method >3, will also help LAKE people become better at understanding what typical things that a method customizes are. We won't set those in stone, but our code is also a visual guideline for method developers for what methods such as 4 have changed so far.

Before you go on an editing spree, can we discuss (also possibly in chat or a video call, not sure yet when I'll be around) what good paths forward without too much disruption are? One option I'd consider is to acknowledge that a split-out refactoring step has served its purpose well, we've hashed out a lot in here, but in the interest of not making your processing needlessly complex (undoing some of the move-into-statstat would be hell of a rebase) we can continue on #415 (once we're sure not to lose anything from in here), refactor things back into a less duplicated state there, and then continue working on that view of the change as a whole.

r_prepare_message_2_statstat(state, crypto, cred_r, r, c_r, cred_transfer, ead_2)
}
// (EDHOCMethod::PSK, PrepareMessage2Details::Psk) =>
// FIXME: it is not an error, but more a lack of agreement between peers.
Copy link
Member

Choose a reason for hiding this comment

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

Continuing from https://github.com/lake-rs/lakers/pull/406/changes#r2888495250 so I can change the text here:

Suggested change
// FIXME: it is not an error, but more a lack of agreement between peers.
// FIXME: it is not a runtime error, but a programming error: The library user has seen
// StatStat details in message 1 and still provided other details creating message 2.

#[derive(Debug)]
pub enum InitiatorIdentity {
StatStat { i: BytesP256ElemLen },
Psk,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Psk,
// Psk,

#[derive(Debug)]
pub enum ResponderIdentity {
StatStat { r: BytesP256ElemLen },
Psk,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Psk,
// Psk,

ProcessingM1, ProcessingM2, ProcessingM2MethodSpecifics, ProcessingM3,
ProcessingM3MethodSpecifics, WaitM2, WaitM3, WaitM3MethodSpecifics, WaitM4,
};
use lakers_shared::Crypto as CryptoTrait;
Copy link
Member

Choose a reason for hiding this comment

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

The functions in here IMO still do way more than they should, as evidenced by a diff between lib/src/edhoc/psk.rs and lib/src/edhoc/statstat.rs the in #415:

Those easily share 20 lines in a row in many of the functions, typically at the start and end. (Sometimes that's just masked by different choices of early return and thus indentation, eg. starting from compute prk_out in r_verify_message_3_*).

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