// 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 ] = [
0 xed, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 x7f,
];
/// `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 = [0 u8; Self ::ENCODED_SIZE];
value.copy_from_slice(&bytes[..Self ::ENCODED_SIZE]);
if mask_top_bit {
value[31 ] &= 0 b0111_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(0 u8);
let mut greater_than_modulus = Choice::from(0 u8);
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.yle='color: green'>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 = [0 u8; 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>) -> Result<(), CodecError> {
bytes.extend_from_slice(&<[u8; Self ::ENCODED_SIZE]>::from(*self ));
Ok(())
}
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 = [0 u8; 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, Integer,
},
};
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 = [0 u8; 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 = [0 u8; 32 ];
fiat_25519_to_bytes(&mut le_bytes, &value.>0 );
BigUint::from_bytes_le(&le_bytes)
}
}
impl Integer for BigUint {
type TryFromUsizeError = <Self as TryFrom<usize>>::Error;
type TryIntoU64Error = <Self as TryInto<u64>>::Error;
fn zero() -> Self {
Self ::new(Vec::new())
}
fn one() -> Self {
Self ::new(Vec::from([1 ]))
}
}
impl TestFieldElementWithInteger for Field255 {
type TestInteger = BigUint;
type IntegerTryFromError = <Self ::TestInteger as TryFrom<usize>>::Error;
type TryIntoU64Error = <Self ::TestInteger as TryInto<u64>>::Error;
fn pow(&self , _exp: Self ::TestInteger) -> Self {
unimplemented!("Field255::pow() is not implemented because it's not needed yet" )
}
fn modulus() -> Self ::TestInteger {
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).unwrap();
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(
&[
0 xed, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 x7f,
],
false ,
),
Err(FieldError::ModulusOverflow)
);
assert_matches!(
Field255::try_from_bytes(
&[
0 xee, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xfe, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xfe,
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 x7f,
],
false ,
),
Ok(_)
);
assert_matches!(
Field255::try_from_bytes(
&[
0 xec, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff,
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 x7f,
],
true ,
),
Ok(element) => assert_eq!(element + Field255::one(), Field255::zero())
);
assert_matches!(
Field255::try_from_bytes(
&[
0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00,
0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00,
0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x80,
],
false
),
Err(FieldError::ModulusOverflow)
);
assert_matches!(
Field255::try_from_bytes(
&[
0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00,
0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00,
0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x80,
],
true
),
Ok(element) => assert_eq!(element, Field255::zero())
);
}
#[ test]
fn u64_conversion() {
assert_eq!(Field255::from(0 u64), Field255::zero());
assert_eq!(Field255::from(1 u64), Field255::one());
let max_bytes: [u8; 32 ] = Field255::from(u64::MAX).into();
assert_eq!(
max_bytes,
[
0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 xff, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00,
0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00,
0 x00, 0 x00, 0 x00, 0 x00
]
);
let want: u64 = 0 xffffffffffffffff;
assert_eq!(u64::try_from(Field255::from(want)).unwrap(), want);
let want: u64 = 0 x7000000000000001;
assert_eq!(u64::try_from(Field255::from(want)).unwrap(), want);
let want: u64 = 0 x1234123412341234;
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"
);
}
}
Messung V0.5 in Prozent C=85 H=94 G=89
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.35Bemerkung:
(vorverarbeitet am 2026-06-18)
¤
*Bot Zugriff