authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2023-11-08 11:56:56+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-08 11:56:56+01:00
loga70d8d29d5895a8b44a86e8f065ee3ee59c9bfe8
treee72871be6b35b0d5f431915b2e1faad34346aeb2
parent4504e03a18d75d0c9e4695250c807b3b4a953791
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Curve25519.fromEdwards25519(): don't assume normalized coordinates (#17920)

The low-level `Curve25519.fromEdwards25519()` function assumed that the X/Y coordinates were not scaled (Z=1). But this is not guaranteed to be the case. In most real-world applications, the coordinates are freshly decoded, either directly or via the `X25519.fromEd25519()` function, so this is not an issue. However, since we offer the ability to do that conversion after arbitrary computations, the assertion was not correct.

1 files changed, 14 insertions(+), 1 deletions(-)

lib/std/crypto/25519/curve25519.zig+14-1
...@@ -105,7 +105,8 @@ pub const Curve25519 = struct {...@@ -105,7 +105,8 @@ pub const Curve25519 = struct {
105 pub fn fromEdwards25519(p: crypto.ecc.Edwards25519) IdentityElementError!Curve25519 {105 pub fn fromEdwards25519(p: crypto.ecc.Edwards25519) IdentityElementError!Curve25519 {
106 try p.clearCofactor().rejectIdentity();106 try p.clearCofactor().rejectIdentity();
107 const one = crypto.ecc.Edwards25519.Fe.one;107 const one = crypto.ecc.Edwards25519.Fe.one;
108 const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd)108 const py = p.y.mul(p.z.invert());
109 const x = one.add(py).mul(one.sub(py).invert()); // xMont=(1+yEd)/(1-yEd)
109 return Curve25519{ .x = x };110 return Curve25519{ .x = x };
110 }111 }
111};112};
...@@ -124,6 +125,18 @@ test "curve25519" {...@@ -124,6 +125,18 @@ test "curve25519" {
124 try std.testing.expectError(error.NonCanonical, Curve25519.rejectNonCanonical(s));125 try std.testing.expectError(error.NonCanonical, Curve25519.rejectNonCanonical(s));
125}126}
126127
128test "non-affine edwards25519 to curve25519 projection" {
129 const skh = "90e7595fc89e52fdfddce9c6a43d74dbf6047025ee0462d2d172e8b6a2841d6e";
130 var sk: [32]u8 = undefined;
131 _ = std.fmt.hexToBytes(&sk, skh) catch unreachable;
132 var edp = try crypto.ecc.Edwards25519.basePoint.mul(sk);
133 const xp = try Curve25519.fromEdwards25519(edp);
134 const expected_hex = "cc4f2cdb695dd766f34118eb67b98652fed1d8bc49c330b119bbfa8a64989378";
135 var expected: [32]u8 = undefined;
136 _ = std.fmt.hexToBytes(&expected, expected_hex) catch unreachable;
137 try std.testing.expectEqualSlices(u8, &xp.toBytes(), &expected);
138}
139
127test "curve25519 small order check" {140test "curve25519 small order check" {
128 var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31;141 var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31;
129 const small_order_ss: [7][32]u8 = .{142 const small_order_ss: [7][32]u8 = .{