diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/prio/src/field | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/prio/src/field')
-rw-r--r-- | third_party/rust/prio/src/field/field255.rs | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/third_party/rust/prio/src/field/field255.rs b/third_party/rust/prio/src/field/field255.rs new file mode 100644 index 0000000000..fd06a6334a --- /dev/null +++ b/third_party/rust/prio/src/field/field255.rs @@ -0,0 +1,543 @@ +// Copyright (c) 2023 ISRG +// SPDX-License-Identifier: MPL-2.0 + +//! Finite field arithmetic for `GF(2^255 - 19)`. + +use crate::{ + codec::{CodecError, Decode, Encode}, + field::{FieldElement, FieldElementVisitor, FieldError}, +}; +use fiat_crypto::curve25519_64::{ + fiat_25519_add, fiat_25519_carry, fiat_25519_carry_mul, fiat_25519_from_bytes, + fiat_25519_loose_field_element, fiat_25519_opp, fiat_25519_relax, fiat_25519_selectznz, + fiat_25519_sub, fiat_25519_tight_field_element, fiat_25519_to_bytes, +}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + convert::TryFrom, + fmt::{self, Debug, Display, Formatter}, + io::{Cursor, Read}, + marker::PhantomData, + mem::size_of, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, +}; +use subtle::{ + Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess, +}; + +// `python3 -c "print(', '.join(hex(x) for x in (2**255-19).to_bytes(32, 'little')))"` +const MODULUS_LITTLE_ENDIAN: [u8; 32] = [ + 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, +]; + +/// `GF(2^255 - 19)`, a 255-bit field. +#[derive(Clone, Copy)] +#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))] +pub struct Field255(fiat_25519_tight_field_element); + +impl Field255 { + /// Attempts to instantiate a `Field255` from the first `Self::ENCODED_SIZE` bytes in the + /// provided slice. + /// + /// # Errors + /// + /// An error is returned if the provided slice is not long enough to encode a field element or + /// if the decoded value is greater than the field prime. + fn try_from_bytes(bytes: &[u8], mask_top_bit: bool) -> Result<Self, FieldError> { + if Self::ENCODED_SIZE > bytes.len() { + return Err(FieldError::ShortRead); + } + + let mut value = [0u8; Self::ENCODED_SIZE]; + value.copy_from_slice(&bytes[..Self::ENCODED_SIZE]); + + if mask_top_bit { + value[31] &= 0b0111_1111; + } + + // Walk through the bytes of the provided value from most significant to least, + // and identify whether the first byte that differs from the field's modulus is less than + // the corresponding byte or greater than the corresponding byte, in constant time. (Or + // whether the provided value is equal to the field modulus.) + let mut less_than_modulus = Choice::from(0u8); + let mut greater_than_modulus = Choice::from(0u8); + for (value_byte, modulus_byte) in value.iter().rev().zip(MODULUS_LITTLE_ENDIAN.iter().rev()) + { + less_than_modulus |= value_byte.ct_lt(modulus_byte) & !greater_than_modulus; + greater_than_modulus |= value_byte.ct_gt(modulus_byte) & !less_than_modulus; + } + + if bool::from(!less_than_modulus) { + return Err(FieldError::ModulusOverflow); + } + + let mut output = fiat_25519_tight_field_element([0; 5]); + fiat_25519_from_bytes(&mut output, &value); + + Ok(Field255(output)) + } +} + +impl ConstantTimeEq for Field255 { + fn ct_eq(&self, rhs: &Self) -> Choice { + // The internal representation used by fiat-crypto is not 1-1 with the field, so it is + // necessary to compare field elements via their canonical encodings. + + let mut self_encoded = [0; 32]; + fiat_25519_to_bytes(&mut self_encoded, &self.0); + let mut rhs_encoded = [0; 32]; + fiat_25519_to_bytes(&mut rhs_encoded, &rhs.0); + + self_encoded.ct_eq(&rhs_encoded) + } +} + +impl ConditionallySelectable for Field255 { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let mut output = [0; 5]; + fiat_25519_selectznz(&mut output, choice.unwrap_u8(), &(a.0).0, &(b.0).0); + Field255(fiat_25519_tight_field_element(output)) + } +} + +impl PartialEq for Field255 { + fn eq(&self, rhs: &Self) -> bool { + self.ct_eq(rhs).into() + } +} + +impl Eq for Field255 {} + +impl Add for Field255 { + type Output = Field255; + + fn add(self, rhs: Self) -> Field255 { + let mut loose_output = fiat_25519_loose_field_element([0; 5]); + fiat_25519_add(&mut loose_output, &self.0, &rhs.0); + let mut output = fiat_25519_tight_field_element([0; 5]); + fiat_25519_carry(&mut output, &loose_output); + Field255(output) + } +} + +impl AddAssign for Field255 { + fn add_assign(&mut self, rhs: Self) { + let mut loose_output = fiat_25519_loose_field_element([0; 5]); + fiat_25519_add(&mut loose_output, &self.0, &rhs.0); + fiat_25519_carry(&mut self.0, &loose_output); + } +} + +impl Sub for Field255 { + type Output = Field255; + + fn sub(self, rhs: Self) -> Field255 { + let mut loose_output = fiat_25519_loose_field_element([0; 5]); + fiat_25519_sub(&mut loose_output, &self.0, &rhs.0); + let mut output = fiat_25519_tight_field_element([0; 5]); + fiat_25519_carry(&mut output, &loose_output); + Field255(output) + } +} + +impl SubAssign for Field255 { + fn sub_assign(&mut self, rhs: Self) { + let mut loose_output = fiat_25519_loose_field_element([0; 5]); + fiat_25519_sub(&mut loose_output, &self.0, &rhs.0); + fiat_25519_carry(&mut self.0, &loose_output); + } +} + +impl Mul for Field255 { + type Output = Field255; + + fn mul(self, rhs: Self) -> Field255 { + let mut self_relaxed = fiat_25519_loose_field_element([0; 5]); + fiat_25519_relax(&mut self_relaxed, &self.0); + let mut rhs_relaxed = fiat_25519_loose_field_element([0; 5]); + fiat_25519_relax(&mut rhs_relaxed, &rhs.0); + let mut output = fiat_25519_tight_field_element([0; 5]); + fiat_25519_carry_mul(&mut output, &self_relaxed, &rhs_relaxed); + Field255(output) + } +} + +impl MulAssign for Field255 { + fn mul_assign(&mut self, rhs: Self) { + let mut self_relaxed = fiat_25519_loose_field_element([0; 5]); + fiat_25519_relax(&mut self_relaxed, &self.0); + let mut rhs_relaxed = fiat_25519_loose_field_element([0; 5]); + fiat_25519_relax(&mut rhs_relaxed, &rhs.0); + fiat_25519_carry_mul(&mut self.0, &self_relaxed, &rhs_relaxed); + } +} + +impl Div for Field255 { + type Output = Field255; + + fn div(self, _rhs: Self) -> Self::Output { + unimplemented!("Div is not implemented for Field255 because it's not needed yet") + } +} + +impl DivAssign for Field255 { + fn div_assign(&mut self, _rhs: Self) { + unimplemented!("DivAssign is not implemented for Field255 because it's not needed yet") + } +} + +impl Neg for Field255 { + type Output = Field255; + + fn neg(self) -> Field255 { + -&self + } +} + +impl<'a> Neg for &'a Field255 { + type Output = Field255; + + fn neg(self) -> Field255 { + let mut loose_output = fiat_25519_loose_field_element([0; 5]); + fiat_25519_opp(&mut loose_output, &self.0); + let mut output = fiat_25519_tight_field_element([0; 5]); + fiat_25519_carry(&mut output, &loose_output); + Field255(output) + } +} + +impl From<u64> for Field255 { + fn from(value: u64) -> Self { + let input_bytes = value.to_le_bytes(); + let mut field_bytes = [0u8; Self::ENCODED_SIZE]; + field_bytes[..input_bytes.len()].copy_from_slice(&input_bytes); + Self::try_from_bytes(&field_bytes, false).unwrap() + } +} + +impl<'a> TryFrom<&'a [u8]> for Field255 { + type Error = FieldError; + + fn try_from(bytes: &[u8]) -> Result<Self, FieldError> { + Self::try_from_bytes(bytes, false) + } +} + +impl From<Field255> for [u8; Field255::ENCODED_SIZE] { + fn from(element: Field255) -> Self { + let mut array = [0; Field255::ENCODED_SIZE]; + fiat_25519_to_bytes(&mut array, &element.0); + array + } +} + +impl From<Field255> for Vec<u8> { + fn from(elem: Field255) -> Vec<u8> { + <[u8; Field255::ENCODED_SIZE]>::from(elem).to_vec() + } +} + +impl Display for Field255 { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let encoded: [u8; Self::ENCODED_SIZE] = (*self).into(); + write!(f, "0x")?; + for byte in encoded { + write!(f, "{byte:02x}")?; + } + Ok(()) + } +} + +impl Debug for Field255 { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + <Self as Display>::fmt(self, f) + } +} + +impl Serialize for Field255 { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + let bytes: [u8; Self::ENCODED_SIZE] = (*self).into(); + serializer.serialize_bytes(&bytes) + } +} + +impl<'de> Deserialize<'de> for Field255 { + fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Field255, D::Error> { + deserializer.deserialize_bytes(FieldElementVisitor { + phantom: PhantomData, + }) + } +} + +impl Encode for Field255 { + fn encode(&self, bytes: &mut Vec<u8>) { + bytes.extend_from_slice(&<[u8; Self::ENCODED_SIZE]>::from(*self)); + } + + fn encoded_len(&self) -> Option<usize> { + Some(Self::ENCODED_SIZE) + } +} + +impl Decode for Field255 { + fn decode(bytes: &mut Cursor<&[u8]>) -> Result<Self, CodecError> { + let mut value = [0u8; Self::ENCODED_SIZE]; + bytes.read_exact(&mut value)?; + Field255::try_from_bytes(&value, false).map_err(|e| { + CodecError::Other(Box::new(e) as Box<dyn std::error::Error + 'static + Send + Sync>) + }) + } +} + +impl FieldElement for Field255 { + const ENCODED_SIZE: usize = 32; + + fn inv(&self) -> Self { + unimplemented!("Field255::inv() is not implemented because it's not needed yet") + } + + fn try_from_random(bytes: &[u8]) -> Result<Self, FieldError> { + Field255::try_from_bytes(bytes, true) + } + + fn zero() -> Self { + Field255(fiat_25519_tight_field_element([0, 0, 0, 0, 0])) + } + + fn one() -> Self { + Field255(fiat_25519_tight_field_element([1, 0, 0, 0, 0])) + } +} + +impl Default for Field255 { + fn default() -> Self { + Field255::zero() + } +} + +impl TryFrom<Field255> for u64 { + type Error = FieldError; + + fn try_from(elem: Field255) -> Result<u64, FieldError> { + const PREFIX_LEN: usize = size_of::<u64>(); + let mut le_bytes = [0; 32]; + + fiat_25519_to_bytes(&mut le_bytes, &elem.0); + if !bool::from(le_bytes[PREFIX_LEN..].ct_eq(&[0_u8; 32 - PREFIX_LEN])) { + return Err(FieldError::IntegerTryFrom); + } + + Ok(u64::from_le_bytes( + le_bytes[..PREFIX_LEN].try_into().unwrap(), + )) + } +} + +#[cfg(test)] +mod tests { + use super::{Field255, MODULUS_LITTLE_ENDIAN}; + use crate::{ + codec::Encode, + field::{ + test_utils::{field_element_test_common, TestFieldElementWithInteger}, + FieldElement, FieldError, + }, + }; + use assert_matches::assert_matches; + use fiat_crypto::curve25519_64::{ + fiat_25519_from_bytes, fiat_25519_tight_field_element, fiat_25519_to_bytes, + }; + use num_bigint::BigUint; + use once_cell::sync::Lazy; + use std::convert::{TryFrom, TryInto}; + + static MODULUS: Lazy<BigUint> = Lazy::new(|| BigUint::from_bytes_le(&MODULUS_LITTLE_ENDIAN)); + + impl From<BigUint> for Field255 { + fn from(value: BigUint) -> Self { + let le_bytes_vec = (value % &*MODULUS).to_bytes_le(); + + let mut le_bytes_array = [0u8; 32]; + le_bytes_array[..le_bytes_vec.len()].copy_from_slice(&le_bytes_vec); + + let mut output = fiat_25519_tight_field_element([0; 5]); + fiat_25519_from_bytes(&mut output, &le_bytes_array); + Field255(output) + } + } + + impl From<Field255> for BigUint { + fn from(value: Field255) -> Self { + let mut le_bytes = [0u8; 32]; + fiat_25519_to_bytes(&mut le_bytes, &value.0); + BigUint::from_bytes_le(&le_bytes) + } + } + + impl TestFieldElementWithInteger for Field255 { + type Integer = BigUint; + type IntegerTryFromError = <Self::Integer as TryFrom<usize>>::Error; + type TryIntoU64Error = <Self::Integer as TryInto<u64>>::Error; + + fn pow(&self, _exp: Self::Integer) -> Self { + unimplemented!("Field255::pow() is not implemented because it's not needed yet") + } + + fn modulus() -> Self::Integer { + MODULUS.clone() + } + } + + #[test] + fn check_modulus() { + let modulus = Field255::modulus(); + let element = Field255::from(modulus); + // Note that these two objects represent the same field element, they encode to the same + // canonical value (32 zero bytes), but they do not have the same internal representation. + assert_eq!(element, Field255::zero()); + } + + #[test] + fn check_identities() { + let zero_bytes: [u8; 32] = Field255::zero().into(); + assert_eq!(zero_bytes, [0; 32]); + let one_bytes: [u8; 32] = Field255::one().into(); + assert_eq!( + one_bytes, + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ] + ); + } + + #[test] + fn encode_endianness() { + let mut one_encoded = Vec::new(); + Field255::one().encode(&mut one_encoded); + assert_eq!( + one_encoded, + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ] + ); + } + + #[test] + fn test_field255() { + field_element_test_common::<Field255>(); + } + + #[test] + fn try_from_bytes() { + assert_matches!( + Field255::try_from_bytes( + &[ + 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + ], + false, + ), + Err(FieldError::ModulusOverflow) + ); + assert_matches!( + Field255::try_from_bytes( + &[ + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + ], + false, + ), + Ok(_) + ); + assert_matches!( + Field255::try_from_bytes( + &[ + 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + ], + true, + ), + Ok(element) => assert_eq!(element + Field255::one(), Field255::zero()) + ); + assert_matches!( + Field255::try_from_bytes( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + ], + false + ), + Err(FieldError::ModulusOverflow) + ); + assert_matches!( + Field255::try_from_bytes( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + ], + true + ), + Ok(element) => assert_eq!(element, Field255::zero()) + ); + } + + #[test] + fn u64_conversion() { + assert_eq!(Field255::from(0u64), Field255::zero()); + assert_eq!(Field255::from(1u64), Field255::one()); + + let max_bytes: [u8; 32] = Field255::from(u64::MAX).into(); + assert_eq!( + max_bytes, + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + ] + ); + + let want: u64 = 0xffffffffffffffff; + assert_eq!(u64::try_from(Field255::from(want)).unwrap(), want); + + let want: u64 = 0x7000000000000001; + assert_eq!(u64::try_from(Field255::from(want)).unwrap(), want); + + let want: u64 = 0x1234123412341234; + assert_eq!(u64::try_from(Field255::from(want)).unwrap(), want); + + assert!(u64::try_from(Field255::try_from_bytes(&[1; 32], false).unwrap()).is_err()); + assert!(u64::try_from(Field255::try_from_bytes(&[2; 32], false).unwrap()).is_err()); + } + + #[test] + fn formatting() { + assert_eq!( + format!("{}", Field255::zero()), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{}", Field255::one()), + "0x0100000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{}", -Field255::one()), + "0xecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f" + ); + assert_eq!( + format!("{:?}", Field255::zero()), + "0x0000000000000000000000000000000000000000000000000000000000000000" + ); + assert_eq!( + format!("{:?}", Field255::one()), + "0x0100000000000000000000000000000000000000000000000000000000000000" + ); + } +} |