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 {...@@ -49,21 +49,26 @@ pub const P256 = struct {
49 }49 }
5050
51 /// Create a point from affine coordinates after checking that they match the curve equation.51 /// 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;
53 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);55 const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B);
54 const yy = y.sq();56 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) {
56 return error.InvalidEncoding;60 return error.InvalidEncoding;
57 }61 }
58 const p: P256 = .{ .x = x, .y = y, .z = Fe.one };62 var ret = P256{ .x = x, .y = y, .z = Fe.one };
59 return p;63 ret.z.cMov(P256.identityElement.z, is_identity);
64 return ret;
60 }65 }
6166
62 /// Create a point from serialized affine coordinates.67 /// Create a point from serialized affine coordinates.
63 pub fn fromSerializedAffineCoordinates(xs: [32]u8, ys: [32]u8, endian: builtin.Endian) (NonCanonicalError || EncodingError)!P256 {68 pub fn fromSerializedAffineCoordinates(xs: [32]u8, ys: [32]u8, endian: builtin.Endian) (NonCanonicalError || EncodingError)!P256 {
64 const x = try Fe.fromBytes(xs, endian);69 const x = try Fe.fromBytes(xs, endian);
65 const y = try Fe.fromBytes(ys, endian);70 const y = try Fe.fromBytes(ys, endian);
66 return fromAffineCoordinates(x, y);71 return fromAffineCoordinates(.{ .x = x, .y = y });
67 }72 }
6873
69 /// Recover the Y coordinate from the X coordinate.74 /// Recover the Y coordinate from the X coordinate.
...@@ -96,7 +101,7 @@ pub const P256 = struct {...@@ -96,7 +101,7 @@ pub const P256 = struct {
96 if (encoded.len != 64) return error.InvalidEncoding;101 if (encoded.len != 64) return error.InvalidEncoding;
97 const x = try Fe.fromBytes(encoded[0..32].*, .Big);102 const x = try Fe.fromBytes(encoded[0..32].*, .Big);
98 const y = try Fe.fromBytes(encoded[32..64].*, .Big);103 const y = try Fe.fromBytes(encoded[32..64].*, .Big);
99 return P256.fromAffineCoordinates(x, y);104 return P256.fromAffineCoordinates(.{ .x = x, .y = y });
100 },105 },
101 else => return error.InvalidEncoding,106 else => return error.InvalidEncoding,
102 }107 }
...@@ -177,7 +182,7 @@ pub const P256 = struct {...@@ -177,7 +182,7 @@ pub const P256 = struct {
177182
178 /// Add P256 points, the second being specified using affine coordinates.183 /// Add P256 points, the second being specified using affine coordinates.
179 // Algorithm 5 from https://eprint.iacr.org/2015/1060.pdf184 // 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 {
181 var t0 = p.x.mul(q.x);186 var t0 = p.x.mul(q.x);
182 var t1 = p.y.mul(q.y);187 var t1 = p.y.mul(q.y);
183 var t3 = q.x.add(q.y);188 var t3 = q.x.add(q.y);
...@@ -194,9 +199,9 @@ pub const P256 = struct {...@@ -194,9 +199,9 @@ pub const P256 = struct {
194 Z3 = X3.dbl();199 Z3 = X3.dbl();
195 X3 = X3.add(Z3);200 X3 = X3.add(Z3);
196 Z3 = t1.sub(X3);201 Z3 = t1.sub(X3);
197 X3 = t1.dbl();202 X3 = t1.add(X3);
198 Y3 = B.mul(Y3);203 Y3 = B.mul(Y3);
199 t1 = p.z.add(p.z);204 t1 = p.z.dbl();
200 var t2 = t1.add(p.z);205 var t2 = t1.add(p.z);
201 Y3 = Y3.sub(t2);206 Y3 = Y3.sub(t2);
202 Y3 = Y3.sub(t0);207 Y3 = Y3.sub(t0);
...@@ -214,14 +219,16 @@ pub const P256 = struct {...@@ -214,14 +219,16 @@ pub const P256 = struct {
214 Z3 = t4.mul(Z3);219 Z3 = t4.mul(Z3);
215 t1 = t3.mul(t0);220 t1 = t3.mul(t0);
216 Z3 = Z3.add(t1);221 Z3 = Z3.add(t1);
217 return .{222 var ret = P256{
218 .x = X3,223 .x = X3,
219 .y = Y3,224 .y = Y3,
220 .z = Z3,225 .z = Z3,
221 };226 };
227 ret.cMov(p, @boolToInt(q.x.isZero()));
228 return ret;
222 }229 }
223230
224 // Add P256 points.231 /// Add P256 points.
225 // Algorithm 4 from https://eprint.iacr.org/2015/1060.pdf232 // Algorithm 4 from https://eprint.iacr.org/2015/1060.pdf
226 pub fn add(p: P256, q: P256) P256 {233 pub fn add(p: P256, q: P256) P256 {
227 var t0 = p.x.mul(q.x);234 var t0 = p.x.mul(q.x);
...@@ -274,18 +281,19 @@ pub const P256 = struct {...@@ -274,18 +281,19 @@ pub const P256 = struct {
274 };281 };
275 }282 }
276283
277 // Subtract P256 points.284 /// Subtract P256 points.
278 pub fn sub(p: P256, q: P256) P256 {285 pub fn sub(p: P256, q: P256) P256 {
279 return p.add(q.neg());286 return p.add(q.neg());
280 }287 }
281288
282 /// Return affine coordinates.289 /// Return affine coordinates.
283 pub fn affineCoordinates(p: P256) struct { x: Fe, y: Fe } {290 pub fn affineCoordinates(p: P256) AffineCoordinates {
284 const zinv = p.z.invert();291 const zinv = p.z.invert();
285 const ret = .{292 var ret = AffineCoordinates{
286 .x = p.x.mul(zinv),293 .x = p.x.mul(zinv),
287 .y = p.y.mul(zinv),294 .y = p.y.mul(zinv),
288 };295 };
296 ret.cMov(AffineCoordinates.identityElement, @boolToInt(p.x.isZero()));
289 return ret;297 return ret;
290 }298 }
291299
...@@ -382,11 +390,21 @@ pub const P256 = struct {...@@ -382,11 +390,21 @@ pub const P256 = struct {
382 return pc;390 return pc;
383 }391 }
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
385 /// Multiply an elliptic curve point by a scalar.403 /// Multiply an elliptic curve point by a scalar.
386 /// Return error.IdentityElement if the result is the identity element.404 /// Return error.IdentityElement if the result is the identity element.
387 pub fn mul(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 {405 pub fn mul(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 {
388 const s = if (endian == .Little) s_ else Fe.orderSwap(s_);406 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: {
390 try p.rejectIdentity();408 try p.rejectIdentity();
391 const xpc = precompute(p, 15);409 const xpc = precompute(p, 15);
392 break :pc xpc;410 break :pc xpc;
...@@ -398,7 +416,7 @@ pub const P256 = struct {...@@ -398,7 +416,7 @@ pub const P256 = struct {
398 /// This can be used for signature verification.416 /// This can be used for signature verification.
399 pub fn mulPublic(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 {417 pub fn mulPublic(p: P256, s_: [32]u8, endian: builtin.Endian) IdentityElementError!P256 {
400 const s = if (endian == .Little) s_ else Fe.orderSwap(s_);418 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: {
402 try p.rejectIdentity();420 try p.rejectIdentity();
403 const xpc = precompute(p, 8);421 const xpc = precompute(p, 8);
404 break :pc xpc;422 break :pc xpc;
...@@ -407,6 +425,20 @@ pub const P256 = struct {...@@ -407,6 +425,20 @@ pub const P256 = struct {
407 }425 }
408};426};
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
410test "p256" {442test "p256" {
411 _ = @import("tests.zig");443 _ = @import("tests.zig");
412}444}
lib/std/crypto/pcurves/tests.zig+6
...@@ -101,3 +101,9 @@ test "p256 field element non-canonical encoding" {...@@ -101,3 +101,9 @@ test "p256 field element non-canonical encoding" {
101 const s = [_]u8{0xff} ** 32;101 const s = [_]u8{0xff} ** 32;
102 try testing.expectError(error.NonCanonical, P256.Fe.fromBytes(s, .Little));102 try testing.expectError(error.NonCanonical, P256.Fe.fromBytes(s, .Little));
103}103}
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}