-
Notifications
You must be signed in to change notification settings - Fork 11
Add the ability to increase the number of Feistel Rounds #35
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
628cec3
569f403
6ebc151
3596de5
66f50b3
df0a1e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,6 +22,9 @@ pub use self::alloc::{BinaryNumeralString, FlexibleNumeralString}; | |||||||||||||||||||||||
| #[cfg(test)] | ||||||||||||||||||||||||
| mod test_vectors; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| #[cfg(test)] | ||||||||||||||||||||||||
| mod ff1_18; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// The minimum allowed numeral string length for any radix. | ||||||||||||||||||||||||
| const MIN_NS_LEN: u32 = 2; | ||||||||||||||||||||||||
| /// The maximum allowed numeral string length for any radix. | ||||||||||||||||||||||||
|
|
@@ -232,24 +235,35 @@ fn generate_s<'a, CIPH: BlockEncrypt>( | |||||||||||||||||||||||
| .take(d) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// A struct for performing FF1 encryption and decryption operations | ||||||||||||||||||||||||
| /// using the default 10 Feistel rounds | ||||||||||||||||||||||||
| pub type FF1<CIPH> = FF1fr<10, CIPH>; | ||||||||||||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// A struct for performing hardened FF1 encryption and decryption operations | ||||||||||||||||||||||||
| /// using 18 Feistel rounds | ||||||||||||||||||||||||
| pub type FF1h<CIPH> = FF1fr<18, CIPH>; | ||||||||||||||||||||||||
|
Comment on lines
+253
to
+261
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is technically no longer FF1, because the number of rounds is part of the FF1 specification. So I do not want to describe it as "hardened FF1", not least because any future research breakthroughs could lead to further weakening such that 18 rounds also becomes insufficient. NIST faced a similar issue with FF3, the original version of which was broken in a way that couldn't be fixed by changing bounds. They named the updated version FF3-1, suggesting this is likely how they would also name subsequent updates. If an FF1-1 were published, I'd name it
Suggested change
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// A struct for performing FF1 encryption and decryption operations. | ||||||||||||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
| pub struct FF1<CIPH: BlockCipher> { | ||||||||||||||||||||||||
| /// with an adjustable number of Feistel rounds | ||||||||||||||||||||||||
| pub struct FF1fr<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher> { | ||||||||||||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per #25 (comment) I do not want to expose the ability to configure an arbitrary number of rounds, as this includes round numbers that are insecure. If/when Rust gains full const generics, I would consider reopening this, because then we could enforce e.g.
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not do this? /// Non-standard (in general) FPE algorithm based on FF1 but with an adjustable number
/// of Feistel rounds.
///
/// The number of Feistel rounds will be `10 + EXTRA_ROUNDS_ABOVE_10`. Apart from that
/// modification, this follows the FF1 algorithm as specified in [NIST SP 800-38G revision 1],
/// with a minimum domain size of $\mathsf{radix}^\mathsf{minlen} \geq 1,000,000$.
///
/// [NIST Special Publication 800-38G]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38Gr1-draft.pdf
pub struct FF1WithExtraRounds<const EXTRA_ROUNDS_ABOVE_10: u8, CIPH: BlockCipher> { ... }
/// A struct for performing FF1 encryption and decryption operations.
///
/// This implements FF1 as specified in [NIST SP 800-38G revision 1], with 10 Feistel
/// rounds and a minimum domain size of $\mathsf{radix}^\mathsf{minlen} \geq 1,000,000$.
///
/// [NIST Special Publication 800-38G]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38Gr1-draft.pdf
pub type FF1<CIPH> = FF1WithExtraRounds<0, CIPH>;
/// Non-standard FPE algorithm that modifies FF1 to use 18 Feistel rounds.
///
/// Other than the modification to the number of Feistel rounds, this follows the FF1
/// algorithm as specified in [NIST SP 800-38G revision 1], with a minimum domain size of
/// $\mathsf{radix}^\mathsf{minlen} \geq 1,000,000$.
///
/// [NIST Special Publication 800-38G]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38Gr1-draft.pdf
pub type FF1R18<CIPH> = FF1WithExtraRounds<8, CIPH>;(poor man's range typing!)
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's no way to raise the floor of this bound later if NIST devices to replace FF1 with a higher-round version. So for now I'd prefer to only expose explicit round versions. |
||||||||||||||||||||||||
| ciph: CIPH, | ||||||||||||||||||||||||
| radix: Radix, | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl<CIPH: BlockCipher + KeyInit> FF1<CIPH> { | ||||||||||||||||||||||||
| impl<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher + KeyInit> FF1fr<FEISTEL_ROUNDS, CIPH> { | ||||||||||||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
| /// Creates a new FF1 object for the given key and radix. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// Returns an error if the given radix is not in [2..2^16]. | ||||||||||||||||||||||||
| pub fn new(key: &[u8], radix: u32) -> Result<Self, InvalidRadix> { | ||||||||||||||||||||||||
| let ciph = CIPH::new(GenericArray::from_slice(key)); | ||||||||||||||||||||||||
| let radix = Radix::from_u32(radix)?; | ||||||||||||||||||||||||
| Ok(FF1 { ciph, radix }) | ||||||||||||||||||||||||
| Ok(FF1fr { ciph, radix }) | ||||||||||||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| impl<CIPH: BlockCipher + BlockEncrypt + Clone> FF1<CIPH> { | ||||||||||||||||||||||||
| impl<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher + BlockEncrypt + Clone> | ||||||||||||||||||||||||
| FF1fr<FEISTEL_ROUNDS, CIPH> | ||||||||||||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| /// Encrypts the given numeral string. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// Returns an error if the numeral string is not in the required radix. | ||||||||||||||||||||||||
|
|
@@ -294,7 +308,7 @@ impl<CIPH: BlockCipher + BlockEncrypt + Clone> FF1<CIPH> { | |||||||||||||||||||||||
| for _ in 0..((((-(t as i32) - (b as i32) - 1) % 16) + 16) % 16) { | ||||||||||||||||||||||||
| prf.update(&[0]); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| for i in 0..10 { | ||||||||||||||||||||||||
| for i in 0..FEISTEL_ROUNDS { | ||||||||||||||||||||||||
| let mut prf = prf.clone(); | ||||||||||||||||||||||||
| prf.update(&[i]); | ||||||||||||||||||||||||
| prf.update(x_b.num_radix(self.radix.to_u32()).to_bytes(b).as_ref()); | ||||||||||||||||||||||||
|
|
@@ -372,8 +386,8 @@ impl<CIPH: BlockCipher + BlockEncrypt + Clone> FF1<CIPH> { | |||||||||||||||||||||||
| for _ in 0..((((-(t as i32) - (b as i32) - 1) % 16) + 16) % 16) { | ||||||||||||||||||||||||
| prf.update(&[0]); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| for i in 0..10 { | ||||||||||||||||||||||||
| let i = 9 - i; | ||||||||||||||||||||||||
| for i in 0..FEISTEL_ROUNDS { | ||||||||||||||||||||||||
| let i = FEISTEL_ROUNDS - 1 - i; | ||||||||||||||||||||||||
| let mut prf = prf.clone(); | ||||||||||||||||||||||||
| prf.update(&[i]); | ||||||||||||||||||||||||
| prf.update(x_a.num_radix(self.radix.to_u32()).to_bytes(b).as_ref()); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,19 @@ | ||||||
| use aes::Aes256; | ||||||
|
|
||||||
| use crate::ff1::{BinaryNumeralString, FF1h}; | ||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| #[test] | ||||||
| fn test_doc_example_18_rounds() { | ||||||
| let key = [0; 32]; | ||||||
| let radix = 2; | ||||||
| let pt = [0xab, 0xcd, 0xef]; | ||||||
|
|
||||||
| let ff = FF1h::<Aes256>::new(&key, radix).unwrap(); | ||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| let ct = ff | ||||||
| .encrypt(&[], &BinaryNumeralString::from_bytes_le(&pt)) | ||||||
| .unwrap(); | ||||||
| assert_eq!(ct.to_bytes_le(), [0x5a, 0x6c, 0x20]); | ||||||
|
|
||||||
| let p2 = ff.decrypt(&[], &ct).unwrap(); | ||||||
| assert_eq!(p2.to_bytes_le(), pt); | ||||||
| } | ||||||
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.