mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
p256: properly handle neutral element & add AffineCoordinates struct (#8718)
Instead of multiple references to an anonymous structure to represent affine coordinates, add an actual `AffineCoordinates` structure. Also properly handle the neutral element during coordinate conversion and fix mixed addition. And comptime the small precomputation table for basepoint multiplication.
This commit is contained in:
parent
f67e756211
commit
2d11967734
@ -49,21 +49,26 @@ pub const P256 = struct {
|
||||
}
|
||||
|
||||
/// Create a point from affine coordinates after checking that they match the curve equation.
|
||||
pub fn fromAffineCoordinates(x: Fe, y: Fe) EncodingError!P256 {
|
||||
pub fn fromAffineCoordinates(p: AffineCoordinates) EncodingError!P256 {
|
||||
const x = p.x;
|
||||
const y = p.y;
|
||||
const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);
|
||||
const yy = y.sq();
|
||||
if (!x3AxB.equivalent(yy)) {
|
||||
const on_curve = @boolToInt(x3AxB.equivalent(yy));
|
||||
const is_identity = @boolToInt(x.equivalent(AffineCoordinates.identityElement.x)) & @boolToInt(y.equivalent(AffineCoordinates.identityElement.y));
|
||||
if ((on_curve | is_identity) == 0) {
|
||||
return error.InvalidEncoding;
|
||||
}
|
||||
const p: P256 = .{ .x = x, .y = y, .z = Fe.one };
|
||||
return p;
|
||||
var ret = P256{ .x = x, .y = y, .z = Fe.one };
|
||||
ret.z.cMov(P256.identityElement.z, is_identity);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Create a point from serialized affine coordinates.
|
||||
pub fn fromSerializedAffineCoordinates(xs: [32]u8, ys: [32]u8, endian: builtin.Endian) (NonCanonicalError || EncodingError)!P256 {
|
||||
const x = try Fe.fromBytes(xs, endian);
|
||||
const y = try Fe.fromBytes(ys, endian);
|
||||
return fromAffineCoordinates(x, y);
|
||||
return fromAffineCoordinates(.{ .x = x, .y = y });
|
||||
}
|
||||
|
||||
/// Recover the Y coordinate from the X coordinate.
|
||||
@ -96,7 +101,7 @@ pub const P256 = struct {
|
||||
if (encoded.len != 64) return error.InvalidEncoding;
|
||||
const x = try Fe.fromBytes(encoded[0..32].*, .Big);
|
||||
const y = try Fe.fromBytes(encoded[32..64].*, .Big);
|
||||
return P256.fromAffineCoordinates(x, y);
|
||||
return P256.fromAffineCoordinates(.{ .x = x, .y = y });
|
||||
},
|
||||
else => return error.InvalidEncoding,
|
||||
}
|
||||
@ -177,7 +182,7 @@ pub const P256 = struct {
|
||||
|
||||
/// Add P256 points, the second being specified using affine coordinates.
|
||||
// Algorithm 5 from https://eprint.iacr.org/2015/1060.pdf
|
||||
pub fn addMixed(p: P256, q: struct { x: Fe, y: Fe }) P256 {
|
||||
pub fn addMixed(p: P256, q: AffineCoordinates) P256 {
|
||||
var t0 = p.x.mul(q.x);
|
||||
var t1 = p.y.mul(q.y);
|
||||
var t3 = q.x.add(q.y);
|
||||
@ -194,9 +199,9 @@ pub const P256 = struct {
|
||||
Z3 = X3.dbl();
|
||||
X3 = X3.add(Z3);
|
||||
Z3 = t1.sub(X3);
|
||||
X3 = t1.dbl();
|
||||
X3 = t1.add(X3);
|
||||
Y3 = B.mul(Y3);
|
||||
t1 = p.z.add(p.z);
|
||||
t1 = p.z.dbl();
|
||||
var t2 = t1.add(p.z);
|
||||
Y3 = Y3.sub(t2);
|
||||
Y3 = Y3.sub(t0);
|
||||
@ -214,14 +219,16 @@ pub const P256 = struct {
|
||||
Z3 = t4.mul(Z3);
|
||||
t1 = t3.mul(t0);
|
||||
Z3 = Z3.add(t1);
|
||||
return .{
|
||||
var ret = P256{
|
||||
.x = X3,
|
||||
.y = Y3,
|
||||
.z = Z3,
|
||||
};
|
||||
ret.cMov(p, @boolToInt(q.x.isZero()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Add P256 points.
|
||||
/// Add P256 points.
|
||||
// Algorithm 4 from https://eprint.iacr.org/2015/1060.pdf
|
||||
pub fn add(p: P256, q: P256) P256 {
|
||||
var t0 = p.x.mul(q.x);
|
||||
@ -274,18 +281,19 @@ pub const P256 = struct {
|
||||
};
|
||||
}
|
||||
|
||||
// Subtract P256 points.
|
||||
/// Subtract P256 points.
|
||||
pub fn sub(p: P256, q: P256) P256 {
|
||||
return p.add(q.neg());
|
||||
}
|
||||
|
||||
/// Return affine coordinates.
|
||||
pub fn affineCoordinates(p: P256) struct { x: Fe, y: Fe } {
|
||||
pub fn affineCoordinates(p: P256) AffineCoordinates {
|
||||
const zinv = p.z.invert();
|
||||
const ret = .{
|
||||
var ret = AffineCoordinates{
|
||||
.x = p.x.mul(zinv),
|
||||
.y = p.y.mul(zinv),
|
||||
};
|
||||
ret.cMov(AffineCoordinates.identityElement, @boolToInt(p.x.isZero()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -382,11 +390,21 @@ pub const P256 = struct {
|
||||
return pc;
|
||||
}
|
||||
|
||||
const basePointPc = comptime pc: {
|
||||
@setEvalBranchQuota(50000);
|
||||
break :pc precompute(P256.basePoint, 15);
|
||||
};
|
||||
|
||||
const basePointPc8 = comptime pc: {
|
||||
@setEvalBranchQuota(50000);
|
||||
break :pc precompute(P256.basePoint, 8);
|
||||
};
|
||||
|
||||
/// Multiply an elliptic curve point by a scalar.
|
||||
/// Return error.IdentityElement if the result is the identity element.
|
||||
pub fn mul(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 {
|
||||
const s = if (endian == .Little) s_ else Fe.orderSwap(s_);
|
||||
const pc = if (p.is_base) precompute(P256.basePoint, 15) else pc: {
|
||||
const pc = if (p.is_base) basePointPc else pc: {
|
||||
try p.rejectIdentity();
|
||||
const xpc = precompute(p, 15);
|
||||
break :pc xpc;
|
||||
@ -398,7 +416,7 @@ pub const P256 = struct {
|
||||
/// This can be used for signature verification.
|
||||
pub fn mulPublic(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 {
|
||||
const s = if (endian == .Little) s_ else Fe.orderSwap(s_);
|
||||
const pc = if (p.is_base) precompute(P256.basePoint, 8) else pc: {
|
||||
const pc = if (p.is_base) basePointPc8 else pc: {
|
||||
try p.rejectIdentity();
|
||||
const xpc = precompute(p, 8);
|
||||
break :pc xpc;
|
||||
@ -407,6 +425,20 @@ pub const P256 = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// A point in affine coordinates.
|
||||
pub const AffineCoordinates = struct {
|
||||
x: P256.Fe,
|
||||
y: P256.Fe,
|
||||
|
||||
/// Identity element in affine coordinates.
|
||||
pub const identityElement = AffineCoordinates{ .x = P256.identityElement.x, .y = P256.identityElement.y };
|
||||
|
||||
fn cMov(p: *AffineCoordinates, a: AffineCoordinates, c: u1) void {
|
||||
p.x.cMov(a.x, c);
|
||||
p.y.cMov(a.y, c);
|
||||
}
|
||||
};
|
||||
|
||||
test "p256" {
|
||||
_ = @import("tests.zig");
|
||||
}
|
||||
|
||||
@ -101,3 +101,9 @@ test "p256 field element non-canonical encoding" {
|
||||
const s = [_]u8{0xff} ** 32;
|
||||
try testing.expectError(error.NonCanonical, P256.Fe.fromBytes(s, .Little));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user