Conversation
# Conflicts: # QUALITY_AND_STYLE.md # crypto/core/src/traits.rs
…ms consume randomness
romen
left a comment
There was a problem hiding this comment.
Is passing &mut dyn RNG really necessary?
Rather than
fn keygen_from_rng<R: RNG>(rng: R) -> Result<(PK, SK), KEMError> { ... }Committing to dynamic dispatch is not necessarily always evil, but does prevent the compiler to optimize as much, and does have some runtime performance penalty due to vtable indirection to resolve the dispatching.
On the other hand, if you intentionally want to support RNG implementations that are known only at runtime and not at compile time, a dyn RNG ref is the way to go.
|
The choice of
That said, if we really want to leave room for static dispatch, a common idiomatic compromise is to keep the public methods generic over Conclusion: There is no need to revert |
|
Thanks @romen . I am still very much wrapping my head around how dyn works. This discussion is helpful. I hadn't really considered that these two versions are alternate ways to achieve the same behaviour, but with some different tradeoffs fn keygen_from_rng(rng: &mut dyn RNG) -> Result<(PK, SK), KEMError> {
let mut seed = KeyMaterial::<64>::new();
rng.fill_keymaterial_out(&mut seed)?;
Self::keygen_from_seed(&seed)
}vs fn keygen_from_rng<R: RNG>(rng: R) -> Result<(PK, SK), KEMError> {
let mut seed = KeyMaterial::<64>::new();
rng.fill_keymaterial_out(&mut seed)?;
Self::keygen_from_seed(&seed)
}My feeling is that Dyn is correct here; especially if we expect this to be highly dynamic at runtime -- for example if the calling application wakes up, looks around, decides what kinds of OS or HW RNGs are present on its system, and then chooses an RNG impl to provide to the alg. I also think that Dyn will play nicer with RNGFactory, which again is an attempt to abstract away the concrete type But this is great. Please continue educating me on the tradeoffs! |
|
@Quant-TheodoreFelix I have to ask, are all your comments AI-generated? I find this comment very odd:
That is referring to a TODO comment that I added in this PR, that is completely unrelated to this discussion, so why would you "recommend" that? |
| pub trait SymmetricCipher<const KEY_LEN: usize, const INIT_DATA_LEN: usize>: | ||
| Algorithm + Secret | ||
| { | ||
| #[cfg(std)] |
There was a problem hiding this comment.
#[cfg(std)] never evaluates true and methods are silently dropped. Standard gate feature is #[cfg(feature = "std")]. No std feature or --cfg std anywhere in the workspace. AFAIK, nothing in .cargo/config or std feature in the workspace.
For traits.rs, as written, encrypt/decrypt/aead_encrypt/aead_decrypt methods are compiled out unconditionally.
Should be #[cfg(feature = "std")], and a corresponding "std" feature should be added to crate's Cargo.toml
There was a problem hiding this comment.
Yep. Or maybe we go with the more idiomatic #[cfg(feature = "alloc")].
Either way, we'll deal with that when we come to it in a different Issue / PR.
| _ => panic!("Unexpected error"), | ||
| }; | ||
|
|
||
| // error case: security strengths too weak and too strong |
There was a problem hiding this comment.
Same copy-paste bug error seen in our last PR. I think you mean to use key instead of mac_key for your encrypt_out(&mac_key, msg, &mut ct) loop, as we're not actually testing security strength properly here
There was a problem hiding this comment.
Umm, yeah. Good point. I seem to have pulled in a whole bunch of commits that are not related to the dyn RNG feature.
Maybe I'll kill this PR and start fresh with only the commits that I intended to include.
| // Put any config options here | ||
| } | ||
|
|
||
| impl TestFrameworkStreamCipher { |
There was a problem hiding this comment.
Public scaffolding; to-do for later
| const SS_LEN: usize, | ||
| >: Sized | ||
| { | ||
| /// Performs an encapsulation against the given public key. |
There was a problem hiding this comment.
encaps_rng is a required method on the core KEM trait but breaks external implementors since we do not have a default body. Do we want a default?
There was a problem hiding this comment.
I don't understand this comment. What is a default body? Do we have those anywhere else?
| { | ||
| /// Run a keygen using the provided RNG implementation. | ||
| // Should still be ok in FIPS mode, provided that you're using the FIPS-approved RNG. | ||
| fn keygen_from_rng(rng: &mut dyn RNG) -> Result<(PK, SK), KEMError> { |
There was a problem hiding this comment.
keygen_from_rng calls keygen_from_seed after rng.fill_keymaterial_out(&mut seed), which just reproduces seed bytes verbatim, but doesn't independently validate keygen.
Might want to expand to assert against existing KAT vectors
There was a problem hiding this comment.
I don't understand what you mean by "independently valitade keygen". What is there to validate?
| Ok(len) | ||
| } | ||
|
|
||
| fn security_strength(&self) -> SecurityStrength { |
There was a problem hiding this comment.
Unconditionally setting security strength to 256 bit regardless of how many bytes were written. Semes test-only and current callers all use buffers greater than or equal to 32 bytes, but nothing this assumption might come in handy.
There was a problem hiding this comment.
Yeah, I mean, this is in the `core-test-framework, and it's an RNG that's not at all R, so if you try to use this for some non-test purpose, I think you're gonna have bigger problems 😝
|
@ounsworth have a few questions. Noticed there is asymmetry in entropy accounting for encaps_rng.
generate_keymaterial_out, however, sets key type/strength. FIPS 203 doesn't mandate a strength gate on m though. Was this asymmetry between these two deliberate? @ounsworth for the API, do we want the core KEM and Signature traits to also use keygen_from_rng? So far, it's only on MLKEMTrait and MLDSATrait, while encaps_rng now part of the core KEM trait. Unsure if you wanted to resolve that in this PR or defer until some other item is done first. @ounsworth add_seed_keymaterial now borrows instead of consuming, since you changed it from impl KeyMaterialTrait to &dyn KeyMaterialTrait. Before, the secret seed was moved in and presumably dropped/zeroized by the callee. With your change, the caller now owns it as its borrowing the secret. So the function cannot drop/zeroize it as part of consuming it. Was this intentional? If so, we want to be careful as this overwrites the old behavior of intentionally destroying the secret after use in the old code. |
Ooo! Great observation! No, that was not intentional; I hadn't thought about this carefully enough. Let me think about this more carefully. If FIPS 203 doesn't specify any entropy requirements for
|
I'm not 100% sure that I'm following this; but I think either way it would end up using the library's default RNG, it's just a question of where we instantiate that? At some point we may want to add a crypto agility layer that takes the "defaults" more dynamically from a config file or env var, and this would be the kind of question to ask at that point, but that's not a feature I'm prepared to put time and effort into right now. |
|
Note-to-self: this PR is stacked on top of the symmetric cipher traits branch, not for any good reason, just because David and I were working on both in the same work session and they got entangled.
|
Another phenomenal observation. Not intentional. I'm still wrapping my head around all the niggly implications of Rust's dyn system. Can you even move a dyn object? That's gonna have
|
|
Closing this unmerged in favour of #43 |
This change set makes the RNG trait dyn-compatible and also leverages it within ML-DSA and ML-KEM so that keygen() and encaps() have versions that can accept a
&dyn RNGand source their randomness from that.This change brings bc-rust a bit closer to bc-java.