diff --git a/p521/src/arithmetic/field.rs b/p521/src/arithmetic/field.rs index de625f54..0a57b35a 100644 --- a/p521/src/arithmetic/field.rs +++ b/p521/src/arithmetic/field.rs @@ -42,8 +42,7 @@ use elliptic_curve::{ Error, FieldBytesEncoding, }; -#[cfg(target_pointer_width = "32")] -use super::util::u32x18_to_u64x9; +use super::util::uint_to_le_bytes_unchecked; /// Constant representing the modulus serialized as hex. /// p = 2^{521} − 1 @@ -106,19 +105,8 @@ impl FieldElement { /// Does *not* perform a check that the field element does not overflow the order. /// /// Used incorrectly this can lead to invalid results! - #[cfg(target_pointer_width = "32")] pub(crate) const fn from_uint_unchecked(w: U576) -> Self { - Self(u32x18_to_u64x9(w.as_words())) - } - - /// Decode [`FieldElement`] from [`U576`]. - /// - /// Does *not* perform a check that the field element does not overflow the order. - /// - /// Used incorrectly this can lead to invalid results! - #[cfg(target_pointer_width = "64")] - pub(crate) const fn from_uint_unchecked(w: U576) -> Self { - Self(w.to_words()) + Self(fiat_p521_from_bytes(&uint_to_le_bytes_unchecked(w))) } /// Returns the big-endian encoding of this [`FieldElement`]. diff --git a/p521/src/arithmetic/util.rs b/p521/src/arithmetic/util.rs index d11a1eb1..8b6c6168 100644 --- a/p521/src/arithmetic/util.rs +++ b/p521/src/arithmetic/util.rs @@ -1,5 +1,7 @@ //! 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")] @@ -30,3 +32,47 @@ 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 uint_to_le_bytes_unchecked(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(); + debug_assert!( + last_word[1] <= 0x1, + "Input bound for the result[65] is [0x0 ~> 0x1]" + ); + debug_assert!( + last_word[2] == 0 + && last_word[3] == 0 + && last_word[4] == 0 + && last_word[5] == 0 + && last_word[6] == 0 + && last_word[7] == 0, + "Expected last word to have leading zeroes" + ); + + result[i * 8] = last_word[0]; + result[(i * 8) + 1] = last_word[1]; + + result +}