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 {...@@ -49,14 +49,10 @@ pub const P256 = struct {
49 const y = p.y;49 const y = p.y;
50 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);50 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);
51 const yy = y.sq();51 const yy = y.sq();
52 const on_curve = @intFromBool(x3AxB.equivalent(yy));52 if (!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) {
55 return error.InvalidEncoding;53 return error.InvalidEncoding;
56 }54 }
57 var ret = P256{ .x = x, .y = y, .z = Fe.one };55 return .{ .x = x, .y = y, .z = Fe.one };
58 ret.z.cMov(P256.identityElement.z, is_identity);
59 return ret;
60 }56 }
6157
62 /// Create a point from serialized affine coordinates.58 /// Create a point from serialized affine coordinates.
lib/std/crypto/pcurves/p384.zig+2-6
...@@ -49,14 +49,10 @@ pub const P384 = struct {...@@ -49,14 +49,10 @@ pub const P384 = struct {
49 const y = p.y;49 const y = p.y;
50 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);50 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);
51 const yy = y.sq();51 const yy = y.sq();
52 const on_curve = @intFromBool(x3AxB.equivalent(yy));52 if (!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) {
55 return error.InvalidEncoding;53 return error.InvalidEncoding;
56 }54 }
57 var ret = P384{ .x = x, .y = y, .z = Fe.one };55 return .{ .x = x, .y = y, .z = Fe.one };
58 ret.z.cMov(P384.identityElement.z, is_identity);
59 return ret;
60 }56 }
6157
62 /// Create a point from serialized affine coordinates.58 /// Create a point from serialized affine coordinates.
lib/std/crypto/pcurves/secp256k1.zig+2-6
...@@ -102,14 +102,10 @@ pub const Secp256k1 = struct {...@@ -102,14 +102,10 @@ pub const Secp256k1 = struct {
102 const y = p.y;102 const y = p.y;
103 const x3B = x.sq().mul(x).add(B);103 const x3B = x.sq().mul(x).add(B);
104 const yy = y.sq();104 const yy = y.sq();
105 const on_curve = @intFromBool(x3B.equivalent(yy));105 if (!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) {
108 return error.InvalidEncoding;106 return error.InvalidEncoding;
109 }107 }
110 var ret = Secp256k1{ .x = x, .y = y, .z = Fe.one };108 return .{ .x = x, .y = y, .z = Fe.one };
111 ret.z.cMov(Secp256k1.identityElement.z, is_identity);
112 return ret;
113 }109 }
114110
115 /// Create a point from serialized affine coordinates.111 /// 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" {...@@ -103,8 +103,8 @@ test "p256 field element non-canonical encoding" {
103103
104test "p256 neutral element decoding" {104test "p256 neutral element decoding" {
105 try testing.expectError(error.InvalidEncoding, P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.zero }));105 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 });106 try testing.expectError(error.InvalidEncoding, P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.one }));
107 try testing.expectError(error.IdentityElement, p.rejectIdentity());107 try testing.expectError(error.IdentityElement, P256.identityElement.rejectIdentity());
108}108}
109109
110test "p256 double base multiplication" {110test "p256 double base multiplication" {
lib/std/crypto/pcurves/tests/p384.zig+2-2
...@@ -106,8 +106,8 @@ test "p384 field element non-canonical encoding" {...@@ -106,8 +106,8 @@ test "p384 field element non-canonical encoding" {
106106
107test "p384 neutral element decoding" {107test "p384 neutral element decoding" {
108 try testing.expectError(error.InvalidEncoding, P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.zero }));108 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 });109 try testing.expectError(error.InvalidEncoding, P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.one }));
110 try testing.expectError(error.IdentityElement, p.rejectIdentity());110 try testing.expectError(error.IdentityElement, P384.identityElement.rejectIdentity());
111}111}
112112
113test "p384 double base multiplication" {113test "p384 double base multiplication" {
lib/std/crypto/pcurves/tests/secp256k1.zig+11-2
...@@ -115,8 +115,17 @@ test "secp256k1 field element non-canonical encoding" {...@@ -115,8 +115,17 @@ test "secp256k1 field element non-canonical encoding" {
115115
116test "secp256k1 neutral element decoding" {116test "secp256k1 neutral element decoding" {
117 try testing.expectError(error.InvalidEncoding, Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.zero }));117 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 });118 try testing.expectError(error.InvalidEncoding, Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.one }));
119 try testing.expectError(error.IdentityElement, p.rejectIdentity());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));
120}129}
121130
122test "secp256k1 double base multiplication" {131test "secp256k1 double base multiplication" {