|
| 1 | +//! Field elements which use an internal Montgomery form representation, implemented using |
| 2 | +//! `crypto-bigint`'s [`MontyForm`]. |
| 3 | +
|
| 4 | +use crate::ByteOrder; |
| 5 | +use bigint::{ |
| 6 | + ArrayEncoding, ByteArray, Uint, |
| 7 | + hybrid_array::{Array, ArraySize, typenum::Unsigned}, |
| 8 | + modular::{ConstMontyForm as MontyForm, ConstMontyParams}, |
| 9 | +}; |
| 10 | +use subtle::{ConstantTimeLess, CtOption}; |
| 11 | + |
| 12 | +/// Creates a ZST representing the Montgomery parameters for a given field modulus. |
| 13 | +/// |
| 14 | +/// Accepts the following parameters: |
| 15 | +/// |
| 16 | +/// - name of the ZST representing the field modulus |
| 17 | +/// - hex serialization of the modulus |
| 18 | +/// - `crypto-bigint` unsigned integer type (e.g. U256) |
| 19 | +/// - number of bytes in an encoded field element |
| 20 | +/// - byte order to use when encoding/decoding field elements |
| 21 | +/// - documentation string for the field modulus type |
| 22 | +/// |
| 23 | +/// ``` |
| 24 | +/// use primefield::{ByteOrder, bigint::U256, consts::U32}; |
| 25 | +/// |
| 26 | +/// primefield::monty_field_params!( |
| 27 | +/// FieldParams, |
| 28 | +/// "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", |
| 29 | +/// U256, |
| 30 | +/// U32, |
| 31 | +/// ByteOrder::BigEndian, |
| 32 | +/// "P-256 field modulus" |
| 33 | +/// ); |
| 34 | +/// ``` |
| 35 | +#[macro_export] |
| 36 | +macro_rules! monty_field_params { |
| 37 | + ($name:ident, $modulus_hex:expr, $uint_type:ty, $byte_size:ty, $byte_order:expr, $doc:expr) => { |
| 38 | + $crate::bigint::const_monty_params!($name, $uint_type, $modulus_hex, $doc); |
| 39 | + |
| 40 | + impl $crate::MontyFieldParams<{ <$uint_type>::LIMBS }> for $name { |
| 41 | + type ByteSize = $byte_size; |
| 42 | + const BYTE_ORDER: $crate::ByteOrder = $byte_order; |
| 43 | + } |
| 44 | + }; |
| 45 | +} |
| 46 | + |
| 47 | +/// Extension trait for defining additional field parameters beyond the ones provided by |
| 48 | +/// [`ConstMontyParams`]. |
| 49 | +pub trait MontyFieldParams<const LIMBS: usize>: ConstMontyParams<LIMBS> { |
| 50 | + /// Size of a field element when serialized as bytes. |
| 51 | + type ByteSize: ArraySize; |
| 52 | + |
| 53 | + /// Byte order to use when serializing a field element as byte. |
| 54 | + const BYTE_ORDER: ByteOrder; |
| 55 | +} |
| 56 | + |
| 57 | +/// Field element type which uses an internal Montgomery form representation. |
| 58 | +#[derive(Clone, Copy)] |
| 59 | +pub struct MontyFieldElement<MOD: MontyFieldParams<LIMBS>, const LIMBS: usize>( |
| 60 | + MontyForm<MOD, LIMBS>, |
| 61 | +); |
| 62 | + |
| 63 | +impl<MOD: MontyFieldParams<LIMBS>, const LIMBS: usize> MontyFieldElement<MOD, LIMBS> { |
| 64 | + /// Zero element (additive identity). |
| 65 | + pub const ZERO: Self = Self(MontyForm::ZERO); |
| 66 | + |
| 67 | + /// Multiplicative identity. |
| 68 | + pub const ONE: Self = Self(MontyForm::ONE); |
| 69 | + |
| 70 | + /// Number of limbs used by the internal integer representation. |
| 71 | + pub const LIMBS: usize = LIMBS; |
| 72 | + |
| 73 | + /// Decode field element from a canonical bytestring representation. |
| 74 | + #[inline] |
| 75 | + pub fn from_bytes(repr: &Array<u8, MOD::ByteSize>) -> CtOption<Self> |
| 76 | + where |
| 77 | + Uint<LIMBS>: ArrayEncoding, |
| 78 | + { |
| 79 | + debug_assert!(repr.len() <= MOD::ByteSize::USIZE); |
| 80 | + let mut byte_array = ByteArray::<Uint<LIMBS>>::default(); |
| 81 | + let offset = MOD::ByteSize::USIZE.saturating_sub(repr.len()); |
| 82 | + |
| 83 | + let uint = match MOD::BYTE_ORDER { |
| 84 | + ByteOrder::BigEndian => { |
| 85 | + byte_array[offset..].copy_from_slice(repr); |
| 86 | + Uint::from_be_byte_array(byte_array) |
| 87 | + } |
| 88 | + ByteOrder::LittleEndian => { |
| 89 | + byte_array[..offset].copy_from_slice(repr); |
| 90 | + Uint::from_le_byte_array(byte_array) |
| 91 | + } |
| 92 | + }; |
| 93 | + |
| 94 | + Self::from_uint(&uint) |
| 95 | + } |
| 96 | + |
| 97 | + /// Decode field element from a canonical byte slice. |
| 98 | + /// |
| 99 | + /// Slice is expected to be zero padded to the expected byte size. |
| 100 | + #[inline] |
| 101 | + pub fn from_slice(slice: &[u8]) -> Option<Self> |
| 102 | + where |
| 103 | + Uint<LIMBS>: ArrayEncoding, |
| 104 | + { |
| 105 | + let array = Array::try_from(slice).ok()?; |
| 106 | + Self::from_bytes(&array).into() |
| 107 | + } |
| 108 | + |
| 109 | + /// Decode a field element from hex-encoded bytes. |
| 110 | + /// |
| 111 | + /// Does *not* perform a check that the field element does not overflow the order. |
| 112 | + /// |
| 113 | + /// This method is primarily intended for defining internal constants. |
| 114 | + pub const fn from_hex(hex: &str) -> Self { |
| 115 | + let uint = match MOD::BYTE_ORDER { |
| 116 | + ByteOrder::BigEndian => Uint::from_be_hex(hex), |
| 117 | + ByteOrder::LittleEndian => Uint::from_le_hex(hex), |
| 118 | + }; |
| 119 | + |
| 120 | + // TODO(tarcieri): ensure value does not overflow the modulus (RustCrypto/crypto-bigint#881) |
| 121 | + // if uint.lt(MOD::PARAMS.modulus().as_ref()).is_false_vartime() { |
| 122 | + // panic!("hex encoded field element overflows modulus"); |
| 123 | + // } |
| 124 | + |
| 125 | + Self::from_uint_unchecked(&uint) |
| 126 | + } |
| 127 | + |
| 128 | + /// Convert [`Uint`] into [`MontyFieldElement`], first converting it into Montgomery form: |
| 129 | + /// |
| 130 | + /// ```text |
| 131 | + /// w * R^2 * R^-1 mod p = wR mod p |
| 132 | + /// ``` |
| 133 | + /// |
| 134 | + /// Does *NOT* ensure that the input value is within range of the modulus! |
| 135 | + #[inline] |
| 136 | + pub const fn from_uint_unchecked(uint: &Uint<LIMBS>) -> Self { |
| 137 | + Self(MontyForm::new(uint)) |
| 138 | + } |
| 139 | + |
| 140 | + /// Convert [`Uint`] into [`MontyFieldElement`], first converting it into Montgomery form: |
| 141 | + /// |
| 142 | + /// ```text |
| 143 | + /// w * R^2 * R^-1 mod p = wR mod p |
| 144 | + /// ``` |
| 145 | + #[inline] |
| 146 | + pub fn from_uint(uint: &Uint<LIMBS>) -> CtOption<Self> { |
| 147 | + let is_some = uint.ct_lt(MOD::PARAMS.modulus()); |
| 148 | + CtOption::new(Self::from_uint_unchecked(uint), is_some) |
| 149 | + } |
| 150 | + |
| 151 | + /// Convert a `u64` into a [`MontyFieldElement`]. |
| 152 | + /// |
| 153 | + /// Does *NOT* ensure that the modulus is greater than 64-bits! |
| 154 | + #[inline] |
| 155 | + pub const fn from_u64(w: u64) -> Self { |
| 156 | + if MOD::PARAMS.modulus().as_ref().bits() <= 64 { |
| 157 | + panic!("modulus is too small to ensure all u64s are in range"); |
| 158 | + } |
| 159 | + |
| 160 | + Self::from_uint_unchecked(&Uint::from_u64(w)) |
| 161 | + } |
| 162 | + |
| 163 | + /// Returns the bytestring encoding of this field element. |
| 164 | + #[inline] |
| 165 | + pub fn to_bytes(self) -> Array<u8, MOD::ByteSize> |
| 166 | + where |
| 167 | + Uint<LIMBS>: ArrayEncoding, |
| 168 | + { |
| 169 | + let mut repr = Array::<u8, MOD::ByteSize>::default(); |
| 170 | + debug_assert!(repr.len() <= MOD::ByteSize::USIZE); |
| 171 | + |
| 172 | + let offset = MOD::ByteSize::USIZE.saturating_sub(repr.len()); |
| 173 | + |
| 174 | + match MOD::BYTE_ORDER { |
| 175 | + ByteOrder::BigEndian => { |
| 176 | + let padded = self.0.retrieve().to_be_byte_array(); |
| 177 | + repr.copy_from_slice(&padded[offset..]); |
| 178 | + } |
| 179 | + ByteOrder::LittleEndian => { |
| 180 | + let padded = self.0.retrieve().to_le_byte_array(); |
| 181 | + repr.copy_from_slice(&padded[..offset]); |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + repr |
| 186 | + } |
| 187 | +} |
0 commit comments