authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2022-05-26 01:38:32+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-26 13:30:03+02:00
logb08d32ceb5aac5b1ba73c84449c6afee630710bb
treef34db20cb2138a60ae57a2aac334cd6252e8d9c8
parent01607b54fc21c5dfc0ee8bc21ef317c8c03868c2

crypto/25519: add scalar.random(), use CompressedScalar type

Add the ability to generate a random, canonical curve25519 scalar, like we do for p256. Also leverage the existing CompressedScalar type to represent these scalars.

1 files changed, 60 insertions(+), 19 deletions(-)

lib/std/crypto/25519/scalar.zig+60-19
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const crypto = std.crypto;
23const mem = std.mem;
34
45const NonCanonicalError = std.crypto.errors.NonCanonicalError;
......@@ -15,7 +16,7 @@ pub const CompressedScalar = [32]u8;
1516pub const zero = [_]u8{0} ** 32;
1617
1718/// Reject a scalar whose encoding is not canonical.
18pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {
19pub fn rejectNonCanonical(s: CompressedScalar) NonCanonicalError!void {
1920 var c: u8 = 0;
2021 var n: u8 = 1;
2122 var i: usize = 31;
......@@ -32,34 +33,34 @@ pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {
3233}
3334
3435/// Reduce a scalar to the field size.
35pub fn reduce(s: [32]u8) [32]u8 {
36pub fn reduce(s: CompressedScalar) CompressedScalar {
3637 return Scalar.fromBytes(s).toBytes();
3738}
3839
3940/// Reduce a 64-bytes scalar to the field size.
40pub fn reduce64(s: [64]u8) [32]u8 {
41pub fn reduce64(s: [64]u8) CompressedScalar {
4142 return ScalarDouble.fromBytes64(s).toBytes();
4243}
4344
4445/// Perform the X25519 "clamping" operation.
4546/// The scalar is then guaranteed to be a multiple of the cofactor.
46pub inline fn clamp(s: *[32]u8) void {
47pub inline fn clamp(s: *CompressedScalar) void {
4748 s[0] &= 248;
4849 s[31] = (s[31] & 127) | 64;
4950}
5051
5152/// Return a*b (mod L)
52pub fn mul(a: [32]u8, b: [32]u8) [32]u8 {
53pub fn mul(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
5354 return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).toBytes();
5455}
5556
5657/// Return a*b+c (mod L)
57pub fn mulAdd(a: [32]u8, b: [32]u8, c: [32]u8) [32]u8 {
58pub fn mulAdd(a: CompressedScalar, b: CompressedScalar, c: CompressedScalar) CompressedScalar {
5859 return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).add(Scalar.fromBytes(c)).toBytes();
5960}
6061
6162/// Return a*8 (mod L)
62pub fn mul8(s: [32]u8) [32]u8 {
63pub fn mul8(s: CompressedScalar) CompressedScalar {
6364 var x = Scalar.fromBytes(s);
6465 x = x.add(x);
6566 x = x.add(x);
......@@ -68,12 +69,12 @@ pub fn mul8(s: [32]u8) [32]u8 {
6869}
6970
7071/// Return a+b (mod L)
71pub fn add(a: [32]u8, b: [32]u8) [32]u8 {
72pub fn add(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
7273 return Scalar.fromBytes(a).add(Scalar.fromBytes(b)).toBytes();
7374}
7475
7576/// Return -s (mod L)
76pub fn neg(s: [32]u8) [32]u8 {
77pub fn neg(s: CompressedScalar) CompressedScalar {
7778 const fs: [64]u8 = field_size ++ [_]u8{0} ** 32;
7879 var sx: [64]u8 = undefined;
7980 mem.copy(u8, sx[0..32], s[0..]);
......@@ -89,23 +90,33 @@ pub fn neg(s: [32]u8) [32]u8 {
8990}
9091
9192/// Return (a-b) (mod L)
92pub fn sub(a: [32]u8, b: [32]u8) [32]u8 {
93pub fn sub(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
9394 return add(a, neg(b));
9495}
9596
97/// Return a random scalar < L
98pub fn random() CompressedScalar {
99 return Scalar.random().toBytes();
100}
101
96102/// A scalar in unpacked representation
97103pub const Scalar = struct {
98104 const Limbs = [5]u64;
99105 limbs: Limbs = undefined,
100106
101107 /// Unpack a 32-byte representation of a scalar
102 pub fn fromBytes(bytes: [32]u8) Scalar {
108 pub fn fromBytes(bytes: CompressedScalar) Scalar {
103109 return ScalarDouble.fromBytes32(bytes).reduce(5);
104110 }
105111
112 /// Unpack a 64-byte representation of a scalar
113 pub fn fromBytes64(bytes: [64]u8) Scalar {
114 return ScalarDouble.fromBytes64(bytes).reduce(5);
115 }
116
106117 /// Pack a scalar into bytes
107 pub fn toBytes(expanded: *const Scalar) [32]u8 {
108 var bytes: [32]u8 = undefined;
118 pub fn toBytes(expanded: *const Scalar) CompressedScalar {
119 var bytes: CompressedScalar = undefined;
109120 var i: usize = 0;
110121 while (i < 4) : (i += 1) {
111122 mem.writeIntLittle(u64, bytes[i * 7 ..][0..8], expanded.limbs[i]);
......@@ -114,7 +125,13 @@ pub const Scalar = struct {
114125 return bytes;
115126 }
116127
117 /// Return x+y (mod l)
128 /// Return true if the scalar is zero
129 pub fn isZero(n: Scalar) bool {
130 const limbs = n.limbs;
131 return (limbs[0] | limbs[1] | limbs[2] | limbs[3] | limbs[4]) == 0;
132 }
133
134 /// Return x+y (mod L)
118135 pub fn add(x: Scalar, y: Scalar) Scalar {
119136 const carry0 = (x.limbs[0] + y.limbs[0]) >> 56;
120137 const t0 = (x.limbs[0] + y.limbs[0]) & 0xffffffffffffff;
......@@ -171,7 +188,7 @@ pub const Scalar = struct {
171188 return Scalar{ .limbs = .{ z00, z10, z20, z30, z40 } };
172189 }
173190
174 /// Return x*r (mod l)
191 /// Return x*r (mod L)
175192 pub fn mul(x: Scalar, y: Scalar) Scalar {
176193 const xy000 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[0]);
177194 const xy010 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[1]);
......@@ -483,7 +500,7 @@ pub const Scalar = struct {
483500 return Scalar{ .limbs = .{ z04, z14, z24, z34, z44 } };
484501 }
485502
486 /// Return x^2 (mod l)
503 /// Return x^2 (mod L)
487504 pub fn sq(x: Scalar) Scalar {
488505 return x.mul(x);
489506 }
......@@ -503,7 +520,7 @@ pub const Scalar = struct {
503520 return x.sqn(n).mul(y);
504521 }
505522
506 /// Return the inverse of a scalar (mod l), or 0 if x=0.
523 /// Return the inverse of a scalar (mod L), or 0 if x=0.
507524 pub fn invert(x: Scalar) Scalar {
508525 const _10 = x.sq();
509526 const _11 = x.mul(_10);
......@@ -533,6 +550,18 @@ pub const Scalar = struct {
533550 .sqn_mul(9, _1101011).sqn_mul(6, _1011).sqn_mul(14, _10010011).sqn_mul(10, _1100011)
534551 .sqn_mul(9, _10010111).sqn_mul(10, _11110101).sqn_mul(8, _11010011).sqn_mul(8, _11101011);
535552 }
553
554 /// Return a random scalar < L.
555 pub fn random() Scalar {
556 var s: [64]u8 = undefined;
557 while (true) {
558 crypto.random.bytes(&s);
559 const n = Scalar.fromBytes64(s);
560 if (!n.isZero()) {
561 return n;
562 }
563 }
564 }
536565};
537566
538567const ScalarDouble = struct {
......@@ -549,7 +578,7 @@ const ScalarDouble = struct {
549578 return ScalarDouble{ .limbs = limbs };
550579 }
551580
552 fn fromBytes32(bytes: [32]u8) ScalarDouble {
581 fn fromBytes32(bytes: CompressedScalar) ScalarDouble {
553582 var limbs: Limbs = undefined;
554583 var i: usize = 0;
555584 while (i < 4) : (i += 1) {
......@@ -560,7 +589,7 @@ const ScalarDouble = struct {
560589 return ScalarDouble{ .limbs = limbs };
561590 }
562591
563 fn toBytes(expanded_double: *ScalarDouble) [32]u8 {
592 fn toBytes(expanded_double: *ScalarDouble) CompressedScalar {
564593 return expanded_double.reduce(10).toBytes();
565594 }
566595
......@@ -840,3 +869,15 @@ test "scalar field inversion" {
840869 const recovered_x = inv.invert();
841870 try std.testing.expectEqualSlices(u8, &bytes, &recovered_x.toBytes());
842871}
872
873test "random scalar" {
874 const s1 = random();
875 const s2 = random();
876 try std.testing.expect(!mem.eql(u8, &s1, &s2));
877}
878
879test "64-bit reduction" {
880 const bytes = field_size ++ [_]u8{0} ** 32;
881 const x = Scalar.fromBytes64(bytes);
882 try std.testing.expect(x.isZero());
883}