authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-05-28 20:36:58+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-07-05 12:29:29+02:00
loge7bc7efda73eb68e5d599feb18e2538cc16df85e
tree94dc9e845504aa6a2c2ccd10667ac6669dd7198e
parentd593a596185c3442afa2e1955e1157a31bd4b554

crypto.pcurves: reject affine encodings of the point at infinity

According to SEC1, the point at infinity has a unique representation (encoded as single 00 byte); it's not supposed to be representable in affine coordinates. So we can simplify the function to just check the equation. The point at infinity can still be decoded by the regular fromSec1() function.

6 files changed, 21 insertions(+), 24 deletions(-)

lib/std/crypto/pcurves/p256.zig+2-6
......@@ -49,14 +49,10 @@ pub const P256 = struct {
4949 const y = p.y;
5050 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);
5151 const yy = y.sq();
52 const on_curve = @intFromBool(x3AxB.equivalent(yy));
53 const is_identity = @intFromBool(x.equivalent(AffineCoordinates.identityElement.x)) & @intFromBool(y.equivalent(AffineCoordinates.identityElement.y));
54 if ((on_curve | is_identity) == 0) {
52 if (!x3AxB.equivalent(yy)) {
5553 return error.InvalidEncoding;
5654 }
57 var ret = P256{ .x = x, .y = y, .z = Fe.one };
58 ret.z.cMov(P256.identityElement.z, is_identity);
59 return ret;
55 return .{ .x = x, .y = y, .z = Fe.one };
6056 }
6157
6258 /// Create a point from serialized affine coordinates.
lib/std/crypto/pcurves/p384.zig+2-6
......@@ -49,14 +49,10 @@ pub const P384 = struct {
4949 const y = p.y;
5050 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);
5151 const yy = y.sq();
52 const on_curve = @intFromBool(x3AxB.equivalent(yy));
53 const is_identity = @intFromBool(x.equivalent(AffineCoordinates.identityElement.x)) & @intFromBool(y.equivalent(AffineCoordinates.identityElement.y));
54 if ((on_curve | is_identity) == 0) {
52 if (!x3AxB.equivalent(yy)) {
5553 return error.InvalidEncoding;
5654 }
57 var ret = P384{ .x = x, .y = y, .z = Fe.one };
58 ret.z.cMov(P384.identityElement.z, is_identity);
59 return ret;
55 return .{ .x = x, .y = y, .z = Fe.one };
6056 }
6157
6258 /// Create a point from serialized affine coordinates.
lib/std/crypto/pcurves/secp256k1.zig+2-6
......@@ -102,14 +102,10 @@ pub const Secp256k1 = struct {
102102 const y = p.y;
103103 const x3B = x.sq().mul(x).add(B);
104104 const yy = y.sq();
105 const on_curve = @intFromBool(x3B.equivalent(yy));
106 const is_identity = @intFromBool(x.equivalent(AffineCoordinates.identityElement.x)) & @intFromBool(y.equivalent(AffineCoordinates.identityElement.y));
107 if ((on_curve | is_identity) == 0) {
105 if (!x3B.equivalent(yy)) {
108106 return error.InvalidEncoding;
109107 }
110 var ret = Secp256k1{ .x = x, .y = y, .z = Fe.one };
111 ret.z.cMov(Secp256k1.identityElement.z, is_identity);
112 return ret;
108 return .{ .x = x, .y = y, .z = Fe.one };
113109 }
114110
115111 /// Create a point from serialized affine coordinates.
lib/std/crypto/pcurves/tests/p256.zig+2-2
......@@ -103,8 +103,8 @@ test "p256 field element non-canonical encoding" {
103103
104104test "p256 neutral element decoding" {
105105 try testing.expectError(error.InvalidEncoding, P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.zero }));
106 const p = try P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.one });
107 try testing.expectError(error.IdentityElement, p.rejectIdentity());
106 try testing.expectError(error.InvalidEncoding, P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.one }));
107 try testing.expectError(error.IdentityElement, P256.identityElement.rejectIdentity());
108108}
109109
110110test "p256 double base multiplication" {
lib/std/crypto/pcurves/tests/p384.zig+2-2
......@@ -106,8 +106,8 @@ test "p384 field element non-canonical encoding" {
106106
107107test "p384 neutral element decoding" {
108108 try testing.expectError(error.InvalidEncoding, P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.zero }));
109 const p = try P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.one });
110 try testing.expectError(error.IdentityElement, p.rejectIdentity());
109 try testing.expectError(error.InvalidEncoding, P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.one }));
110 try testing.expectError(error.IdentityElement, P384.identityElement.rejectIdentity());
111111}
112112
113113test "p384 double base multiplication" {
lib/std/crypto/pcurves/tests/secp256k1.zig+11-2
......@@ -115,8 +115,17 @@ test "secp256k1 field element non-canonical encoding" {
115115
116116test "secp256k1 neutral element decoding" {
117117 try testing.expectError(error.InvalidEncoding, Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.zero }));
118 const p = try Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.one });
119 try testing.expectError(error.IdentityElement, p.rejectIdentity());
118 try testing.expectError(error.InvalidEncoding, Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.one }));
119 try testing.expectError(error.IdentityElement, Secp256k1.identityElement.rejectIdentity());
120}
121
122test "secp256k1 uncompressed SEC1 must not accept infinity" {
123 var buf: [65]u8 = @splat(0);
124 buf[0] = 0x04;
125 buf[64] = 0x01;
126 try testing.expectError(error.InvalidEncoding, Secp256k1.fromSec1(&buf));
127 buf[64] = 0x00;
128 try testing.expectError(error.InvalidEncoding, Secp256k1.fromSec1(&buf));
120129}
121130
122131test "secp256k1 double base multiplication" {