Add the ability to increase the number of Feistel Rounds#35
Add the ability to increase the number of Feistel Rounds#35bgrieder wants to merge 6 commits intostr4d:mainfrom
Conversation
src/ff1.rs
Outdated
| /// with an adjustable number of Feistel rounds | ||
| pub struct FF1fr<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher> { |
There was a problem hiding this comment.
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. FEISTEL_ROUNDS >= 10. But for now, this should not be part of the public API:
| /// with an adjustable number of Feistel rounds | |
| pub struct FF1fr<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher> { | |
| /// with an adjustable number of Feistel rounds. | |
| struct FF1Core<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher> { |
There was a problem hiding this comment.
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!)
There was a problem hiding this comment.
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.
src/ff1.rs
Outdated
| /// using 18 Feistel rounds | ||
| pub type FF1h<CIPH> = FF1fr<18, CIPH>; | ||
|
|
||
| /// A struct for performing FF1 encryption and decryption operations. |
There was a problem hiding this comment.
| /// A struct for performing FF1 encryption and decryption operations. | |
| /// A struct for performing FF1 encryption and decryption operations |
| /// A struct for performing FF1 encryption and decryption operations | ||
| /// using the default 10 Feistel rounds |
There was a problem hiding this comment.
| /// A struct for performing FF1 encryption and decryption operations | |
| /// using the default 10 Feistel rounds | |
| /// 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 |
|
|
||
| /// A struct for performing FF1 encryption and decryption operations | ||
| /// using the default 10 Feistel rounds | ||
| pub type FF1<CIPH> = FF1fr<10, CIPH>; |
There was a problem hiding this comment.
| pub type FF1<CIPH> = FF1fr<10, CIPH>; | |
| pub type FF1<CIPH> = FF1Core<10, CIPH>; |
| } | ||
|
|
||
| impl<CIPH: BlockCipher + KeyInit> FF1<CIPH> { | ||
| impl<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher + KeyInit> FF1fr<FEISTEL_ROUNDS, CIPH> { |
There was a problem hiding this comment.
| impl<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher + KeyInit> FF1fr<FEISTEL_ROUNDS, CIPH> { | |
| impl<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher + KeyInit> FF1Core<FEISTEL_ROUNDS, CIPH> { |
| let ciph = CIPH::new(GenericArray::from_slice(key)); | ||
| let radix = Radix::from_u32(radix)?; | ||
| Ok(FF1 { ciph, radix }) | ||
| Ok(FF1fr { ciph, radix }) |
There was a problem hiding this comment.
| Ok(FF1fr { ciph, radix }) | |
| Ok(Self { ciph, radix }) |
|
|
||
| impl<CIPH: BlockCipher + BlockEncrypt + Clone> FF1<CIPH> { | ||
| impl<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher + BlockEncrypt + Clone> | ||
| FF1fr<FEISTEL_ROUNDS, CIPH> |
There was a problem hiding this comment.
| FF1fr<FEISTEL_ROUNDS, CIPH> | |
| FF1Core<FEISTEL_ROUNDS, CIPH> |
| /// A struct for performing hardened FF1 encryption and decryption operations | ||
| /// using 18 Feistel rounds | ||
| pub type FF1h<CIPH> = FF1fr<18, CIPH>; |
There was a problem hiding this comment.
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 FF1_1 (underscores are allowed in struct names under Rust naming conventions when separating integers). In the interest of not colliding with this, I propose that we name this increased-round modification :
| /// A struct for performing hardened FF1 encryption and decryption operations | |
| /// using 18 Feistel rounds | |
| pub type FF1h<CIPH> = FF1fr<18, 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> = FF1Core<18, CIPH>; |
| @@ -0,0 +1,19 @@ | |||
| use aes::Aes256; | |||
|
|
|||
| use crate::ff1::{BinaryNumeralString, FF1h}; | |||
There was a problem hiding this comment.
| use crate::ff1::{BinaryNumeralString, FF1h}; | |
| use crate::ff1::{BinaryNumeralString, FF1R18}; |
| let radix = 2; | ||
| let pt = [0xab, 0xcd, 0xef]; | ||
|
|
||
| let ff = FF1h::<Aes256>::new(&key, radix).unwrap(); |
There was a problem hiding this comment.
| let ff = FF1h::<Aes256>::new(&key, radix).unwrap(); | |
| let ff = FF1R18::<Aes256>::new(&key, radix).unwrap(); |
feat: improvement fixes
fix: cargo toml
This is a simple PR to allow increasing the number of Feistel rounds.
It does not change the default
FF1API (that uses 10 rounds) but offers 2 additional FF1 structuresFF1frthat takes as a const generic the number of desired Fesitel roundsFF1hthat set the number of rounds to 18The value 18, comes from this cryptanalysis paper: https://eprint.iacr.org/2020/1311.pdf, page 18, last paragraph before the acknowledgments