mirror of
https://github.com/saymrwulf/anza-cryptography-source.git
synced 2026-09-04 20:24:04 +00:00
[ed25519] fix point conversions (#46)
* Update short_weierstrass.rs * remove option for to_affine_le_bytes
This commit is contained in:
parent
53383206b8
commit
befbe09d36
2 changed files with 79 additions and 28 deletions
|
|
@ -59,15 +59,10 @@ fn le_bytes_to_u64x4(bytes: [u8; 32]) -> [u64; 4] {
|
|||
///
|
||||
/// The identity is encoded as (0, 0) to match SPPARK `affine_t` rules.
|
||||
pub fn sw_point_to_sppark_affine(point: &SwPoint) -> SpparkAffine {
|
||||
match point.to_affine_le_bytes() {
|
||||
Some((x, y)) => SpparkAffine {
|
||||
x: le_bytes_to_u64x4(x),
|
||||
y: le_bytes_to_u64x4(y),
|
||||
},
|
||||
None => SpparkAffine {
|
||||
x: [0u64; 4],
|
||||
y: [0u64; 4],
|
||||
},
|
||||
let (x, y) = point.to_affine_le_bytes();
|
||||
SpparkAffine {
|
||||
x: le_bytes_to_u64x4(x),
|
||||
y: le_bytes_to_u64x4(y),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,13 +76,8 @@ pub fn scalar_to_sppark_scalar(scalar: &Scalar) -> SpparkScalar {
|
|||
|
||||
/// Convert a short Weierstrass point into byte-serialized affine layout.
|
||||
pub fn sw_point_to_sppark_affine_bytes(point: &SwPoint) -> SpparkAffineBytes {
|
||||
match point.to_affine_le_bytes() {
|
||||
Some((x, y)) => SpparkAffineBytes { x, y },
|
||||
None => SpparkAffineBytes {
|
||||
x: [0u8; 32],
|
||||
y: [0u8; 32],
|
||||
},
|
||||
}
|
||||
let (x, y) = point.to_affine_le_bytes();
|
||||
SpparkAffineBytes { x, y }
|
||||
}
|
||||
|
||||
/// Convert a scalar into byte-serialized layout.
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ impl SwPoint {
|
|||
}
|
||||
|
||||
/// Convert an Edwards point into the short Weierstrass model.
|
||||
///
|
||||
/// The Edwards and short-Weierstrass models are related by a birational
|
||||
/// map, so there are exceptional points that do not round-trip through
|
||||
/// both directions. In particular, the non-identity Edwards point
|
||||
/// `(0, -1)` maps to a short-Weierstrass affine point with `y == 0`;
|
||||
/// [`Self::to_edwards`] rejects that exceptional affine point.
|
||||
pub fn from_edwards(point: &EdwardsPoint) -> Self {
|
||||
if point.is_identity() {
|
||||
return SwPoint::Identity;
|
||||
|
|
@ -54,10 +60,17 @@ impl SwPoint {
|
|||
}
|
||||
|
||||
/// Convert this point into an Edwards point, if defined.
|
||||
///
|
||||
/// Returns `None` for affine coordinates that are not on the short-Weierstrass
|
||||
/// curve and for exceptional affine points where the birational map is
|
||||
/// undefined.
|
||||
pub fn to_edwards(&self) -> Option<EdwardsPoint> {
|
||||
match self {
|
||||
SwPoint::Identity => Some(EdwardsPoint::identity()),
|
||||
SwPoint::Affine { x, y } => {
|
||||
if !affine_coordinates_on_curve(x, y) {
|
||||
return None;
|
||||
}
|
||||
if *y == FieldElement::ZERO {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -89,14 +102,18 @@ impl SwPoint {
|
|||
}
|
||||
|
||||
/// Return affine coordinates as little-endian byte arrays.
|
||||
pub fn to_affine_le_bytes(&self) -> Option<([u8; 32], [u8; 32])> {
|
||||
///
|
||||
/// The point at infinity is encoded as the reserved all-zero pair.
|
||||
pub fn to_affine_le_bytes(&self) -> ([u8; 32], [u8; 32]) {
|
||||
match self {
|
||||
SwPoint::Identity => None,
|
||||
SwPoint::Affine { x, y } => Some((x.to_bytes(), y.to_bytes())),
|
||||
SwPoint::Identity => ([0u8; 32], [0u8; 32]),
|
||||
SwPoint::Affine { x, y } => (x.to_bytes(), y.to_bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a point from affine little-endian byte arrays.
|
||||
///
|
||||
/// The all-zero pair is reserved as the point-at-infinity encoding.
|
||||
pub fn from_affine_le_bytes(x: [u8; 32], y: [u8; 32]) -> Option<Self> {
|
||||
if x == [0u8; 32] && y == [0u8; 32] {
|
||||
return Some(SwPoint::Identity);
|
||||
|
|
@ -142,17 +159,19 @@ impl SwPoint {
|
|||
pub fn is_on_curve(&self) -> bool {
|
||||
match self {
|
||||
SwPoint::Identity => true,
|
||||
SwPoint::Affine { x, y } => {
|
||||
let y2 = y.square();
|
||||
let x2 = x.square();
|
||||
let x3 = &x2 * x;
|
||||
let rhs = &x3 + &(&sw_a() * x);
|
||||
y2 == &rhs + &sw_b()
|
||||
}
|
||||
SwPoint::Affine { x, y } => affine_coordinates_on_curve(x, y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn affine_coordinates_on_curve(x: &FieldElement, y: &FieldElement) -> bool {
|
||||
let y2 = y.square();
|
||||
let x2 = x.square();
|
||||
let x3 = &x2 * x;
|
||||
let rhs = &x3 + &(&sw_a() * x);
|
||||
y2 == &rhs + &sw_b()
|
||||
}
|
||||
|
||||
fn double_affine(x: &FieldElement, y: &FieldElement) -> SwPoint {
|
||||
if *y == FieldElement::ZERO {
|
||||
return SwPoint::Identity;
|
||||
|
|
@ -219,6 +238,8 @@ mod tests {
|
|||
use super::SwPoint;
|
||||
use super::{sw_a, sw_b};
|
||||
use crate::constants;
|
||||
use crate::edwards::EdwardsPoint;
|
||||
use crate::field::FieldElement;
|
||||
use crate::scalar::Scalar;
|
||||
use rand::Rng;
|
||||
|
||||
|
|
@ -338,12 +359,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn sw_constants_match_expected() {
|
||||
let expected_a = crate::field::FieldElement::from_bytes(&[
|
||||
let expected_a = FieldElement::from_bytes(&[
|
||||
0x44, 0xa1, 0x14, 0x49, 0x98, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xaa, 0xaa, 0xaa, 0x2a,
|
||||
]);
|
||||
let expected_b = crate::field::FieldElement::from_bytes(&[
|
||||
let expected_b = FieldElement::from_bytes(&[
|
||||
0x64, 0xc8, 0x10, 0x77, 0x9c, 0x5e, 0x0b, 0x26, 0xb4, 0x97, 0xd0, 0x5e, 0x42, 0x7b,
|
||||
0x09, 0xed, 0x25, 0xb4, 0x97, 0xd0, 0x5e, 0x42, 0x7b, 0x09, 0xed, 0x25, 0xb4, 0x97,
|
||||
0xd0, 0x5e, 0x42, 0x7b,
|
||||
|
|
@ -352,4 +373,44 @@ mod tests {
|
|||
assert_eq!(sw_a(), expected_a);
|
||||
assert_eq!(sw_b(), expected_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sw_to_edwards_rejects_off_curve_affine_input() {
|
||||
let malformed = SwPoint::Affine {
|
||||
x: FieldElement::ZERO,
|
||||
y: FieldElement::ONE,
|
||||
};
|
||||
|
||||
assert!(!malformed.is_on_curve());
|
||||
assert!(malformed.to_edwards().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sw_to_edwards_rejects_order_two_exception() {
|
||||
let order_two = EdwardsPoint {
|
||||
X: FieldElement::ZERO,
|
||||
Y: FieldElement::MINUS_ONE,
|
||||
Z: FieldElement::ONE,
|
||||
T: FieldElement::ZERO,
|
||||
};
|
||||
let sw = SwPoint::from_edwards(&order_two);
|
||||
|
||||
assert!(sw.is_on_curve());
|
||||
let SwPoint::Affine { y, .. } = sw else {
|
||||
panic!("order-two point should map to affine");
|
||||
};
|
||||
assert_eq!(y, FieldElement::ZERO);
|
||||
assert!(sw.to_edwards().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sw_identity_affine_bytes_round_trip() {
|
||||
let encoded = SwPoint::Identity.to_affine_le_bytes();
|
||||
|
||||
assert_eq!(encoded, ([0u8; 32], [0u8; 32]));
|
||||
assert_eq!(
|
||||
SwPoint::from_affine_le_bytes(encoded.0, encoded.1),
|
||||
Some(SwPoint::Identity)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue