From e7bc7efda73eb68e5d599feb18e2538cc16df85e Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 28 May 2026 20:36:58 +0200 Subject: [PATCH] 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. --- lib/std/crypto/pcurves/p256.zig | 8 ++------ lib/std/crypto/pcurves/p384.zig | 8 ++------ lib/std/crypto/pcurves/secp256k1.zig | 8 ++------ lib/std/crypto/pcurves/tests/p256.zig | 4 ++-- lib/std/crypto/pcurves/tests/p384.zig | 4 ++-- lib/std/crypto/pcurves/tests/secp256k1.zig | 13 +++++++++++-- 6 files changed, 21 insertions(+), 24 deletions(-) diff --git a/lib/std/crypto/pcurves/p256.zig b/lib/std/crypto/pcurves/p256.zig index 4746061e020627147ba7c0cc642c9b42c0498cf8..1b5469d8f582cb54b5fd3c8bc455840ce9e63aa1 100644 --- a/lib/std/crypto/pcurves/p256.zig +++ b/lib/std/crypto/pcurves/p256.zig @@ -49,14 +49,10 @@ pub const P256 = struct { const y = p.y; const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B); const yy = y.sq(); - const on_curve = @intFromBool(x3AxB.equivalent(yy)); - const is_identity = @intFromBool(x.equivalent(AffineCoordinates.identityElement.x)) & @intFromBool(y.equivalent(AffineCoordinates.identityElement.y)); - if ((on_curve | is_identity) == 0) { + if (!x3AxB.equivalent(yy)) { return error.InvalidEncoding; } - var ret = P256{ .x = x, .y = y, .z = Fe.one }; - ret.z.cMov(P256.identityElement.z, is_identity); - return ret; + return .{ .x = x, .y = y, .z = Fe.one }; } /// Create a point from serialized affine coordinates. diff --git a/lib/std/crypto/pcurves/p384.zig b/lib/std/crypto/pcurves/p384.zig index 0dbfdc67f159171d7bc443be2aa03ce4c595fd1c..8bc0ec36f2d64e3c6d73e5d782b6df143c6a859f 100644 --- a/lib/std/crypto/pcurves/p384.zig +++ b/lib/std/crypto/pcurves/p384.zig @@ -49,14 +49,10 @@ pub const P384 = struct { const y = p.y; const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B); const yy = y.sq(); - const on_curve = @intFromBool(x3AxB.equivalent(yy)); - const is_identity = @intFromBool(x.equivalent(AffineCoordinates.identityElement.x)) & @intFromBool(y.equivalent(AffineCoordinates.identityElement.y)); - if ((on_curve | is_identity) == 0) { + if (!x3AxB.equivalent(yy)) { return error.InvalidEncoding; } - var ret = P384{ .x = x, .y = y, .z = Fe.one }; - ret.z.cMov(P384.identityElement.z, is_identity); - return ret; + return .{ .x = x, .y = y, .z = Fe.one }; } /// Create a point from serialized affine coordinates. diff --git a/lib/std/crypto/pcurves/secp256k1.zig b/lib/std/crypto/pcurves/secp256k1.zig index 1c1caae19aae145caa95a541f388318991865585..9ce8b944ebbffc071bcdd3be90fa47463525289e 100644 --- a/lib/std/crypto/pcurves/secp256k1.zig +++ b/lib/std/crypto/pcurves/secp256k1.zig @@ -102,14 +102,10 @@ pub const Secp256k1 = struct { const y = p.y; const x3B = x.sq().mul(x).add(B); const yy = y.sq(); - const on_curve = @intFromBool(x3B.equivalent(yy)); - const is_identity = @intFromBool(x.equivalent(AffineCoordinates.identityElement.x)) & @intFromBool(y.equivalent(AffineCoordinates.identityElement.y)); - if ((on_curve | is_identity) == 0) { + if (!x3B.equivalent(yy)) { return error.InvalidEncoding; } - var ret = Secp256k1{ .x = x, .y = y, .z = Fe.one }; - ret.z.cMov(Secp256k1.identityElement.z, is_identity); - return ret; + return .{ .x = x, .y = y, .z = Fe.one }; } /// Create a point from serialized affine coordinates. diff --git a/lib/std/crypto/pcurves/tests/p256.zig b/lib/std/crypto/pcurves/tests/p256.zig index 2e2615b2ca8913a045203b4a6145c47851cb82ac..15ee5c99b75740d0912ad7a5e4ccb5e7ea31ab0d 100644 --- a/lib/std/crypto/pcurves/tests/p256.zig +++ b/lib/std/crypto/pcurves/tests/p256.zig @@ -103,8 +103,8 @@ test "p256 field element non-canonical encoding" { test "p256 neutral element decoding" { try testing.expectError(error.InvalidEncoding, P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.zero })); - const p = try P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.one }); - try testing.expectError(error.IdentityElement, p.rejectIdentity()); + try testing.expectError(error.InvalidEncoding, P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.one })); + try testing.expectError(error.IdentityElement, P256.identityElement.rejectIdentity()); } test "p256 double base multiplication" { diff --git a/lib/std/crypto/pcurves/tests/p384.zig b/lib/std/crypto/pcurves/tests/p384.zig index 56342af8bd680527ee26758c50522790b3126817..2cbbb96b3eb21d32328e97c86536f2f73d905c6b 100644 --- a/lib/std/crypto/pcurves/tests/p384.zig +++ b/lib/std/crypto/pcurves/tests/p384.zig @@ -106,8 +106,8 @@ test "p384 field element non-canonical encoding" { test "p384 neutral element decoding" { try testing.expectError(error.InvalidEncoding, P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.zero })); - const p = try P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.one }); - try testing.expectError(error.IdentityElement, p.rejectIdentity()); + try testing.expectError(error.InvalidEncoding, P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.one })); + try testing.expectError(error.IdentityElement, P384.identityElement.rejectIdentity()); } test "p384 double base multiplication" { diff --git a/lib/std/crypto/pcurves/tests/secp256k1.zig b/lib/std/crypto/pcurves/tests/secp256k1.zig index 158e85dea3320f83dcce49087b56d6a94f5a221f..94fb65ccec59ab493d6f03be44bc363f9302a2f4 100644 --- a/lib/std/crypto/pcurves/tests/secp256k1.zig +++ b/lib/std/crypto/pcurves/tests/secp256k1.zig @@ -115,8 +115,17 @@ test "secp256k1 field element non-canonical encoding" { test "secp256k1 neutral element decoding" { try testing.expectError(error.InvalidEncoding, Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.zero })); - const p = try Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.one }); - try testing.expectError(error.IdentityElement, p.rejectIdentity()); + try testing.expectError(error.InvalidEncoding, Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.one })); + try testing.expectError(error.IdentityElement, Secp256k1.identityElement.rejectIdentity()); +} + +test "secp256k1 uncompressed SEC1 must not accept infinity" { + var buf: [65]u8 = @splat(0); + buf[0] = 0x04; + buf[64] = 0x01; + try testing.expectError(error.InvalidEncoding, Secp256k1.fromSec1(&buf)); + buf[64] = 0x00; + try testing.expectError(error.InvalidEncoding, Secp256k1.fromSec1(&buf)); } test "secp256k1 double base multiplication" { -- 2.54.0