authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2021-05-09 18:20:43+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-05-09 18:20:43+02:00
log2d119677344c4ca6a2700b8de0a24215b9a37c06
tree632e3aea243e09bc440ed0c0ec2fe6ad73f735b9
parentf67e756211b6e69cc8fadbb6b1ec5af1bd5c7049
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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.

2 files changed, 54 insertions(+), 16 deletions(-)

lib/std/crypto/pcurves/p256.zig+48-16
......@@ -49,21 +49,26 @@ pub const P256 = struct {
4949 }
5050
5151 /// Create a point from affine coordinates after checking that they match the curve equation.
52 pub fn fromAffineCoordinates(x: Fe, y: Fe) EncodingError!P256 {
52 pub fn fromAffineCoordinates(p: AffineCoordinates) EncodingError!P256 {
53 const x = p.x;
54 const y = p.y;
5355 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);
5456 const yy = y.sq();
55 if (!x3AxB.equivalent(yy)) {
57 const on_curve = @boolToInt(x3AxB.equivalent(yy));
58 const is_identity = @boolToInt(x.equivalent(AffineCoordinates.identityElement.x)) & @boolToInt(y.equivalent(AffineCoordinates.identityElement.y));
59 if ((on_curve | is_identity) == 0) {
5660 return error.InvalidEncoding;
5761 }
58 const p: P256 = .{ .x = x, .y = y, .z = Fe.one };
59 return p;
62 var ret = P256{ .x = x, .y = y, .z = Fe.one };
63 ret.z.cMov(P256.identityElement.z, is_identity);
64 return ret;
6065 }
6166
6267 /// Create a point from serialized affine coordinates.
6368 pub fn fromSerializedAffineCoordinates(xs: [32]u8, ys: [32]u8, endian: builtin.Endian) (NonCanonicalError || EncodingError)!P256 {
6469 const x = try Fe.fromBytes(xs, endian);
6570 const y = try Fe.fromBytes(ys, endian);
66 return fromAffineCoordinates(x, y);
71 return fromAffineCoordinates(.{ .x = x, .y = y });
6772 }
6873
6974 /// Recover the Y coordinate from the X coordinate.
......@@ -96,7 +101,7 @@ pub const P256 = struct {
96101 if (encoded.len != 64) return error.InvalidEncoding;
97102 const x = try Fe.fromBytes(encoded[0..32].*, .Big);
98103 const y = try Fe.fromBytes(encoded[32..64].*, .Big);
99 return P256.fromAffineCoordinates(x, y);
104 return P256.fromAffineCoordinates(.{ .x = x, .y = y });
100105 },
101106 else => return error.InvalidEncoding,
102107 }
......@@ -177,7 +182,7 @@ pub const P256 = struct {
177182
178183 /// Add P256 points, the second being specified using affine coordinates.
179184 // Algorithm 5 from https://eprint.iacr.org/2015/1060.pdf
180 pub fn addMixed(p: P256, q: struct { x: Fe, y: Fe }) P256 {
185 pub fn addMixed(p: P256, q: AffineCoordinates) P256 {
181186 var t0 = p.x.mul(q.x);
182187 var t1 = p.y.mul(q.y);
183188 var t3 = q.x.add(q.y);
......@@ -194,9 +199,9 @@ pub const P256 = struct {
194199 Z3 = X3.dbl();
195200 X3 = X3.add(Z3);
196201 Z3 = t1.sub(X3);
197 X3 = t1.dbl();
202 X3 = t1.add(X3);
198203 Y3 = B.mul(Y3);
199 t1 = p.z.add(p.z);
204 t1 = p.z.dbl();
200205 var t2 = t1.add(p.z);
201206 Y3 = Y3.sub(t2);
202207 Y3 = Y3.sub(t0);
......@@ -214,14 +219,16 @@ pub const P256 = struct {
214219 Z3 = t4.mul(Z3);
215220 t1 = t3.mul(t0);
216221 Z3 = Z3.add(t1);
217 return .{
222 var ret = P256{
218223 .x = X3,
219224 .y = Y3,
220225 .z = Z3,
221226 };
227 ret.cMov(p, @boolToInt(q.x.isZero()));
228 return ret;
222229 }
223230
224 // Add P256 points.
231 /// Add P256 points.
225232 // Algorithm 4 from https://eprint.iacr.org/2015/1060.pdf
226233 pub fn add(p: P256, q: P256) P256 {
227234 var t0 = p.x.mul(q.x);
......@@ -274,18 +281,19 @@ pub const P256 = struct {
274281 };
275282 }
276283
277 // Subtract P256 points.
284 /// Subtract P256 points.
278285 pub fn sub(p: P256, q: P256) P256 {
279286 return p.add(q.neg());
280287 }
281288
282289 /// Return affine coordinates.
283 pub fn affineCoordinates(p: P256) struct { x: Fe, y: Fe } {
290 pub fn affineCoordinates(p: P256) AffineCoordinates {
284291 const zinv = p.z.invert();
285 const ret = .{
292 var ret = AffineCoordinates{
286293 .x = p.x.mul(zinv),
287294 .y = p.y.mul(zinv),
288295 };
296 ret.cMov(AffineCoordinates.identityElement, @boolToInt(p.x.isZero()));
289297 return ret;
290298 }
291299
......@@ -382,11 +390,21 @@ pub const P256 = struct {
382390 return pc;
383391 }
384392
393 const basePointPc = comptime pc: {
394 @setEvalBranchQuota(50000);
395 break :pc precompute(P256.basePoint, 15);
396 };
397
398 const basePointPc8 = comptime pc: {
399 @setEvalBranchQuota(50000);
400 break :pc precompute(P256.basePoint, 8);
401 };
402
385403 /// Multiply an elliptic curve point by a scalar.
386404 /// Return error.IdentityElement if the result is the identity element.
387405 pub fn mul(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 {
388406 const s = if (endian == .Little) s_ else Fe.orderSwap(s_);
389 const pc = if (p.is_base) precompute(P256.basePoint, 15) else pc: {
407 const pc = if (p.is_base) basePointPc else pc: {
390408 try p.rejectIdentity();
391409 const xpc = precompute(p, 15);
392410 break :pc xpc;
......@@ -398,7 +416,7 @@ pub const P256 = struct {
398416 /// This can be used for signature verification.
399417 pub fn mulPublic(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 {
400418 const s = if (endian == .Little) s_ else Fe.orderSwap(s_);
401 const pc = if (p.is_base) precompute(P256.basePoint, 8) else pc: {
419 const pc = if (p.is_base) basePointPc8 else pc: {
402420 try p.rejectIdentity();
403421 const xpc = precompute(p, 8);
404422 break :pc xpc;
......@@ -407,6 +425,20 @@ pub const P256 = struct {
407425 }
408426};
409427
428/// A point in affine coordinates.
429pub const AffineCoordinates = struct {
430 x: P256.Fe,
431 y: P256.Fe,
432
433 /// Identity element in affine coordinates.
434 pub const identityElement = AffineCoordinates{ .x = P256.identityElement.x, .y = P256.identityElement.y };
435
436 fn cMov(p: *AffineCoordinates, a: AffineCoordinates, c: u1) void {
437 p.x.cMov(a.x, c);
438 p.y.cMov(a.y, c);
439 }
440};
441
410442test "p256" {
411443 _ = @import("tests.zig");
412444}
lib/std/crypto/pcurves/tests.zig+6
......@@ -101,3 +101,9 @@ test "p256 field element non-canonical encoding" {
101101 const s = [_]u8{0xff} ** 32;
102102 try testing.expectError(error.NonCanonical, P256.Fe.fromBytes(s, .Little));
103103}
104
105test "p256 neutral element decoding" {
106 try testing.expectError(error.InvalidEncoding, P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.zero }));
107 const p = try P256.fromAffineCoordinates(.{ .x = P256.Fe.zero, .y = P256.Fe.one });
108 try testing.expectError(error.IdentityElement, p.rejectIdentity());
109}