Skip to content

Commit

Permalink
p256: properly handle neutral element & add AffineCoordinates struct (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
jedisct1 authored May 9, 2021
1 parent f67e756 commit 2d11967
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 16 deletions.
64 changes: 48 additions & 16 deletions lib/std/crypto/pcurves/p256.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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");
}
6 changes: 6 additions & 0 deletions lib/std/crypto/pcurves/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

0 comments on commit 2d11967

Please sign in to comment.