Skip to content

Commit e092afb

Browse files
authored
p521: initial Scalar with fiat-crypto arithmetic (RustCrypto#760)
Adds a scalar field implementation generated by fiat-crypto, run with the following arguments: $ ./word_by_word_montgomery --lang Rust --inline p521_scalar 64 '0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409' The output has been postprocessed by the `fiat-constify` utility, which rewrites the functions in the fiat-crypto output as `const fn`. Only a 64-bit implementation has been synthesized. I encountered a stack overflow when attempting a 32-bit one. Constants for the `PrimeField` impl were calculated in `sage` as follows: sage: p = 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f7 ....: 09a5d03bb5c9b8899c47aebb6fb71e91386409 sage: multiplicative_generator = GF(p).primitive_element() sage: p_minus_1_bin = (p - 1).binary() sage: s = len(p_minus_1_bin) - len(p_minus_1_bin.rstrip('0')) # count trailing zeros in binary sage: t = (p - 1) >> s sage: root_of_unity = pow(multiplicative_generator,t,p) sage: delta = pow(multiplicative_generator, 2^s, p) sage: multiplicative_generator 3 sage: p_minus_1_bin '11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110100101000110000110100001111000001110111111001011111001011001101011011111111100110000000001010010001111011100001001101001011101000000111011101101011100100110111000100010011001110001000111101011101011101101101111101101110001111010010001001110000110010000001000' sage: s 3 sage: hex(t) '0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4a30d0f077e5f2cd6ff980291ee134ba0776b937113388f5d76df6e3d2270c81' sage: hex(root_of_unity) '0x9a0a650d44b28c17f3d708ad2fa8c4fbc7e6000d7c12dafa92fcc5673a3055276d535f79ff391dcdbcd998b7836647d3a72472b3da861ac810a7f9c7b7b63e2205' sage: delta 6561
1 parent 2afc27e commit e092afb

File tree

4 files changed

+7754
-17
lines changed

4 files changed

+7754
-17
lines changed

p521/src/arithmetic.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,35 @@
55
//! [NIST SP 800-186]: https://csrc.nist.gov/publications/detail/sp/800-186/final
66
77
pub mod field;
8+
pub mod scalar;
9+
10+
/// Convert an 18-element array of `u32` into a 9-element array of `u16`,
11+
/// assuming integer arrays are in little-endian order.
12+
#[cfg(target_pointer_width = "32")]
13+
const fn u32x18_to_u64x9(w: &[u32; 18]) -> [u64; 9] {
14+
let mut ret = [0u64; 9];
15+
let mut i = 0;
16+
17+
while i < 9 {
18+
ret[i] = (w[i * 2] as u64) | ((w[(i * 2) + 1] as u64) << 32);
19+
i += 1;
20+
}
21+
22+
ret
23+
}
24+
25+
/// Convert a 9-element array of `u64` into an 18-element array of `u32`,
26+
/// assuming integers are in little-endian order.
27+
#[cfg(target_pointer_width = "32")]
28+
const fn u64x9_to_u32x18(w: &[u64; 9]) -> [u32; 18] {
29+
let mut ret = [0u32; 18];
30+
let mut i = 0;
31+
32+
while i < 9 {
33+
ret[i * 2] = (w[i] & 0xFFFFFFFF) as u32;
34+
ret[(i * 2) + 1] = (w[i] >> 32) as u32;
35+
i += 1;
36+
}
37+
38+
ret
39+
}

p521/src/arithmetic/field.rs

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,15 @@ use core::{
3939
use elliptic_curve::{
4040
ff::{self, Field, PrimeField},
4141
generic_array::GenericArray,
42+
rand_core::RngCore,
4243
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeLess, CtOption},
4344
zeroize::DefaultIsZeroes,
44-
FieldBytesEncoding,
45+
Error, FieldBytesEncoding,
4546
};
4647

48+
#[cfg(target_pointer_width = "32")]
49+
use super::u32x18_to_u64x9;
50+
4751
/// Constant representing the modulus serialized as hex.
4852
/// p = 2^{521} − 1
4953
const MODULUS_HEX: &str = "00000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
@@ -73,10 +77,10 @@ impl FieldElement {
7377
/// Decode [`FieldElement`] from a big endian byte slice.
7478
pub fn from_slice(slice: &[u8]) -> elliptic_curve::Result<Self> {
7579
if slice.len() != Self::BYTES {
76-
return Err(elliptic_curve::Error);
80+
return Err(Error);
7781
}
7882

79-
Option::from(Self::from_bytes(GenericArray::from_slice(slice))).ok_or(elliptic_curve::Error)
83+
Option::from(Self::from_bytes(GenericArray::from_slice(slice))).ok_or(Error)
8084
}
8185

8286
/// Decode [`FieldElement`] from [`U576`].
@@ -107,19 +111,7 @@ impl FieldElement {
107111
/// Used incorrectly this can lead to invalid results!
108112
#[cfg(target_pointer_width = "32")]
109113
pub(crate) const fn from_uint_unchecked(w: U576) -> Self {
110-
let words = w.to_words();
111-
112-
Self([
113-
(words[0] as u64) | ((words[1] as u64) << 32),
114-
(words[2] as u64) | ((words[3] as u64) << 32),
115-
(words[4] as u64) | ((words[5] as u64) << 32),
116-
(words[6] as u64) | ((words[7] as u64) << 32),
117-
(words[8] as u64) | ((words[9] as u64) << 32),
118-
(words[10] as u64) | ((words[11] as u64) << 32),
119-
(words[12] as u64) | ((words[13] as u64) << 32),
120-
(words[14] as u64) | ((words[15] as u64) << 32),
121-
(words[16] as u64) | ((words[17] as u64) << 32),
122-
])
114+
Self(u32x18_to_u64x9(w.as_words()))
123115
}
124116

125117
/// Decode [`FieldElement`] from [`U576`].
@@ -326,7 +318,7 @@ impl Field for FieldElement {
326318
const ZERO: Self = Self::ZERO;
327319
const ONE: Self = Self::ONE;
328320

329-
fn random(mut rng: impl elliptic_curve::rand_core::RngCore) -> Self {
321+
fn random(mut rng: impl RngCore) -> Self {
330322
// NOTE: can't use ScalarPrimitive::random due to CryptoRng bound
331323
let mut bytes = <FieldBytes>::default();
332324

0 commit comments

Comments
 (0)