mirrored from https://www.bouncycastle.org/repositories/bc-rust
-
Notifications
You must be signed in to change notification settings - Fork 16
New feature: Suspendable (serializable state) #48
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
Merged
ounsworth
merged 20 commits into
bcgit:release/0.1.2alpha
from
ounsworth:feature/serialize_state
Jul 10, 2026
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
19ea35f
initial trait defn for SerializableState
ounsworth 64365de
sha2 impls SerializableState
ounsworth f75bc5d
rustfmt
ounsworth 54f93f6
Incorporating Nikola and David's feedback
ounsworth 46100d3
An in-progress SHA3 object can now be serialized
ounsworth 6b3ecec
Impl'd SerializableState for MLDSA, and some quality-of-life fixes fo…
ounsworth d7192dd
rustfmt
ounsworth 45ba742
Impl'd SerializableState for MLDSA-lowmemory.
ounsworth 3e1ce14
Merge branch 'release/0.1.2alpha' into feature/serialize_state
ounsworth d9b06bd
Impl'd SerializableKeyedState for HMAC.
ounsworth e464f68
Fixed a bug in HMAC that could cause panics.
ounsworth 937d859
Implemented Suspendable for HKDF and a bunch of other tidy-up.
ounsworth 1dacaa2
rustfmt
ounsworth 07889fc
HMAC now impl's Drop like it should.
ounsworth 3c8d64b
Adjusted the Suspendable version checker to allow future versions fro…
ounsworth fd17f91
HKDF now explicitly carries a lib_ver tag
ounsworth fac79c3
Fixed Keccak corrupt-state rejection, Hashfactory honest Algorithm le…
officialfrancismendoza c4e4373
rustfmt of all code changes in (#48)
officialfrancismendoza fcec4af
tweaks
ounsworth 751c033
merge from release/0.1.2alpha
ounsworth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| use bouncycastle_core::errors::SuspendableError; | ||
| use bouncycastle_core::suspendable_state::{LIB_VERSION, SemVer}; | ||
| use bouncycastle_core::traits::{Suspendable, SuspendableKeyed}; | ||
|
|
||
| pub struct TestFrameworkSuspendableState {} | ||
|
|
||
| impl TestFrameworkSuspendableState { | ||
| pub fn new() -> Self { | ||
| Self {} | ||
| } | ||
|
|
||
| /// Test all the members of trait SerializableState. | ||
| /// | ||
| /// Expects ta be handed an instance of the object that has some in-progress state to be serialized. | ||
| pub fn test<const SERIALIZED_STATE_LEN: usize, S: Suspendable<SERIALIZED_STATE_LEN> + Clone>( | ||
| &self, | ||
| instance: &S, | ||
| ) { | ||
| // There's not a lot we can test here in the abstract, but we can test a few things to | ||
| // ensure that the SerializableState trait has been impl'd correctly. | ||
|
|
||
| // we need to work on a clone because .serialize_state() moves self, which you can't do on a | ||
| // borrowed instance. | ||
| let instance_clone = instance.clone(); | ||
|
|
||
| // You can serialize and then deserialize the state. | ||
| let serialized_state = instance_clone.suspend(); | ||
| assert_eq!(serialized_state.len(), SERIALIZED_STATE_LEN); | ||
|
|
||
| let _deserialized_state = S::from_suspended(serialized_state).unwrap(); | ||
|
|
||
| // The serialized state MUST include a prefix indicating the current version of the library. | ||
| let state_sized: [u8; 3] = serialized_state[..3].try_into().unwrap(); | ||
| assert_eq!(SemVer::from(state_sized), LIB_VERSION); | ||
|
|
||
| // All implementations MUST reject a serialized state from lib ver 0.0.0 | ||
| // This doesn't really serve any purpose except testing that all impl's have properly | ||
| // used the helper functions. | ||
| let mut ver0_serialized_state = serialized_state.clone(); | ||
| ver0_serialized_state[..3].copy_from_slice(&[0, 0, 0]); | ||
| match S::from_suspended(ver0_serialized_state) { | ||
| Err(SuspendableError::IncompatibleVersion) => { /* good */ } | ||
| _ => { | ||
| panic!("Expected IncompatibleVersion error") | ||
| } | ||
| } | ||
|
|
||
| // All implementations MUST reject a serialized state from a future MAJOR or MINOR version. | ||
| let mut future_ver = LIB_VERSION; | ||
| future_ver.major += 1; | ||
| let mut futurever_serialized_state = serialized_state.clone(); | ||
| futurever_serialized_state[..3] | ||
| .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); | ||
| match S::from_suspended(futurever_serialized_state) { | ||
| Err(SuspendableError::IncompatibleVersion) => { /* good */ } | ||
| _ => { | ||
| panic!("Expected IncompatibleVersion error") | ||
| } | ||
| } | ||
|
|
||
| let mut future_ver = LIB_VERSION; | ||
| future_ver.minor += 1; | ||
| let mut futurever_serialized_state = serialized_state.clone(); | ||
| futurever_serialized_state[..3] | ||
| .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); | ||
| match S::from_suspended(futurever_serialized_state) { | ||
| Err(SuspendableError::IncompatibleVersion) => { /* good */ } | ||
| _ => { | ||
| panic!("Expected IncompatibleVersion error") | ||
| } | ||
| } | ||
|
|
||
| // but should accept anything on the same patch stream. | ||
| let mut future_ver = LIB_VERSION; | ||
| future_ver.patch += 1; | ||
| let mut futurever_serialized_state = serialized_state.clone(); | ||
| futurever_serialized_state[..3] | ||
| .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); | ||
| let _deserialized_state = S::from_suspended(futurever_serialized_state).unwrap(); | ||
|
|
||
| // ... even up to patch 255 | ||
| let mut future_ver = LIB_VERSION; | ||
| future_ver.patch = 255; | ||
| let mut futurever_serialized_state = serialized_state.clone(); | ||
| futurever_serialized_state[..3] | ||
| .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); | ||
| let _deserialized_state = S::from_suspended(futurever_serialized_state).unwrap(); | ||
| } | ||
| } | ||
|
|
||
| pub struct TestFrameworkSuspendableKeyedState {} | ||
|
|
||
| impl TestFrameworkSuspendableKeyedState { | ||
| pub fn new() -> Self { | ||
| Self {} | ||
| } | ||
|
|
||
| /// Test all the members of trait SerializableState. | ||
| /// | ||
| /// Expects ta be handed an instance of the object that has some in-progress state to be serialized. | ||
| pub fn test< | ||
| const SERIALIZED_STATE_LEN: usize, | ||
| S: SuspendableKeyed<SERIALIZED_STATE_LEN> + Clone, | ||
| >( | ||
| &self, | ||
| instance: &S, | ||
| key: &S::Key, | ||
| ) { | ||
| // There's not a lot we can test here in the abstract, but we can test a few things to | ||
| // ensure that the SerializableState trait has been impl'd correctly. | ||
|
|
||
| // we need to work on a clone because .serialize_state() moves self, which you can't do on a | ||
| // borrowed instance. | ||
| let instance_clone = instance.clone(); | ||
|
|
||
| // You can serialize and then deserialize the state. | ||
| let serialized_state = instance_clone.suspend(); | ||
| assert_eq!(serialized_state.len(), SERIALIZED_STATE_LEN); | ||
|
|
||
| let _deserialized_state = S::from_suspended(serialized_state, key).unwrap(); | ||
|
|
||
| // The serialized state MUST include a prefix indicating the current version of the library. | ||
| let state_sized: [u8; 3] = serialized_state[..3].try_into().unwrap(); | ||
| assert_eq!(SemVer::from(state_sized), LIB_VERSION); | ||
|
|
||
| // All implementations MUST reject a serialized state from lib ver 0.0.0 | ||
| // This doesn't really serve any purpose except testing that all impl's have properly | ||
| // used the helper functions. | ||
| let mut ver0_serialized_state = serialized_state.clone(); | ||
| ver0_serialized_state[..3].copy_from_slice(&[0, 0, 0]); | ||
| match S::from_suspended(ver0_serialized_state, key) { | ||
| Err(SuspendableError::IncompatibleVersion) => { /* good */ } | ||
| _ => { | ||
| panic!("Expected IncompatibleVersion error") | ||
| } | ||
| } | ||
|
|
||
| // All implementations MUST reject a serialized state from a future MAJOR or MINOR version. | ||
| let mut future_ver = LIB_VERSION; | ||
| future_ver.major += 1; | ||
| let mut futurever_serialized_state = serialized_state.clone(); | ||
| futurever_serialized_state[..3] | ||
| .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); | ||
| match S::from_suspended(futurever_serialized_state, key) { | ||
| Err(SuspendableError::IncompatibleVersion) => { /* good */ } | ||
| _ => { | ||
| panic!("Expected IncompatibleVersion error") | ||
| } | ||
| } | ||
|
|
||
| let mut future_ver = LIB_VERSION; | ||
| future_ver.minor += 1; | ||
| let mut futurever_serialized_state = serialized_state.clone(); | ||
| futurever_serialized_state[..3] | ||
| .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); | ||
| match S::from_suspended(futurever_serialized_state, key) { | ||
| Err(SuspendableError::IncompatibleVersion) => { /* good */ } | ||
| _ => { | ||
| panic!("Expected IncompatibleVersion error") | ||
| } | ||
| } | ||
|
|
||
| // but should accept anything on the same patch stream. | ||
| let mut future_ver = LIB_VERSION; | ||
| future_ver.patch += 1; | ||
| let mut futurever_serialized_state = serialized_state.clone(); | ||
| futurever_serialized_state[..3] | ||
| .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); | ||
| let _deserialized_state = S::from_suspended(futurever_serialized_state, key).unwrap(); | ||
|
|
||
| // ... even up to patch 255 | ||
| let mut future_ver = LIB_VERSION; | ||
| future_ver.patch = 255; | ||
| let mut futurever_serialized_state = serialized_state.clone(); | ||
| futurever_serialized_state[..3] | ||
| .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); | ||
| let _deserialized_state = S::from_suspended(futurever_serialized_state, key).unwrap(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,4 +7,5 @@ | |
|
|
||
| pub mod errors; | ||
| pub mod key_material; | ||
| pub mod suspendable_state; | ||
| pub mod traits; | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
IncorrectKey is never constructed, and its doc claims tr-based wrong-key detection that isn't implemented (MuBuilder::from_suspended takes no key; HMAC/HKDF can't detect it).
Recommend either wire up the detection or remove the variant ,so callers don't assume wrong-key protection that doesn't exist.
Uh oh!
There was an error while loading. Please reload this page.
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.
Good catch. I added that when I was impl'ing Suspendable for ML-DSA, but ended up backing that out and only impl'ing it for ML-DSA::MuBuilder. So that was a leftover.
Now removed.