-
Notifications
You must be signed in to change notification settings - Fork 9
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?
Conversation
/// with an adjustable number of Feistel rounds | ||
pub struct FF1fr<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher> { |
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.
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.
Choose a reason for hiding this comment
The 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!)
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.
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.
/// A struct for performing hardened FF1 encryption and decryption operations | ||
/// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// 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 |
@@ -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>; |
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.
pub type FF1<CIPH> = FF1fr<10, CIPH>; | |
pub type FF1<CIPH> = FF1Core<10, CIPH>; |
ciph: CIPH, | ||
radix: Radix, | ||
} | ||
|
||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
impl<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher + KeyInit> FF1fr<FEISTEL_ROUNDS, CIPH> { | |
impl<const FEISTEL_ROUNDS: u8, CIPH: BlockCipher + KeyInit> FF1Core<FEISTEL_ROUNDS, CIPH> { |
/// 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 }) |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The 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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let ff = FF1h::<Aes256>::new(&key, radix).unwrap(); | |
let ff = FF1R18::<Aes256>::new(&key, radix).unwrap(); |
This is a simple PR to allow increasing the number of Feistel rounds.
It does not change the default
FF1
API (that uses 10 rounds) but offers 2 additional FF1 structuresFF1fr
that takes as a const generic the number of desired Fesitel roundsFF1h
that 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