curve: rename FieldElement*::as_bytes => ::to_bytes (#767)

* curve: rename `FieldElement*::as_bytes` => `::to_bytes`

Methods named `as_*` should perform a zero-cost  borrowing conversion:

https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv

Methods named `to_*` can perform an expensive owned conversion.

Since the `FieldElement*` types are technically part of the public API
(but feature gated), this also preserves the old names with a
deprecation. We can remove them in the next breaking release.

The same change was also made to the backend `Scalar*` types, however
these types are not a part of the public API.
This commit is contained in:
Tony Arcieri 2025-06-06 15:10:29 -06:00 committed by GitHub
parent ad4a37df53
commit cf7b099585
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 50 additions and 29 deletions

View file

@ -203,10 +203,7 @@ fn process_mod(
};
let feature = feature.value();
if !spec_features
.iter()
.any(|enabled_feature| feature == *enabled_feature)
{
if !spec_features.contains(&feature) {
*item = syn::Item::Verbatim(Default::default());
continue 'next_item;
}

View file

@ -239,9 +239,15 @@ impl FieldElement2625 {
FieldElement2625(output)
}
/// Renamed to `to_bytes`.
#[deprecated(since = "4.1.4", note = "use `to_bytes` instead")]
pub fn as_bytes(&self) -> [u8; 32] {
self.to_bytes()
}
/// Serialize this `FieldElement51` to a 32-byte array. The
/// encoding is canonical.
pub fn as_bytes(&self) -> [u8; 32] {
pub fn to_bytes(self) -> [u8; 32] {
let mut bytes = [0u8; 32];
fiat_25519_to_bytes(&mut bytes, &self.0);
bytes

View file

@ -216,9 +216,15 @@ impl FieldElement51 {
FieldElement51(output)
}
/// Renamed to `to_bytes`.
#[deprecated(since = "4.1.4", note = "use `to_bytes` instead")]
pub fn as_bytes(&self) -> [u8; 32] {
self.to_bytes()
}
/// Serialize this `FieldElement51` to a 32-byte array. The
/// encoding is canonical.
pub fn as_bytes(&self) -> [u8; 32] {
pub fn to_bytes(self) -> [u8; 32] {
let mut bytes = [0u8; 32];
fiat_25519_to_bytes(&mut bytes, &self.0);
bytes

View file

@ -428,10 +428,16 @@ impl FieldElement2625 {
FieldElement2625::reduce(h)
}
/// Renamed to `to_bytes`.
#[deprecated(since = "4.1.4", note = "use `to_bytes` instead")]
pub fn as_bytes(&self) -> [u8; 32] {
self.to_bytes()
}
/// Serialize this `FieldElement51` to a 32-byte array. The
/// encoding is canonical.
#[allow(clippy::identity_op)]
pub fn as_bytes(&self) -> [u8; 32] {
pub fn to_bytes(self) -> [u8; 32] {
let inp = &self.0;
// Reduce the value represented by `in` to the range [0,2*p)
let mut h: [u32; 10] = FieldElement2625::reduce([

View file

@ -129,7 +129,7 @@ impl Scalar29 {
/// Pack the limbs of this `Scalar29` into 32 bytes.
#[rustfmt::skip] // keep alignment of s[*] calculations
#[allow(clippy::identity_op)]
pub fn as_bytes(&self) -> [u8; 32] {
pub fn to_bytes(self) -> [u8; 32] {
let mut s = [0u8; 32];
s[ 0] = (self.0[0] >> 0) as u8;

View file

@ -362,10 +362,16 @@ impl FieldElement51 {
])
}
/// Renamed to `to_bytes`.
#[deprecated(since = "4.1.4", note = "use `to_bytes` instead")]
pub fn as_bytes(&self) -> [u8; 32] {
self.to_bytes()
}
/// Serialize this `FieldElement51` to a 32-byte array. The
/// encoding is canonical.
#[rustfmt::skip] // keep alignment of s[*] calculations
pub fn as_bytes(&self) -> [u8; 32] {
pub fn to_bytes(self) -> [u8; 32] {
// Let h = limbs[0] + limbs[1]*2^51 + ... + limbs[4]*2^204.
//
// Write h = pq + r with 0 <= r < p.

View file

@ -118,7 +118,7 @@ impl Scalar52 {
/// Pack the limbs of this `Scalar52` into 32 bytes
#[rustfmt::skip] // keep alignment of s[*] calculations
#[allow(clippy::identity_op)]
pub fn as_bytes(&self) -> [u8; 32] {
pub fn to_bytes(self) -> [u8; 32] {
let mut s = [0u8; 32];
s[ 0] = (self.0[ 0] >> 0) as u8;

View file

@ -560,7 +560,7 @@ impl EdwardsPoint {
let U = &self.Z + &self.Y;
let W = &self.Z - &self.Y;
let u = &U * &W.invert();
MontgomeryPoint(u.as_bytes())
MontgomeryPoint(u.to_bytes())
}
/// Converts a large batch of points to Edwards at once. This has the same
@ -579,7 +579,7 @@ impl EdwardsPoint {
let mut ret = Vec::with_capacity(eds.len());
for (ed, d) in eds.iter().zip(denominators.iter()) {
let u = &(&ed.Z + &ed.Y) * d;
ret.push(MontgomeryPoint(u.as_bytes()));
ret.push(MontgomeryPoint(u.to_bytes()));
}
ret
@ -614,7 +614,7 @@ impl EdwardsPoint {
/// Compress affine Edwards coordinates into `CompressedEdwardsY` format.
#[inline]
fn compress_affine(x: FieldElement, y: FieldElement) -> CompressedEdwardsY {
let mut s = y.as_bytes();
let mut s = y.to_bytes();
s[31] ^= x.is_negative().unwrap_u8() << 7;
CompressedEdwardsY(s)
}

View file

@ -86,7 +86,7 @@ impl ConstantTimeEq for FieldElement {
/// internal representation is not canonical, the field elements
/// are normalized to wire format before comparison.
fn ct_eq(&self, other: &FieldElement) -> Choice {
self.as_bytes().ct_eq(&other.as_bytes())
self.to_bytes().ct_eq(&other.to_bytes())
}
}
@ -99,7 +99,7 @@ impl FieldElement {
///
/// If negative, return `Choice(1)`. Otherwise, return `Choice(0)`.
pub(crate) fn is_negative(&self) -> Choice {
let bytes = self.as_bytes();
let bytes = self.to_bytes();
(bytes[0] & 1).into()
}
@ -110,7 +110,7 @@ impl FieldElement {
/// If zero, return `Choice(1)`. Otherwise, return `Choice(0)`.
pub(crate) fn is_zero(&self) -> Choice {
let zero = [0u8; 32];
let bytes = self.as_bytes();
let bytes = self.to_bytes();
bytes.ct_eq(&zero)
}
@ -480,7 +480,7 @@ mod test {
// Decode to a field element
let one = FieldElement::from_bytes(&one_encoded_wrongly_bytes);
// .. then check that the encoding is correct
let one_bytes = one.as_bytes();
let one_bytes = one.to_bytes();
assert_eq!(one_bytes[0], 1);
for byte in &one_bytes[1..] {
assert_eq!(*byte, 0);

View file

@ -104,7 +104,7 @@ impl Hash for MontgomeryPoint {
fn hash<H: Hasher>(&self, state: &mut H) {
// Do a round trip through a `FieldElement`. `as_bytes` is guaranteed to give a canonical
// 32-byte encoding
let canonical_bytes = FieldElement::from_bytes(&self.0).as_bytes();
let canonical_bytes = FieldElement::from_bytes(&self.0).to_bytes();
canonical_bytes.hash(state);
}
}
@ -245,7 +245,7 @@ impl MontgomeryPoint {
let y = &(&u - &one) * &(&u + &one).invert();
let mut y_bytes = y.as_bytes();
let mut y_bytes = y.to_bytes();
y_bytes[31] ^= sign << 7;
CompressedEdwardsY(y_bytes).decompress()
@ -278,7 +278,7 @@ pub(crate) fn elligator_encode(r_0: &FieldElement) -> MontgomeryPoint {
let mut u = &d + &Atemp; /* d, or d+A if nonsquare */
u.conditional_negate(!eps_is_sq); /* d, or -d-A if nonsquare */
MontgomeryPoint(u.as_bytes())
MontgomeryPoint(u.to_bytes())
}
/// A `ProjectivePoint` holds a point on the projective line
@ -327,7 +327,7 @@ impl ProjectivePoint {
/// * \\( 0 \\) if \\( W \eq 0 \\);
pub fn as_affine(&self) -> MontgomeryPoint {
let u = &self.U * &self.W.invert();
MontgomeryPoint(u.as_bytes())
MontgomeryPoint(u.to_bytes())
}
}
@ -498,14 +498,14 @@ mod test {
let one = FieldElement::ONE;
// u = 2 corresponds to a point on the twist.
let two = MontgomeryPoint((&one + &one).as_bytes());
let two = MontgomeryPoint((&one + &one).to_bytes());
assert!(two.to_edwards(0).is_none());
// u = -1 corresponds to a point on the twist, but should be
// checked explicitly because it's an exceptional point for the
// birational map. For instance, libsignal will accept it.
let minus_one = MontgomeryPoint((-&one).as_bytes());
let minus_one = MontgomeryPoint((-&one).to_bytes());
assert!(minus_one.to_edwards(0).is_none());
}

View file

@ -285,7 +285,7 @@ mod decompress {
// original input, since our encoding routine is canonical.
let s = FieldElement::from_bytes(repr.as_bytes());
let s_bytes_check = s.as_bytes();
let s_bytes_check = s.to_bytes();
let s_encoding_is_canonical = s_bytes_check[..].ct_eq(repr.as_bytes());
let s_is_negative = s.is_negative();
@ -518,7 +518,7 @@ impl RistrettoPoint {
let s_is_negative = s.is_negative();
s.conditional_negate(s_is_negative);
CompressedRistretto(s.as_bytes())
CompressedRistretto(s.to_bytes())
}
/// Double-and-compress a batch of points. The Ristretto encoding
@ -630,7 +630,7 @@ impl RistrettoPoint {
let s_is_negative = s.is_negative();
s.conditional_negate(s_is_negative);
CompressedRistretto(s.as_bytes())
CompressedRistretto(s.to_bytes())
})
.collect()
}
@ -1361,7 +1361,7 @@ mod test {
#[test]
fn decompress_negative_s_fails() {
// constants::d is neg, so decompression should fail as |d| != d.
let bad_compressed = CompressedRistretto(constants::EDWARDS_D.as_bytes());
let bad_compressed = CompressedRistretto(constants::EDWARDS_D.to_bytes());
assert!(bad_compressed.decompress().is_none());
}

View file

@ -1140,7 +1140,7 @@ impl UnpackedScalar {
/// Pack the limbs of this `UnpackedScalar` into a `Scalar`.
fn pack(&self) -> Scalar {
Scalar {
bytes: self.as_bytes(),
bytes: self.to_bytes(),
}
}
@ -1731,7 +1731,7 @@ pub(crate) mod test {
#[test]
fn to_bytes_from_bytes_roundtrips() {
let unpacked = X.unpack();
let bytes = unpacked.as_bytes();
let bytes = unpacked.to_bytes();
let should_be_unpacked = UnpackedScalar::from_bytes(&bytes);
assert_eq!(should_be_unpacked.0, unpacked.0);