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 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const crypto = std.crypto;
2const mem = std.mem;3const mem = std.mem;
34
4const NonCanonicalError = std.crypto.errors.NonCanonicalError;5const NonCanonicalError = std.crypto.errors.NonCanonicalError;
...@@ -15,7 +16,7 @@ pub const CompressedScalar = [32]u8;...@@ -15,7 +16,7 @@ pub const CompressedScalar = [32]u8;
15pub const zero = [_]u8{0} ** 32;16pub const zero = [_]u8{0} ** 32;
1617
17/// Reject a scalar whose encoding is not canonical.18/// Reject a scalar whose encoding is not canonical.
18pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {19pub fn rejectNonCanonical(s: CompressedScalar) NonCanonicalError!void {
19 var c: u8 = 0;20 var c: u8 = 0;
20 var n: u8 = 1;21 var n: u8 = 1;
21 var i: usize = 31;22 var i: usize = 31;
...@@ -32,34 +33,34 @@ pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {...@@ -32,34 +33,34 @@ pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {
32}33}
3334
34/// Reduce a scalar to the field size.35/// Reduce a scalar to the field size.
35pub fn reduce(s: [32]u8) [32]u8 {36pub fn reduce(s: CompressedScalar) CompressedScalar {
36 return Scalar.fromBytes(s).toBytes();37 return Scalar.fromBytes(s).toBytes();
37}38}
3839
39/// Reduce a 64-bytes scalar to the field size.40/// Reduce a 64-bytes scalar to the field size.
40pub fn reduce64(s: [64]u8) [32]u8 {41pub fn reduce64(s: [64]u8) CompressedScalar {
41 return ScalarDouble.fromBytes64(s).toBytes();42 return ScalarDouble.fromBytes64(s).toBytes();
42}43}
4344
44/// Perform the X25519 "clamping" operation.45/// Perform the X25519 "clamping" operation.
45/// The scalar is then guaranteed to be a multiple of the cofactor.46/// 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 {
47 s[0] &= 248;48 s[0] &= 248;
48 s[31] = (s[31] & 127) | 64;49 s[31] = (s[31] & 127) | 64;
49}50}
5051
51/// Return a*b (mod L)52/// Return a*b (mod L)
52pub fn mul(a: [32]u8, b: [32]u8) [32]u8 {53pub fn mul(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
53 return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).toBytes();54 return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).toBytes();
54}55}
5556
56/// Return a*b+c (mod L)57/// 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 {
58 return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).add(Scalar.fromBytes(c)).toBytes();59 return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).add(Scalar.fromBytes(c)).toBytes();
59}60}
6061
61/// Return a*8 (mod L)62/// Return a*8 (mod L)
62pub fn mul8(s: [32]u8) [32]u8 {63pub fn mul8(s: CompressedScalar) CompressedScalar {
63 var x = Scalar.fromBytes(s);64 var x = Scalar.fromBytes(s);
64 x = x.add(x);65 x = x.add(x);
65 x = x.add(x);66 x = x.add(x);
...@@ -68,12 +69,12 @@ pub fn mul8(s: [32]u8) [32]u8 {...@@ -68,12 +69,12 @@ pub fn mul8(s: [32]u8) [32]u8 {
68}69}
6970
70/// Return a+b (mod L)71/// Return a+b (mod L)
71pub fn add(a: [32]u8, b: [32]u8) [32]u8 {72pub fn add(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
72 return Scalar.fromBytes(a).add(Scalar.fromBytes(b)).toBytes();73 return Scalar.fromBytes(a).add(Scalar.fromBytes(b)).toBytes();
73}74}
7475
75/// Return -s (mod L)76/// Return -s (mod L)
76pub fn neg(s: [32]u8) [32]u8 {77pub fn neg(s: CompressedScalar) CompressedScalar {
77 const fs: [64]u8 = field_size ++ [_]u8{0} ** 32;78 const fs: [64]u8 = field_size ++ [_]u8{0} ** 32;
78 var sx: [64]u8 = undefined;79 var sx: [64]u8 = undefined;
79 mem.copy(u8, sx[0..32], s[0..]);80 mem.copy(u8, sx[0..32], s[0..]);
...@@ -89,23 +90,33 @@ pub fn neg(s: [32]u8) [32]u8 {...@@ -89,23 +90,33 @@ pub fn neg(s: [32]u8) [32]u8 {
89}90}
9091
91/// Return (a-b) (mod L)92/// Return (a-b) (mod L)
92pub fn sub(a: [32]u8, b: [32]u8) [32]u8 {93pub fn sub(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
93 return add(a, neg(b));94 return add(a, neg(b));
94}95}
9596
97/// Return a random scalar < L
98pub fn random() CompressedScalar {
99 return Scalar.random().toBytes();
100}
101
96/// A scalar in unpacked representation102/// A scalar in unpacked representation
97pub const Scalar = struct {103pub const Scalar = struct {
98 const Limbs = [5]u64;104 const Limbs = [5]u64;
99 limbs: Limbs = undefined,105 limbs: Limbs = undefined,
100106
101 /// Unpack a 32-byte representation of a scalar107 /// Unpack a 32-byte representation of a scalar
102 pub fn fromBytes(bytes: [32]u8) Scalar {108 pub fn fromBytes(bytes: CompressedScalar) Scalar {
103 return ScalarDouble.fromBytes32(bytes).reduce(5);109 return ScalarDouble.fromBytes32(bytes).reduce(5);
104 }110 }
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
106 /// Pack a scalar into bytes117 /// Pack a scalar into bytes
107 pub fn toBytes(expanded: *const Scalar) [32]u8 {118 pub fn toBytes(expanded: *const Scalar) CompressedScalar {
108 var bytes: [32]u8 = undefined;119 var bytes: CompressedScalar = undefined;
109 var i: usize = 0;120 var i: usize = 0;
110 while (i < 4) : (i += 1) {121 while (i < 4) : (i += 1) {
111 mem.writeIntLittle(u64, bytes[i * 7 ..][0..8], expanded.limbs[i]);122 mem.writeIntLittle(u64, bytes[i * 7 ..][0..8], expanded.limbs[i]);
...@@ -114,7 +125,13 @@ pub const Scalar = struct {...@@ -114,7 +125,13 @@ pub const Scalar = struct {
114 return bytes;125 return bytes;
115 }126 }
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)
118 pub fn add(x: Scalar, y: Scalar) Scalar {135 pub fn add(x: Scalar, y: Scalar) Scalar {
119 const carry0 = (x.limbs[0] + y.limbs[0]) >> 56;136 const carry0 = (x.limbs[0] + y.limbs[0]) >> 56;
120 const t0 = (x.limbs[0] + y.limbs[0]) & 0xffffffffffffff;137 const t0 = (x.limbs[0] + y.limbs[0]) & 0xffffffffffffff;
...@@ -171,7 +188,7 @@ pub const Scalar = struct {...@@ -171,7 +188,7 @@ pub const Scalar = struct {
171 return Scalar{ .limbs = .{ z00, z10, z20, z30, z40 } };188 return Scalar{ .limbs = .{ z00, z10, z20, z30, z40 } };
172 }189 }
173190
174 /// Return x*r (mod l)191 /// Return x*r (mod L)
175 pub fn mul(x: Scalar, y: Scalar) Scalar {192 pub fn mul(x: Scalar, y: Scalar) Scalar {
176 const xy000 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[0]);193 const xy000 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[0]);
177 const xy010 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[1]);194 const xy010 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[1]);
...@@ -483,7 +500,7 @@ pub const Scalar = struct {...@@ -483,7 +500,7 @@ pub const Scalar = struct {
483 return Scalar{ .limbs = .{ z04, z14, z24, z34, z44 } };500 return Scalar{ .limbs = .{ z04, z14, z24, z34, z44 } };
484 }501 }
485502
486 /// Return x^2 (mod l)503 /// Return x^2 (mod L)
487 pub fn sq(x: Scalar) Scalar {504 pub fn sq(x: Scalar) Scalar {
488 return x.mul(x);505 return x.mul(x);
489 }506 }
...@@ -503,7 +520,7 @@ pub const Scalar = struct {...@@ -503,7 +520,7 @@ pub const Scalar = struct {
503 return x.sqn(n).mul(y);520 return x.sqn(n).mul(y);
504 }521 }
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.
507 pub fn invert(x: Scalar) Scalar {524 pub fn invert(x: Scalar) Scalar {
508 const _10 = x.sq();525 const _10 = x.sq();
509 const _11 = x.mul(_10);526 const _11 = x.mul(_10);
...@@ -533,6 +550,18 @@ pub const Scalar = struct {...@@ -533,6 +550,18 @@ pub const Scalar = struct {
533 .sqn_mul(9, _1101011).sqn_mul(6, _1011).sqn_mul(14, _10010011).sqn_mul(10, _1100011)550 .sqn_mul(9, _1101011).sqn_mul(6, _1011).sqn_mul(14, _10010011).sqn_mul(10, _1100011)
534 .sqn_mul(9, _10010111).sqn_mul(10, _11110101).sqn_mul(8, _11010011).sqn_mul(8, _11101011);551 .sqn_mul(9, _10010111).sqn_mul(10, _11110101).sqn_mul(8, _11010011).sqn_mul(8, _11101011);
535 }552 }
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 }
536};565};
537566
538const ScalarDouble = struct {567const ScalarDouble = struct {
...@@ -549,7 +578,7 @@ const ScalarDouble = struct {...@@ -549,7 +578,7 @@ const ScalarDouble = struct {
549 return ScalarDouble{ .limbs = limbs };578 return ScalarDouble{ .limbs = limbs };
550 }579 }
551580
552 fn fromBytes32(bytes: [32]u8) ScalarDouble {581 fn fromBytes32(bytes: CompressedScalar) ScalarDouble {
553 var limbs: Limbs = undefined;582 var limbs: Limbs = undefined;
554 var i: usize = 0;583 var i: usize = 0;
555 while (i < 4) : (i += 1) {584 while (i < 4) : (i += 1) {
...@@ -560,7 +589,7 @@ const ScalarDouble = struct {...@@ -560,7 +589,7 @@ const ScalarDouble = struct {
560 return ScalarDouble{ .limbs = limbs };589 return ScalarDouble{ .limbs = limbs };
561 }590 }
562591
563 fn toBytes(expanded_double: *ScalarDouble) [32]u8 {592 fn toBytes(expanded_double: *ScalarDouble) CompressedScalar {
564 return expanded_double.reduce(10).toBytes();593 return expanded_double.reduce(10).toBytes();
565 }594 }
566595
...@@ -840,3 +869,15 @@ test "scalar field inversion" {...@@ -840,3 +869,15 @@ test "scalar field inversion" {
840 const recovered_x = inv.invert();869 const recovered_x = inv.invert();
841 try std.testing.expectEqualSlices(u8, &bytes, &recovered_x.toBytes());870 try std.testing.expectEqualSlices(u8, &bytes, &recovered_x.toBytes());
842}871}
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}