Skip to content

Commit edd7411

Browse files
authored
p521: 32-bit base field impl; Uint type (#1467)
Uses fiat-crypto v0.1.5 to synthesize a 32-bit "unsaturated Solinas" field implementation, and re-synthesizes the 64-bit backend using the same options for consistency. Notably the unsaturated representation needs some more limbs on 32-bit platforms (where on 64-bit there's ample headroom), so this also goes ahead and adds special case support for that, and also uses the `U544` type on 32-bit targets instead of `U576`, similar to how the `p224` crate has different sized `Uint` on 32-bit targets because otherwise they would have an extra unused limb.
1 parent 662081c commit edd7411

File tree

9 files changed

+3290
-1386
lines changed

9 files changed

+3290
-1386
lines changed

p521/src/arithmetic.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ impl PrimeCurveParams for NistP521 {
4848
/// b = 0x051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b 99b315f3
4949
/// b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd 3bb1bf07
5050
/// 3573df88 3d2c34f1 ef451fd4 6b503f00
51-
const EQUATION_B: FieldElement = FieldElement::from_hex_unchecked(
52-
"0000000000000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
51+
const EQUATION_B: FieldElement = FieldElement::from_hex(
52+
"0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
5353
);
5454

5555
/// Base point of P-521.
@@ -63,11 +63,11 @@ impl PrimeCurveParams for NistP521 {
6363
/// 353c7086 a272c240 88be9476 9fd16650
6464
/// ```
6565
const GENERATOR: (FieldElement, FieldElement) = (
66-
FieldElement::from_hex_unchecked(
67-
"00000000000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
66+
FieldElement::from_hex(
67+
"00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
6868
),
69-
FieldElement::from_hex_unchecked(
70-
"000000000000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650",
69+
FieldElement::from_hex(
70+
"011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650",
7171
),
7272
);
7373
}

p521/src/arithmetic/field.rs

Lines changed: 68 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,42 @@
1010
//! Apache License (Version 2.0), and the BSD 1-Clause License;
1111
//! users may pick which license to apply.
1212
13-
// TODO(tarcieri): 32-bit backend?
14-
#[path = "field/p521_64.rs"]
13+
#[cfg_attr(target_pointer_width = "32", path = "field/p521_32.rs")]
14+
#[cfg_attr(target_pointer_width = "64", path = "field/p521_64.rs")]
1515
#[allow(clippy::needless_lifetimes, clippy::unnecessary_cast)]
1616
#[allow(dead_code)] // TODO(tarcieri): remove this when we can use `const _` to silence warnings
17+
#[rustfmt::skip]
1718
mod field_impl;
1819
mod loose;
1920

2021
pub(crate) use self::loose::LooseFieldElement;
2122

2223
use self::field_impl::*;
23-
use crate::{FieldBytes, NistP521, U576};
24+
use crate::{FieldBytes, NistP521, Uint};
2425
use core::{
26+
cmp::Ordering,
2527
fmt::{self, Debug},
2628
iter::{Product, Sum},
2729
ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
2830
};
29-
use elliptic_curve::ops::Invert;
3031
use elliptic_curve::{
3132
Error, FieldBytesEncoding,
33+
bigint::Word,
3234
ff::{self, Field, PrimeField},
35+
ops::Invert,
3336
rand_core::TryRngCore,
3437
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeLess, CtOption},
3538
zeroize::DefaultIsZeroes,
3639
};
37-
use primefield::bigint::{Limb, Uint};
40+
use primefield::bigint::{self, Limb};
3841

42+
#[cfg(target_pointer_width = "32")]
43+
const MODULUS_HEX: &str = "000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
44+
#[cfg(target_pointer_width = "64")]
3945
const MODULUS_HEX: &str = "00000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
4046

4147
/// Field modulus: p = 2^{521} − 1
42-
pub(crate) const MODULUS: U576 = U576::from_be_hex(MODULUS_HEX);
48+
pub(crate) const MODULUS: Uint = Uint::from_be_hex(MODULUS_HEX);
4349

4450
/// Element of the secp521r1 base field used for curve coordinates.
4551
#[derive(Clone, Copy)]
@@ -52,9 +58,14 @@ impl FieldElement {
5258
/// Multiplicative identity.
5359
pub const ONE: Self = Self::from_u64(1);
5460

61+
#[cfg(target_pointer_width = "32")]
62+
const LIMBS: usize = 19;
63+
#[cfg(target_pointer_width = "64")]
64+
const LIMBS: usize = 9;
65+
5566
/// Create a [`FieldElement`] from a canonical big-endian representation.
5667
pub fn from_bytes(repr: &FieldBytes) -> CtOption<Self> {
57-
let uint = <U576 as FieldBytesEncoding<NistP521>>::decode_field_bytes(repr);
68+
let uint = <Uint as FieldBytesEncoding<NistP521>>::decode_field_bytes(repr);
5869
Self::from_uint(uint)
5970
}
6071

@@ -64,33 +75,56 @@ impl FieldElement {
6475
Self::from_bytes(&field_bytes).into_option().ok_or(Error)
6576
}
6677

67-
/// Decode [`FieldElement`] from [`U576`].
68-
pub fn from_uint(uint: U576) -> CtOption<Self> {
78+
/// Decode [`FieldElement`] from [`Uint`].
79+
pub fn from_uint(uint: Uint) -> CtOption<Self> {
6980
let is_some = uint.ct_lt(&MODULUS);
7081
CtOption::new(Self::from_uint_unchecked(uint), is_some)
7182
}
7283

7384
/// Parse a [`FieldElement`] from big endian hex-encoded bytes.
7485
///
75-
/// Does *not* perform a check that the field element does not overflow the order.
76-
///
7786
/// This method is primarily intended for defining internal constants.
78-
pub(crate) const fn from_hex_unchecked(hex: &str) -> Self {
79-
Self::from_uint_unchecked(U576::from_be_hex(hex))
87+
///
88+
/// # Panics
89+
/// - if the input in hex is not the correct length
90+
/// - if the given value when decoded from hex overflows the modulus
91+
pub(crate) const fn from_hex(hex: &str) -> Self {
92+
assert!(
93+
hex.len() == 521usize.div_ceil(8) * 2,
94+
"hex is the wrong length (expected 132 hex chars)"
95+
);
96+
97+
// Build a hex string of the expected size, regardless of the size of `Uint`
98+
let mut hex_bytes = [b'0'; { Uint::BITS as usize / 4 }];
99+
100+
let offset = hex_bytes.len() - hex.len();
101+
let mut i = 0;
102+
while i < hex.len() {
103+
hex_bytes[i + offset] = hex.as_bytes()[i];
104+
i += 1;
105+
}
106+
107+
let uint = match core::str::from_utf8(&hex_bytes) {
108+
Ok(padded_hex) => Uint::from_be_hex(padded_hex),
109+
Err(_) => panic!("invalid hex string"),
110+
};
111+
112+
assert!(matches!(uint.cmp_vartime(&MODULUS), Ordering::Less));
113+
Self::from_uint_unchecked(uint)
80114
}
81115

82116
/// Convert a `u64` into a [`FieldElement`].
83117
pub const fn from_u64(w: u64) -> Self {
84-
Self::from_uint_unchecked(U576::from_u64(w))
118+
Self::from_uint_unchecked(Uint::from_u64(w))
85119
}
86120

87-
/// Decode [`FieldElement`] from [`U576`].
121+
/// Decode [`FieldElement`] from [`Uint`].
88122
///
89123
/// Does *not* perform a check that the field element does not overflow the order.
90124
///
91125
/// Used incorrectly this can lead to invalid results!
92-
pub(crate) const fn from_uint_unchecked(w: U576) -> Self {
93-
// Converts the saturated representation used by `U576` into a 66-byte array with a
126+
pub(crate) const fn from_uint_unchecked(w: Uint) -> Self {
127+
// Converts the saturated representation used by `Uint` into a 66-byte array with a
94128
// little-endian byte ordering.
95129
// TODO(tarcieri): use `FieldBytesEncoding::encode_field_bytes` when `const impl` is stable
96130
let le_bytes_wide = w.to_le_bytes();
@@ -106,7 +140,7 @@ impl FieldElement {
106140

107141
// Decode the little endian serialization into the unsaturated big integer form used by
108142
// the fiat-crypto synthesized code.
109-
let mut out = fiat_p521_tight_field_element([0; 9]);
143+
let mut out = fiat_p521_tight_field_element([0; Self::LIMBS]);
110144
fiat_p521_from_bytes(&mut out, &le_bytes);
111145
Self(out)
112146
}
@@ -149,7 +183,7 @@ impl FieldElement {
149183
/// Add elements.
150184
#[inline]
151185
pub const fn add_loose(&self, rhs: &Self) -> LooseFieldElement {
152-
let mut out = fiat_p521_loose_field_element([0; 9]);
186+
let mut out = fiat_p521_loose_field_element([0; Self::LIMBS]);
153187
fiat_p521_add(&mut out, &self.0, &rhs.0);
154188
LooseFieldElement(out)
155189
}
@@ -164,39 +198,39 @@ impl FieldElement {
164198
/// Subtract elements, returning a loose field element.
165199
#[inline]
166200
pub const fn sub_loose(&self, rhs: &Self) -> LooseFieldElement {
167-
let mut out = fiat_p521_loose_field_element([0; 9]);
201+
let mut out = fiat_p521_loose_field_element([0; Self::LIMBS]);
168202
fiat_p521_sub(&mut out, &self.0, &rhs.0);
169203
LooseFieldElement(out)
170204
}
171205

172206
/// Negate element, returning a loose field element.
173207
#[inline]
174208
pub const fn neg_loose(&self) -> LooseFieldElement {
175-
let mut out = fiat_p521_loose_field_element([0; 9]);
209+
let mut out = fiat_p521_loose_field_element([0; Self::LIMBS]);
176210
fiat_p521_opp(&mut out, &self.0);
177211
LooseFieldElement(out)
178212
}
179213

180214
/// Add two field elements.
181215
#[inline]
182216
pub const fn add(&self, rhs: &Self) -> Self {
183-
let mut out = fiat_p521_tight_field_element([0; 9]);
217+
let mut out = fiat_p521_tight_field_element([0; Self::LIMBS]);
184218
fiat_p521_carry_add(&mut out, &self.0, &rhs.0);
185219
Self(out)
186220
}
187221

188222
/// Subtract field elements.
189223
#[inline]
190224
pub const fn sub(&self, rhs: &Self) -> Self {
191-
let mut out = fiat_p521_tight_field_element([0; 9]);
225+
let mut out = fiat_p521_tight_field_element([0; Self::LIMBS]);
192226
fiat_p521_carry_sub(&mut out, &self.0, &rhs.0);
193227
Self(out)
194228
}
195229

196230
/// Negate element.
197231
#[inline]
198232
pub const fn neg(&self) -> Self {
199-
let mut out = fiat_p521_tight_field_element([0; 9]);
233+
let mut out = fiat_p521_tight_field_element([0; Self::LIMBS]);
200234
fiat_p521_carry_opp(&mut out, &self.0);
201235
Self(out)
202236
}
@@ -236,7 +270,7 @@ impl FieldElement {
236270
/// **This operation is variable time with respect to the exponent `exp`.**
237271
///
238272
/// If the exponent is fixed, this operation is constant time.
239-
pub const fn pow_vartime<const RHS_LIMBS: usize>(&self, exp: &Uint<RHS_LIMBS>) -> Self {
273+
pub const fn pow_vartime<const RHS_LIMBS: usize>(&self, exp: &bigint::Uint<RHS_LIMBS>) -> Self {
240274
let mut res = Self::ONE;
241275
let mut i = RHS_LIMBS;
242276

@@ -329,7 +363,7 @@ impl FieldElement {
329363
/// Relax a tight field element into a loose one.
330364
#[inline]
331365
pub const fn relax(&self) -> LooseFieldElement {
332-
let mut out = fiat_p521_loose_field_element([0; 9]);
366+
let mut out = fiat_p521_loose_field_element([0; Self::LIMBS]);
333367
fiat_p521_relax(&mut out, &self.0);
334368
LooseFieldElement(out)
335369
}
@@ -389,32 +423,25 @@ impl PartialEq for FieldElement {
389423

390424
impl From<u32> for FieldElement {
391425
fn from(n: u32) -> FieldElement {
392-
Self::from_uint_unchecked(U576::from(n))
426+
Self::from_uint_unchecked(Uint::from(n))
393427
}
394428
}
395429

396430
impl From<u64> for FieldElement {
397431
fn from(n: u64) -> FieldElement {
398-
Self::from_uint_unchecked(U576::from(n))
432+
Self::from_uint_unchecked(Uint::from(n))
399433
}
400434
}
401435

402436
impl From<u128> for FieldElement {
403437
fn from(n: u128) -> FieldElement {
404-
Self::from_uint_unchecked(U576::from(n))
438+
Self::from_uint_unchecked(Uint::from(n))
405439
}
406440
}
407441

408442
impl ConditionallySelectable for FieldElement {
409443
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
410-
let mut out = [0; 9];
411-
let a = &a.0;
412-
let b = &b.0;
413-
414-
for i in 0..out.len() {
415-
out[i] = u64::conditional_select(&a[i], &b[i], choice);
416-
}
417-
444+
let out = <[Word; Self::LIMBS]>::conditional_select(&a.0.0, &b.0.0, choice);
418445
Self(fiat_p521_tight_field_element(out))
419446
}
420447
}
@@ -479,8 +506,8 @@ impl PrimeField for FieldElement {
479506
const TWO_INV: Self = Self::from_u64(2).invert_unchecked();
480507
const MULTIPLICATIVE_GENERATOR: Self = Self::from_u64(3);
481508
const S: u32 = 1;
482-
const ROOT_OF_UNITY: Self = Self::from_hex_unchecked(
483-
"00000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
509+
const ROOT_OF_UNITY: Self = Self::from_hex(
510+
"01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
484511
);
485512
const ROOT_OF_UNITY_INV: Self = Self::ROOT_OF_UNITY.invert_unchecked();
486513
const DELTA: Self = Self::from_u64(9);
@@ -675,10 +702,10 @@ impl Invert for FieldElement {
675702

676703
#[cfg(test)]
677704
mod tests {
678-
use super::{FieldElement, U576};
705+
use super::{FieldElement, Uint};
679706
use hex_literal::hex;
680707

681-
primefield::test_primefield!(FieldElement, U576);
708+
primefield::test_primefield!(FieldElement, Uint);
682709

683710
/// Regression test for RustCrypto/elliptic-curves#965
684711
#[test]

p521/src/arithmetic/field/loose.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,31 @@ use core::ops::Mul;
66
pub struct LooseFieldElement(pub(super) fiat_p521_loose_field_element);
77

88
impl LooseFieldElement {
9+
#[cfg(target_pointer_width = "32")]
10+
const LIMBS: usize = 19;
11+
#[cfg(target_pointer_width = "64")]
12+
const LIMBS: usize = 9;
13+
914
/// Reduce field element.
1015
#[inline]
1116
pub const fn carry(&self) -> FieldElement {
12-
let mut out = fiat_p521_tight_field_element([0; 9]);
17+
let mut out = fiat_p521_tight_field_element([0; Self::LIMBS]);
1318
fiat_p521_carry(&mut out, &self.0);
1419
FieldElement(out)
1520
}
1621

1722
/// Multiplies two field elements and reduces the result.
1823
#[inline]
1924
pub const fn multiply(&self, rhs: &Self) -> FieldElement {
20-
let mut out = fiat_p521_tight_field_element([0; 9]);
25+
let mut out = fiat_p521_tight_field_element([0; Self::LIMBS]);
2126
fiat_p521_carry_mul(&mut out, &self.0, &rhs.0);
2227
FieldElement(out)
2328
}
2429

2530
/// Squares a field element and reduces the result.
2631
#[inline]
2732
pub const fn square(&self) -> FieldElement {
28-
let mut out = fiat_p521_tight_field_element([0; 9]);
33+
let mut out = fiat_p521_tight_field_element([0; Self::LIMBS]);
2934
fiat_p521_carry_square(&mut out, &self.0);
3035
FieldElement(out)
3136
}

0 commit comments

Comments
 (0)