Skip to content

Commit

Permalink
p521: Use unsaturated limbs (#945)
Browse files Browse the repository at this point in the history
`U576::words()` returns saturated 64-bit limbs but the field
arithmetic for tight points require 59-bit limbs, convert it into a
little endian byte array and let `fiat-crypto` deal with the
unsaturation.

Signed-off-by: Arvind Mukund <[email protected]>
  • Loading branch information
MasterAwesome authored Nov 1, 2023
1 parent c7b2262 commit 1f6eb5b
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 14 deletions.
16 changes: 2 additions & 14 deletions p521/src/arithmetic/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`].
Expand Down
46 changes: 46 additions & 0 deletions p521/src/arithmetic/util.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand Down Expand Up @@ -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
}

0 comments on commit 1f6eb5b

Please sign in to comment.