authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-02-23 23:04:26+01:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-02-23 13:04:58+01:00
loge8ca9229c8aa6bd2d8abb1f91a8662eed2e68fd5
tree4706f66e7f8116985df342c2ac5226b6a5f71bd5
parent27ef6e9a9b21ff7ca4e349af4384a3a23a1e3cde

Expose the elligator map for Curve25519

This is the same as for Edwards25519 without the y coordinate, since it returns Montgomery coordinates, but it can be confusing to call the Edwards25519 function while working on the Curve25519 representation. New protocols such as CPACE requires the map over Curve25519.

1 files changed, 22 insertions(+), 0 deletions(-)

lib/std/crypto/25519/curve25519.zig+22
...@@ -101,6 +101,11 @@ pub const Curve25519 = struct {...@@ -101,6 +101,11 @@ pub const Curve25519 = struct {
101 return try ladder(p, s, 256);101 return try ladder(p, s, 256);
102 }102 }
103103
104 /// Elligator2 map for Curve25519.
105 pub fn elligator2(r: Fe) Curve25519 {
106 return .{ .x = crypto.ecc.Edwards25519.elligator2(r).x };
107 }
108
104 /// Compute the Curve25519 equivalent to an Edwards25519 point.109 /// Compute the Curve25519 equivalent to an Edwards25519 point.
105 ///110 ///
106 /// Note that the function doesn't check that the input point is111 /// Note that the function doesn't check that the input point is
...@@ -145,6 +150,23 @@ test "non-affine edwards25519 to curve25519 projection" {...@@ -145,6 +150,23 @@ test "non-affine edwards25519 to curve25519 projection" {
145 try std.testing.expectEqualSlices(u8, &xp.toBytes(), &expected);150 try std.testing.expectEqualSlices(u8, &xp.toBytes(), &expected);
146}151}
147152
153test "elligator2" {
154 const Fe = Curve25519.Fe;
155
156 // RFC 9380 Appendix J.4.2 test vector (curve25519_XMD:SHA-512_ELL2_NU_)
157 // u = 0x608d892b641f0328523802a6603427c26e55e6f27e71a91a478148d45b5093cd
158 // Q.x = 0x51125222da5e763d97f3c10fcc92ea6860b9ccbbd2eb1285728f566721c1e65b
159 const u = Fe.fromBytes(.{
160 0xcd, 0x93, 0x50, 0x5b, 0xd4, 0x48, 0x81, 0x47, 0x1a, 0xa9, 0x71, 0x7e, 0xf2, 0xe6, 0x55, 0x6e,
161 0xc2, 0x27, 0x34, 0x60, 0xa6, 0x02, 0x38, 0x52, 0x28, 0x03, 0x1f, 0x64, 0x2b, 0x89, 0x8d, 0x60,
162 });
163 const p = Curve25519.elligator2(u);
164 try std.testing.expectEqual([32]u8{
165 0x5b, 0xe6, 0xc1, 0x21, 0x67, 0x56, 0x8f, 0x72, 0x85, 0x12, 0xeb, 0xd2, 0xbb, 0xcc, 0xb9, 0x60,
166 0x68, 0xea, 0x92, 0xcc, 0x0f, 0xc1, 0xf3, 0x97, 0x3d, 0x76, 0x5e, 0xda, 0x22, 0x52, 0x12, 0x51,
167 }, p.toBytes());
168}
169
148test "small order check" {170test "small order check" {
149 var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31;171 var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31;
150 const small_order_ss: [7][32]u8 = .{172 const small_order_ss: [7][32]u8 = .{