From 91a5b3282840c78e86bb4ee6ccb21e822d59dcf0 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 11 Nov 2023 12:38:09 -0700 Subject: [PATCH] p521: merge `u576_to_le_bytes` into `FieldBytes::from_uint_unchecked` (#969) Moves `u576_to_le_bytes` from the `util` module to inside of `from_uint_unchecked`. The old function in the `util` module was not general-purpose, but specialized to the case of decoding P-521 base field elements in the unsaturated format used by fiat-crypto's Solinas prime backend. Additionally `FieldElement::from_uint_unchecked` is the only caller, and it really is an implementation detail of that function. --- p521/src/arithmetic/field.rs | 37 ++++++++++++++++++++++++++++++++++-- p521/src/arithmetic/util.rs | 32 ------------------------------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/p521/src/arithmetic/field.rs b/p521/src/arithmetic/field.rs index 2dc67c14..9bbee554 100644 --- a/p521/src/arithmetic/field.rs +++ b/p521/src/arithmetic/field.rs @@ -43,7 +43,8 @@ use elliptic_curve::{ Error, FieldBytesEncoding, }; -use super::util::u576_to_le_bytes; +#[cfg(target_pointer_width = "32")] +use super::util; /// Constant representing the modulus serialized as hex. /// p = 2^{521} − 1 @@ -106,7 +107,39 @@ impl FieldElement { /// /// Used incorrectly this can lead to invalid results! pub(crate) const fn from_uint_unchecked(w: U576) -> Self { - Self(fiat_p521_from_bytes(&u576_to_le_bytes(w))) + // Converts the saturated representation used by `U576` into a 66-byte array with a + // little-endian byte ordering. + // + // TODO(tarcieri): use `FieldBytesEncoding::encode_field_bytes` when `const impl` is stable + #[cfg(target_pointer_width = "32")] + let words = util::u32x18_to_u64x9(w.as_words()); + #[cfg(target_pointer_width = "64")] + let words = w.as_words(); + + let mut le_bytes = [0u8; 66]; + let mut i = 0; + + while i < words.len() - 1 { + let word = words[i].to_le_bytes(); + let start = i * 8; + le_bytes[start] = word[0]; + le_bytes[start + 1] = word[1]; + le_bytes[start + 2] = word[2]; + le_bytes[start + 3] = word[3]; + le_bytes[start + 4] = word[4]; + le_bytes[start + 5] = word[5]; + le_bytes[start + 6] = word[6]; + le_bytes[start + 7] = word[7]; + i += 1; + } + + let last_word = words[8].to_le_bytes(); + le_bytes[i * 8] = last_word[0]; + le_bytes[(i * 8) + 1] = last_word[1]; + + // Decode the little endian serialization into the unsaturated big integer form used by + // the fiat-crypto synthesized code. + Self(fiat_p521_from_bytes(&le_bytes)) } /// Returns the big-endian encoding of this [`FieldElement`]. diff --git a/p521/src/arithmetic/util.rs b/p521/src/arithmetic/util.rs index 1b14e703..d11a1eb1 100644 --- a/p521/src/arithmetic/util.rs +++ b/p521/src/arithmetic/util.rs @@ -1,7 +1,5 @@ //! Utility functions. -use elliptic_curve::bigint::U576; - /// Convert an 18-element array of `u32` into a 9-element array of `u16`, /// assuming integer arrays are in little-endian order. #[cfg(target_pointer_width = "32")] @@ -32,33 +30,3 @@ pub(crate) const fn u64x9_to_u32x18(w: &[u64; 9]) -> [u32; 18] { ret } - -/// Converts the saturated representation [`U576`] into a 528bit array. Each -/// word is copied in little-endian. -pub const fn u576_to_le_bytes(w: U576) -> [u8; 66] { - #[cfg(target_pointer_width = "32")] - let words = u32x18_to_u64x9(w.as_words()); - #[cfg(target_pointer_width = "64")] - let words = w.as_words(); - - let mut result: [u8; 66] = [0u8; 66]; - let mut i = 0; - while i < words.len() - 1 { - let word = words[i].to_le_bytes(); - let start = i * 8; - result[start] = word[0]; - result[start + 1] = word[1]; - result[start + 2] = word[2]; - result[start + 3] = word[3]; - result[start + 4] = word[4]; - result[start + 5] = word[5]; - result[start + 6] = word[6]; - result[start + 7] = word[7]; - i += 1; - } - let last_word = words[8].to_le_bytes(); - result[i * 8] = last_word[0]; - result[(i * 8) + 1] = last_word[1]; - - result -}