Skip to content

Commit

Permalink
p521: merge u576_to_le_bytes into FieldBytes::from_uint_unchecked (
Browse files Browse the repository at this point in the history
…#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.
  • Loading branch information
tarcieri authored Nov 11, 2023
1 parent 24e3306 commit 91a5b32
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 34 deletions.
37 changes: 35 additions & 2 deletions p521/src/arithmetic/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`].
Expand Down
32 changes: 0 additions & 32 deletions p521/src/arithmetic/util.rs
Original file line number Diff line number Diff line change
@@ -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")]
Expand Down Expand Up @@ -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
}

0 comments on commit 91a5b32

Please sign in to comment.