| author | |
| committer | |
| log | addeff889a1017beeef8c710dfdd55dbda2449e3 |
| tree | 36beac5edb4683d4b14bd395addcf55f846e315d |
| parent | f46e375bbe0ac0893717bd477eab78f51863e277 |
| parent | 7f9a227abfbce0e67746cc57dd9b6a4bf0a8d94a |
closes #605010 files changed, 1315 insertions(+), 678 deletions(-)
lib/std/crypto.zig+13-2| ... | @@ -34,12 +34,17 @@ pub const chaCha20IETF = import_chaCha20.chaCha20IETF; | ... | @@ -34,12 +34,17 @@ pub const chaCha20IETF = import_chaCha20.chaCha20IETF; |
| 34 | pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; | 34 | pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; |
| 35 | 35 | ||
| 36 | pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; | 36 | pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; |
| 37 | pub const X25519 = @import("crypto/x25519.zig").X25519; | ||
| 38 | 37 | ||
| 39 | const import_aes = @import("crypto/aes.zig"); | 38 | const import_aes = @import("crypto/aes.zig"); |
| 40 | pub const AES128 = import_aes.AES128; | 39 | pub const AES128 = import_aes.AES128; |
| 41 | pub const AES256 = import_aes.AES256; | 40 | pub const AES256 = import_aes.AES256; |
| 42 | 41 | ||
| 42 | pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; | ||
| 43 | pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519; | ||
| 44 | pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519; | ||
| 45 | pub const X25519 = @import("crypto/25519/x25519.zig").X25519; | ||
| 46 | pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255; | ||
| 47 | |||
| 43 | const std = @import("std.zig"); | 48 | const std = @import("std.zig"); |
| 44 | pub const randomBytes = std.os.getrandom; | 49 | pub const randomBytes = std.os.getrandom; |
| 45 | 50 | ||
| ... | @@ -55,7 +60,13 @@ test "crypto" { | ... | @@ -55,7 +60,13 @@ test "crypto" { |
| 55 | _ = @import("crypto/sha1.zig"); | 60 | _ = @import("crypto/sha1.zig"); |
| 56 | _ = @import("crypto/sha2.zig"); | 61 | _ = @import("crypto/sha2.zig"); |
| 57 | _ = @import("crypto/sha3.zig"); | 62 | _ = @import("crypto/sha3.zig"); |
| 58 | _ = @import("crypto/x25519.zig"); | 63 | _ = @import("crypto/25519/curve25519.zig"); |
| 64 | _ = @import("crypto/25519/ed25519.zig"); | ||
| 65 | _ = @import("crypto/25519/edwards25519.zig"); | ||
| 66 | _ = @import("crypto/25519/field.zig"); | ||
| 67 | _ = @import("crypto/25519/scalar.zig"); | ||
| 68 | _ = @import("crypto/25519/x25519.zig"); | ||
| 69 | _ = @import("crypto/25519/ristretto255.zig"); | ||
| 59 | } | 70 | } |
| 60 | 71 | ||
| 61 | test "issue #4532: no index out of bounds" { | 72 | test "issue #4532: no index out of bounds" { |
lib/std/crypto/25519/curve25519.zig created+144| ... | @@ -0,0 +1,144 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | /// Group operations over Curve25519. | ||
| 4 | pub const Curve25519 = struct { | ||
| 5 | /// The underlying prime field. | ||
| 6 | pub const Fe = @import("field.zig").Fe; | ||
| 7 | /// Field arithmetic mod the order of the main subgroup. | ||
| 8 | pub const scalar = @import("scalar.zig"); | ||
| 9 | |||
| 10 | x: Fe, | ||
| 11 | |||
| 12 | /// Decode a Curve25519 point from its compressed (X) coordinates. | ||
| 13 | pub inline fn fromBytes(s: [32]u8) Curve25519 { | ||
| 14 | return .{ .x = Fe.fromBytes(s) }; | ||
| 15 | } | ||
| 16 | |||
| 17 | /// Encode a Curve25519 point. | ||
| 18 | pub inline fn toBytes(p: Curve25519) [32]u8 { | ||
| 19 | return p.x.toBytes(); | ||
| 20 | } | ||
| 21 | |||
| 22 | /// The Curve25519 base point. | ||
| 23 | pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint }; | ||
| 24 | |||
| 25 | /// Check that the encoding of a Curve25519 point is canonical. | ||
| 26 | pub fn rejectNonCanonical(s: [32]u8) !void { | ||
| 27 | return Fe.rejectNonCanonical(s, false); | ||
| 28 | } | ||
| 29 | |||
| 30 | /// Reject the neutral element. | ||
| 31 | pub fn rejectIdentity(p: Curve25519) !void { | ||
| 32 | if (p.x.isZero()) { | ||
| 33 | return error.IdentityElement; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) !Curve25519 { | ||
| 38 | var x1 = p.x; | ||
| 39 | var x2 = Fe.one; | ||
| 40 | var z2 = Fe.zero; | ||
| 41 | var x3 = x1; | ||
| 42 | var z3 = Fe.one; | ||
| 43 | var swap: u8 = 0; | ||
| 44 | var pos: usize = bits - 1; | ||
| 45 | while (true) : (pos -= 1) { | ||
| 46 | const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 1; | ||
| 47 | swap ^= bit; | ||
| 48 | Fe.cSwap2(&x2, &x3, &z2, &z3, swap); | ||
| 49 | swap = bit; | ||
| 50 | const a = x2.add(z2); | ||
| 51 | const b = x2.sub(z2); | ||
| 52 | const aa = a.sq(); | ||
| 53 | const bb = b.sq(); | ||
| 54 | x2 = aa.mul(bb); | ||
| 55 | const e = aa.sub(bb); | ||
| 56 | const da = x3.sub(z3).mul(a); | ||
| 57 | const cb = x3.add(z3).mul(b); | ||
| 58 | x3 = da.add(cb).sq(); | ||
| 59 | z3 = x1.mul(da.sub(cb).sq()); | ||
| 60 | z2 = e.mul(bb.add(e.mul32(121666))); | ||
| 61 | if (pos == 0) break; | ||
| 62 | } | ||
| 63 | Fe.cSwap2(&x2, &x3, &z2, &z3, swap); | ||
| 64 | z2 = z2.invert(); | ||
| 65 | x2 = x2.mul(z2); | ||
| 66 | if (x2.isZero()) { | ||
| 67 | return error.IdentityElement; | ||
| 68 | } | ||
| 69 | return Curve25519{ .x = x2 }; | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Multiply a Curve25519 point by a scalar after "clamping" it. | ||
| 73 | /// Clamping forces the scalar to be a multiple of the cofactor in | ||
| 74 | /// order to prevent small subgroups attacks. This is the standard | ||
| 75 | /// way to use Curve25519 for a DH operation. | ||
| 76 | /// Return error.IdentityElement if the resulting point is | ||
| 77 | /// the identity element. | ||
| 78 | pub fn clampedMul(p: Curve25519, s: [32]u8) !Curve25519 { | ||
| 79 | var t: [32]u8 = s; | ||
| 80 | scalar.clamp(&t); | ||
| 81 | return try ladder(p, t, 255); | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Multiply a Curve25519 point by a scalar without clamping it. | ||
| 85 | /// Return error.IdentityElement if the resulting point is | ||
| 86 | /// the identity element or error.WeakPublicKey if the public | ||
| 87 | /// key is a low-order point. | ||
| 88 | pub fn mul(p: Curve25519, s: [32]u8) !Curve25519 { | ||
| 89 | const cofactor = [_]u8{8} ++ [_]u8{0} ** 31; | ||
| 90 | _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey; | ||
| 91 | return try ladder(p, s, 256); | ||
| 92 | } | ||
| 93 | }; | ||
| 94 | |||
| 95 | test "curve25519" { | ||
| 96 | var s = [32]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 }; | ||
| 97 | const p = try Curve25519.basePoint.clampedMul(s); | ||
| 98 | try p.rejectIdentity(); | ||
| 99 | var buf: [128]u8 = undefined; | ||
| 100 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145"); | ||
| 101 | const q = try p.clampedMul(s); | ||
| 102 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537"); | ||
| 103 | |||
| 104 | try Curve25519.rejectNonCanonical(s); | ||
| 105 | s[31] |= 0x80; | ||
| 106 | std.testing.expectError(error.NonCanonical, Curve25519.rejectNonCanonical(s)); | ||
| 107 | } | ||
| 108 | |||
| 109 | test "curve25519 small order check" { | ||
| 110 | var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31; | ||
| 111 | const small_order_ss: [7][32]u8 = .{ | ||
| 112 | .{ | ||
| 113 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 (order 4) | ||
| 114 | }, | ||
| 115 | .{ | ||
| 116 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 (order 1) | ||
| 117 | }, | ||
| 118 | .{ | ||
| 119 | 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00, // 325606250916557431795983626356110631294008115727848805560023387167927233504 (order 8) */ | ||
| 120 | }, | ||
| 121 | .{ | ||
| 122 | 0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57, // 39382357235489614581723060781553021112529911719440698176882885853963445705823 (order 8) | ||
| 123 | }, | ||
| 124 | .{ | ||
| 125 | 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p-1 (order 2) | ||
| 126 | }, | ||
| 127 | .{ | ||
| 128 | 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p (=0, order 4) | ||
| 129 | }, | ||
| 130 | .{ | ||
| 131 | 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p+1 (=1, order 1) | ||
| 132 | }, | ||
| 133 | }; | ||
| 134 | for (small_order_ss) |small_order_s| { | ||
| 135 | std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).mul(s)); | ||
| 136 | var extra = small_order_s; | ||
| 137 | extra[31] ^= 0x80; | ||
| 138 | std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(extra).mul(s)); | ||
| 139 | var valid = small_order_s; | ||
| 140 | valid[31] = 0x40; | ||
| 141 | s[0] = 0; | ||
| 142 | std.testing.expectError(error.IdentityElement, Curve25519.fromBytes(valid).mul(s)); | ||
| 143 | } | ||
| 144 | } | ||
lib/std/crypto/25519/ed25519.zig created+134| ... | @@ -0,0 +1,134 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const fmt = std.fmt; | ||
| 3 | const mem = std.mem; | ||
| 4 | const Sha512 = std.crypto.Sha512; | ||
| 5 | |||
| 6 | /// Ed25519 (EdDSA) signatures. | ||
| 7 | pub const Ed25519 = struct { | ||
| 8 | /// The underlying elliptic curve. | ||
| 9 | pub const Curve = @import("edwards25519.zig").Edwards25519; | ||
| 10 | /// Length (in bytes) of a seed required to create a key pair. | ||
| 11 | pub const seed_length = 32; | ||
| 12 | /// Length (in bytes) of a compressed key pair. | ||
| 13 | pub const keypair_length = 64; | ||
| 14 | /// Length (in bytes) of a compressed public key. | ||
| 15 | pub const public_length = 32; | ||
| 16 | /// Length (in bytes) of a signature. | ||
| 17 | pub const signature_length = 64; | ||
| 18 | /// Length (in bytes) of optional random bytes, for non-deterministic signatures. | ||
| 19 | pub const noise_length = 32; | ||
| 20 | |||
| 21 | /// Derive a key pair from a secret seed. | ||
| 22 | /// | ||
| 23 | /// As in RFC 8032, an Ed25519 public key is generated by hashing | ||
| 24 | /// the secret key using the SHA-512 function, and interpreting the | ||
| 25 | /// bit-swapped, clamped lower-half of the output as the secret scalar. | ||
| 26 | /// | ||
| 27 | /// For this reason, an EdDSA secret key is commonly called a seed, | ||
| 28 | /// from which the actual secret is derived. | ||
| 29 | pub fn createKeyPair(seed: [seed_length]u8) ![keypair_length]u8 { | ||
| 30 | var az: [Sha512.digest_length]u8 = undefined; | ||
| 31 | var h = Sha512.init(); | ||
| 32 | h.update(&seed); | ||
| 33 | h.final(&az); | ||
| 34 | const p = try Curve.basePoint.clampedMul(az[0..32].*); | ||
| 35 | var keypair: [keypair_length]u8 = undefined; | ||
| 36 | mem.copy(u8, &keypair, &seed); | ||
| 37 | mem.copy(u8, keypair[seed_length..], &p.toBytes()); | ||
| 38 | return keypair; | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Return the public key for a given key pair. | ||
| 42 | pub fn publicKey(key_pair: [keypair_length]u8) [public_length]u8 { | ||
| 43 | var public_key: [public_length]u8 = undefined; | ||
| 44 | mem.copy(u8, public_key[0..], key_pair[seed_length..]); | ||
| 45 | return public_key; | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Sign a message using a key pair, and optional random noise. | ||
| 49 | /// Having noise creates non-standard, non-deterministic signatures, | ||
| 50 | /// but has been proven to increase resilience against fault attacks. | ||
| 51 | pub fn sign(msg: []const u8, key_pair: [keypair_length]u8, noise: ?[noise_length]u8) ![signature_length]u8 { | ||
| 52 | const public_key = key_pair[32..]; | ||
| 53 | var az: [Sha512.digest_length]u8 = undefined; | ||
| 54 | var h = Sha512.init(); | ||
| 55 | h.update(key_pair[0..seed_length]); | ||
| 56 | h.final(&az); | ||
| 57 | |||
| 58 | h = Sha512.init(); | ||
| 59 | if (noise) |*z| { | ||
| 60 | h.update(z); | ||
| 61 | } | ||
| 62 | h.update(az[32..]); | ||
| 63 | h.update(msg); | ||
| 64 | var nonce64: [64]u8 = undefined; | ||
| 65 | h.final(&nonce64); | ||
| 66 | const nonce = Curve.scalar.reduce64(nonce64); | ||
| 67 | const r = try Curve.basePoint.mul(nonce); | ||
| 68 | |||
| 69 | var sig: [signature_length]u8 = undefined; | ||
| 70 | mem.copy(u8, sig[0..32], &r.toBytes()); | ||
| 71 | mem.copy(u8, sig[32..], public_key); | ||
| 72 | h = Sha512.init(); | ||
| 73 | h.update(&sig); | ||
| 74 | h.update(msg); | ||
| 75 | var hram64: [Sha512.digest_length]u8 = undefined; | ||
| 76 | h.final(&hram64); | ||
| 77 | const hram = Curve.scalar.reduce64(hram64); | ||
| 78 | |||
| 79 | var x = az[0..32]; | ||
| 80 | Curve.scalar.clamp(x); | ||
| 81 | const s = Curve.scalar.mulAdd(hram, x.*, nonce); | ||
| 82 | mem.copy(u8, sig[32..], s[0..]); | ||
| 83 | return sig; | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Verify an Ed25519 signature given a message and a public key. | ||
| 87 | /// Returns error.InvalidSignature is the signature verification failed. | ||
| 88 | pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) !void { | ||
| 89 | const r = sig[0..32]; | ||
| 90 | const s = sig[32..64]; | ||
| 91 | try Curve.scalar.rejectNonCanonical(s.*); | ||
| 92 | try Curve.rejectNonCanonical(public_key); | ||
| 93 | const a = try Curve.fromBytes(public_key); | ||
| 94 | try a.rejectIdentity(); | ||
| 95 | |||
| 96 | var h = Sha512.init(); | ||
| 97 | h.update(r); | ||
| 98 | h.update(&public_key); | ||
| 99 | h.update(msg); | ||
| 100 | var hram64: [Sha512.digest_length]u8 = undefined; | ||
| 101 | h.final(&hram64); | ||
| 102 | const hram = Curve.scalar.reduce64(hram64); | ||
| 103 | |||
| 104 | const p = try a.neg().mul(hram); | ||
| 105 | const check = (try Curve.basePoint.mul(s.*)).add(p).toBytes(); | ||
| 106 | if (mem.eql(u8, &check, r) == false) { | ||
| 107 | return error.InvalidSignature; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | }; | ||
| 111 | |||
| 112 | test "ed25519 key pair creation" { | ||
| 113 | var seed: [32]u8 = undefined; | ||
| 114 | try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | ||
| 115 | const key_pair = try Ed25519.createKeyPair(seed); | ||
| 116 | var buf: [256]u8 = undefined; | ||
| 117 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); | ||
| 118 | |||
| 119 | const public_key = Ed25519.publicKey(key_pair); | ||
| 120 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); | ||
| 121 | } | ||
| 122 | |||
| 123 | test "ed25519 signature" { | ||
| 124 | var seed: [32]u8 = undefined; | ||
| 125 | try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | ||
| 126 | const key_pair = try Ed25519.createKeyPair(seed); | ||
| 127 | |||
| 128 | const sig = try Ed25519.sign("test", key_pair, null); | ||
| 129 | var buf: [128]u8 = undefined; | ||
| 130 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{sig}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808"); | ||
| 131 | const public_key = Ed25519.publicKey(key_pair); | ||
| 132 | try Ed25519.verify(sig, "test", public_key); | ||
| 133 | std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", public_key)); | ||
| 134 | } | ||
lib/std/crypto/25519/edwards25519.zig created+214| ... | @@ -0,0 +1,214 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const fmt = std.fmt; | ||
| 3 | |||
| 4 | /// Group operations over Edwards25519. | ||
| 5 | pub const Edwards25519 = struct { | ||
| 6 | /// The underlying prime field. | ||
| 7 | pub const Fe = @import("field.zig").Fe; | ||
| 8 | /// Field arithmetic mod the order of the main subgroup. | ||
| 9 | pub const scalar = @import("scalar.zig"); | ||
| 10 | |||
| 11 | x: Fe, | ||
| 12 | y: Fe, | ||
| 13 | z: Fe, | ||
| 14 | t: Fe, | ||
| 15 | |||
| 16 | is_base: bool = false, | ||
| 17 | |||
| 18 | /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates. | ||
| 19 | pub fn fromBytes(s: [32]u8) !Edwards25519 { | ||
| 20 | const z = Fe.one; | ||
| 21 | const y = Fe.fromBytes(s); | ||
| 22 | var u = y.sq(); | ||
| 23 | var v = u.mul(Fe.edwards25519d); | ||
| 24 | u = u.sub(z); | ||
| 25 | v = v.add(z); | ||
| 26 | const v3 = v.sq().mul(v); | ||
| 27 | var x = v3.sq().mul(v).mul(u).pow2523().mul(v3).mul(u); | ||
| 28 | const vxx = x.sq().mul(v); | ||
| 29 | const has_m_root = vxx.sub(u).isZero(); | ||
| 30 | const has_p_root = vxx.add(u).isZero(); | ||
| 31 | if ((@boolToInt(has_m_root) | @boolToInt(has_p_root)) == 0) { // best-effort to avoid two conditional branches | ||
| 32 | return error.InvalidEncoding; | ||
| 33 | } | ||
| 34 | x.cMov(x.mul(Fe.sqrtm1), 1 - @boolToInt(has_m_root)); | ||
| 35 | x.cMov(x.neg(), @boolToInt(x.isNegative()) ^ (s[31] >> 7)); | ||
| 36 | const t = x.mul(y); | ||
| 37 | return Edwards25519{ .x = x, .y = y, .z = z, .t = t }; | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Encode an Edwards25519 point. | ||
| 41 | pub fn toBytes(p: Edwards25519) [32]u8 { | ||
| 42 | const zi = p.z.invert(); | ||
| 43 | var s = p.y.mul(zi).toBytes(); | ||
| 44 | s[31] ^= @as(u8, @boolToInt(p.x.mul(zi).isNegative())) << 7; | ||
| 45 | return s; | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Check that the encoding of a point is canonical. | ||
| 49 | pub fn rejectNonCanonical(s: [32]u8) !void { | ||
| 50 | return Fe.rejectNonCanonical(s, true); | ||
| 51 | } | ||
| 52 | |||
| 53 | /// The edwards25519 base point. | ||
| 54 | pub const basePoint = Edwards25519{ | ||
| 55 | .x = Fe{ .limbs = .{ 3990542415680775, 3398198340507945, 4322667446711068, 2814063955482877, 2839572215813860 } }, | ||
| 56 | .y = Fe{ .limbs = .{ 1801439850948184, 1351079888211148, 450359962737049, 900719925474099, 1801439850948198 } }, | ||
| 57 | .z = Fe.one, | ||
| 58 | .t = Fe{ .limbs = .{ 1841354044333475, 16398895984059, 755974180946558, 900171276175154, 1821297809914039 } }, | ||
| 59 | .is_base = true, | ||
| 60 | }; | ||
| 61 | |||
| 62 | const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero }; | ||
| 63 | |||
| 64 | /// Reject the neutral element. | ||
| 65 | pub fn rejectIdentity(p: Edwards25519) !void { | ||
| 66 | if (p.x.isZero()) { | ||
| 67 | return error.IdentityElement; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Flip the sign of the X coordinate. | ||
| 72 | pub inline fn neg(p: Edwards25519) Edwards25519 { | ||
| 73 | return .{ .x = p.x.neg(), .y = p.y, .z = p.z, .t = p.t.neg() }; | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Double an Edwards25519 point. | ||
| 77 | pub fn dbl(p: Edwards25519) Edwards25519 { | ||
| 78 | const t0 = p.x.add(p.y).sq(); | ||
| 79 | var x = p.x.sq(); | ||
| 80 | var z = p.y.sq(); | ||
| 81 | const y = z.add(x); | ||
| 82 | z = z.sub(x); | ||
| 83 | x = t0.sub(y); | ||
| 84 | const t = p.z.sq2().sub(z); | ||
| 85 | return .{ | ||
| 86 | .x = x.mul(t), | ||
| 87 | .y = y.mul(z), | ||
| 88 | .z = z.mul(t), | ||
| 89 | .t = x.mul(y), | ||
| 90 | }; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Add two Edwards25519 points. | ||
| 94 | pub fn add(p: Edwards25519, q: Edwards25519) Edwards25519 { | ||
| 95 | const a = p.y.sub(p.x).mul(q.y.sub(q.x)); | ||
| 96 | const b = p.x.add(p.y).mul(q.x.add(q.y)); | ||
| 97 | const c = p.t.mul(q.t).mul(Fe.edwards25519d2); | ||
| 98 | var d = p.z.mul(q.z); | ||
| 99 | d = d.add(d); | ||
| 100 | const x = b.sub(a); | ||
| 101 | const y = b.add(a); | ||
| 102 | const z = d.add(c); | ||
| 103 | const t = d.sub(c); | ||
| 104 | return .{ | ||
| 105 | .x = x.mul(t), | ||
| 106 | .y = y.mul(z), | ||
| 107 | .z = z.mul(t), | ||
| 108 | .t = x.mul(y), | ||
| 109 | }; | ||
| 110 | } | ||
| 111 | |||
| 112 | inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void { | ||
| 113 | p.x.cMov(a.x, c); | ||
| 114 | p.y.cMov(a.y, c); | ||
| 115 | p.z.cMov(a.z, c); | ||
| 116 | p.t.cMov(a.t, c); | ||
| 117 | } | ||
| 118 | |||
| 119 | inline fn pcSelect(pc: [16]Edwards25519, b: u8) Edwards25519 { | ||
| 120 | var t = Edwards25519.identityElement; | ||
| 121 | comptime var i: u8 = 0; | ||
| 122 | inline while (i < 16) : (i += 1) { | ||
| 123 | t.cMov(pc[i], ((@as(usize, b ^ i) -% 1) >> 8) & 1); | ||
| 124 | } | ||
| 125 | return t; | ||
| 126 | } | ||
| 127 | |||
| 128 | fn pcMul(pc: [16]Edwards25519, s: [32]u8) !Edwards25519 { | ||
| 129 | var q = Edwards25519.identityElement; | ||
| 130 | var pos: usize = 252; | ||
| 131 | while (true) : (pos -= 4) { | ||
| 132 | q = q.dbl().dbl().dbl().dbl(); | ||
| 133 | const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf; | ||
| 134 | q = q.add(pcSelect(pc, bit)); | ||
| 135 | if (pos == 0) break; | ||
| 136 | } | ||
| 137 | try q.rejectIdentity(); | ||
| 138 | return q; | ||
| 139 | } | ||
| 140 | |||
| 141 | fn precompute(p: Edwards25519) [16]Edwards25519 { | ||
| 142 | var pc: [16]Edwards25519 = undefined; | ||
| 143 | pc[0] = Edwards25519.identityElement; | ||
| 144 | pc[1] = p; | ||
| 145 | var i: usize = 2; | ||
| 146 | while (i < 16) : (i += 1) { | ||
| 147 | pc[i] = pc[i - 1].add(p); | ||
| 148 | } | ||
| 149 | return pc; | ||
| 150 | } | ||
| 151 | |||
| 152 | /// Multiply an Edwards25519 point by a scalar without clamping it. | ||
| 153 | /// Return error.WeakPublicKey if the resulting point is | ||
| 154 | /// the identity element. | ||
| 155 | pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 { | ||
| 156 | var pc: [16]Edwards25519 = undefined; | ||
| 157 | if (p.is_base) { | ||
| 158 | @setEvalBranchQuota(10000); | ||
| 159 | pc = comptime precompute(Edwards25519.basePoint); | ||
| 160 | } else { | ||
| 161 | pc = precompute(p); | ||
| 162 | pc[4].rejectIdentity() catch |_| return error.WeakPublicKey; | ||
| 163 | } | ||
| 164 | return pcMul(pc, s); | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Multiply an Edwards25519 point by a scalar after "clamping" it. | ||
| 168 | /// Clamping forces the scalar to be a multiple of the cofactor in | ||
| 169 | /// order to prevent small subgroups attacks. | ||
| 170 | /// This is strongly recommended for DH operations. | ||
| 171 | /// Return error.WeakPublicKey if the resulting point is | ||
| 172 | /// the identity element. | ||
| 173 | pub fn clampedMul(p: Edwards25519, s: [32]u8) !Edwards25519 { | ||
| 174 | var t: [32]u8 = s; | ||
| 175 | scalar.clamp(&t); | ||
| 176 | return mul(p, t); | ||
| 177 | } | ||
| 178 | }; | ||
| 179 | |||
| 180 | test "edwards25519 packing/unpacking" { | ||
| 181 | const s = [_]u8{170} ++ [_]u8{0} ** 31; | ||
| 182 | var b = Edwards25519.basePoint; | ||
| 183 | const pk = try b.mul(s); | ||
| 184 | var buf: [128]u8 = undefined; | ||
| 185 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{pk.toBytes()}), "074BC7E0FCBD587FDBC0969444245FADC562809C8F6E97E949AF62484B5B81A6"); | ||
| 186 | |||
| 187 | const small_order_ss: [7][32]u8 = .{ | ||
| 188 | .{ | ||
| 189 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 (order 4) | ||
| 190 | }, | ||
| 191 | .{ | ||
| 192 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 (order 1) | ||
| 193 | }, | ||
| 194 | .{ | ||
| 195 | 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05, // 270738550114484064931822528722565878893680426757531351946374360975030340202(order 8) | ||
| 196 | }, | ||
| 197 | .{ | ||
| 198 | 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a, // 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) | ||
| 199 | }, | ||
| 200 | .{ | ||
| 201 | 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p-1 (order 2) | ||
| 202 | }, | ||
| 203 | .{ | ||
| 204 | 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p (=0, order 4) | ||
| 205 | }, | ||
| 206 | .{ | ||
| 207 | 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p+1 (=1, order 1) | ||
| 208 | }, | ||
| 209 | }; | ||
| 210 | for (small_order_ss) |small_order_s| { | ||
| 211 | const small_p = try Edwards25519.fromBytes(small_order_s); | ||
| 212 | std.testing.expectError(error.WeakPublicKey, small_p.mul(s)); | ||
| 213 | } | ||
| 214 | } | ||
lib/std/crypto/25519/field.zig created+312| ... | @@ -0,0 +1,312 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const readIntLittle = std.mem.readIntLittle; | ||
| 3 | const writeIntLittle = std.mem.writeIntLittle; | ||
| 4 | |||
| 5 | pub const Fe = struct { | ||
| 6 | limbs: [5]u64, | ||
| 7 | |||
| 8 | const MASK51: u64 = 0x7ffffffffffff; | ||
| 9 | |||
| 10 | pub const zero = Fe{ .limbs = .{ 0, 0, 0, 0, 0 } }; | ||
| 11 | |||
| 12 | pub const one = Fe{ .limbs = .{ 1, 0, 0, 0, 0 } }; | ||
| 13 | |||
| 14 | pub const sqrtm1 = Fe{ .limbs = .{ 1718705420411056, 234908883556509, 2233514472574048, 2117202627021982, 765476049583133 } }; // sqrt(-1) | ||
| 15 | |||
| 16 | pub const curve25519BasePoint = Fe{ .limbs = .{ 9, 0, 0, 0, 0 } }; | ||
| 17 | |||
| 18 | pub const edwards25519d = Fe{ .limbs = .{ 929955233495203, 466365720129213, 1662059464998953, 2033849074728123, 1442794654840575 } }; // 37095705934669439343138083508754565189542113879843219016388785533085940283555 | ||
| 19 | |||
| 20 | pub const edwards25519d2 = Fe{ .limbs = .{ 1859910466990425, 932731440258426, 1072319116312658, 1815898335770999, 633789495995903 } }; // 2d | ||
| 21 | |||
| 22 | pub const edwards25519sqrtamd = Fe{ .limbs = .{ 278908739862762, 821645201101625, 8113234426968, 1777959178193151, 2118520810568447 } }; // 1/sqrt(a-d) | ||
| 23 | |||
| 24 | pub inline fn isZero(fe: Fe) bool { | ||
| 25 | var reduced = fe; | ||
| 26 | reduced.reduce(); | ||
| 27 | const limbs = reduced.limbs; | ||
| 28 | return (limbs[0] | limbs[1] | limbs[2] | limbs[3] | limbs[4]) == 0; | ||
| 29 | } | ||
| 30 | |||
| 31 | pub inline fn equivalent(a: Fe, b: Fe) bool { | ||
| 32 | return a.sub(b).isZero(); | ||
| 33 | } | ||
| 34 | |||
| 35 | pub fn fromBytes(s: [32]u8) Fe { | ||
| 36 | var fe: Fe = undefined; | ||
| 37 | fe.limbs[0] = readIntLittle(u64, s[0..8]) & MASK51; | ||
| 38 | fe.limbs[1] = (readIntLittle(u64, s[6..14]) >> 3) & MASK51; | ||
| 39 | fe.limbs[2] = (readIntLittle(u64, s[12..20]) >> 6) & MASK51; | ||
| 40 | fe.limbs[3] = (readIntLittle(u64, s[19..27]) >> 1) & MASK51; | ||
| 41 | fe.limbs[4] = (readIntLittle(u64, s[24..32]) >> 12) & MASK51; | ||
| 42 | |||
| 43 | return fe; | ||
| 44 | } | ||
| 45 | |||
| 46 | pub fn toBytes(fe: Fe) [32]u8 { | ||
| 47 | var reduced = fe; | ||
| 48 | reduced.reduce(); | ||
| 49 | var s: [32]u8 = undefined; | ||
| 50 | writeIntLittle(u64, s[0..8], reduced.limbs[0] | (reduced.limbs[1] << 51)); | ||
| 51 | writeIntLittle(u64, s[8..16], (reduced.limbs[1] >> 13) | (reduced.limbs[2] << 38)); | ||
| 52 | writeIntLittle(u64, s[16..24], (reduced.limbs[2] >> 26) | (reduced.limbs[3] << 25)); | ||
| 53 | writeIntLittle(u64, s[24..32], (reduced.limbs[3] >> 39) | (reduced.limbs[4] << 12)); | ||
| 54 | |||
| 55 | return s; | ||
| 56 | } | ||
| 57 | |||
| 58 | pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) !void { | ||
| 59 | var c: u16 = (s[31] & 0x7f) ^ 0x7f; | ||
| 60 | comptime var i = 30; | ||
| 61 | inline while (i > 0) : (i -= 1) { | ||
| 62 | c |= s[i] ^ 0xff; | ||
| 63 | } | ||
| 64 | c = (c -% 1) >> 8; | ||
| 65 | const d = (@as(u16, 0xed - 1) -% @as(u16, s[0])) >> 8; | ||
| 66 | const x = if (ignore_extra_bit) 0 else s[31] >> 7; | ||
| 67 | if ((((c & d) | x) & 1) != 0) { | ||
| 68 | return error.NonCanonical; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | fn reduce(fe: *Fe) void { | ||
| 73 | comptime var i = 0; | ||
| 74 | comptime var j = 0; | ||
| 75 | const limbs = &fe.limbs; | ||
| 76 | inline while (j < 2) : (j += 1) { | ||
| 77 | i = 0; | ||
| 78 | inline while (i < 4) : (i += 1) { | ||
| 79 | limbs[i + 1] += limbs[i] >> 51; | ||
| 80 | limbs[i] &= MASK51; | ||
| 81 | } | ||
| 82 | limbs[0] += 19 * (limbs[4] >> 51); | ||
| 83 | limbs[4] &= MASK51; | ||
| 84 | } | ||
| 85 | limbs[0] += 19; | ||
| 86 | i = 0; | ||
| 87 | inline while (i < 4) : (i += 1) { | ||
| 88 | limbs[i + 1] += limbs[i] >> 51; | ||
| 89 | limbs[i] &= MASK51; | ||
| 90 | } | ||
| 91 | limbs[0] += 19 * (limbs[4] >> 51); | ||
| 92 | limbs[4] &= MASK51; | ||
| 93 | |||
| 94 | limbs[0] += 0x8000000000000 - 19; | ||
| 95 | limbs[1] += 0x8000000000000 - 1; | ||
| 96 | limbs[2] += 0x8000000000000 - 1; | ||
| 97 | limbs[3] += 0x8000000000000 - 1; | ||
| 98 | limbs[4] += 0x8000000000000 - 1; | ||
| 99 | |||
| 100 | i = 0; | ||
| 101 | inline while (i < 4) : (i += 1) { | ||
| 102 | limbs[i + 1] += limbs[i] >> 51; | ||
| 103 | limbs[i] &= MASK51; | ||
| 104 | } | ||
| 105 | limbs[4] &= MASK51; | ||
| 106 | } | ||
| 107 | |||
| 108 | pub inline fn add(a: Fe, b: Fe) Fe { | ||
| 109 | var fe: Fe = undefined; | ||
| 110 | comptime var i = 0; | ||
| 111 | inline while (i < 5) : (i += 1) { | ||
| 112 | fe.limbs[i] = a.limbs[i] + b.limbs[i]; | ||
| 113 | } | ||
| 114 | return fe; | ||
| 115 | } | ||
| 116 | |||
| 117 | pub inline fn sub(a: Fe, b: Fe) Fe { | ||
| 118 | var fe = b; | ||
| 119 | comptime var i = 0; | ||
| 120 | inline while (i < 4) : (i += 1) { | ||
| 121 | fe.limbs[i + 1] += fe.limbs[i] >> 51; | ||
| 122 | fe.limbs[i] &= MASK51; | ||
| 123 | } | ||
| 124 | fe.limbs[0] += 19 * (fe.limbs[4] >> 51); | ||
| 125 | fe.limbs[4] &= MASK51; | ||
| 126 | fe.limbs[0] = (a.limbs[0] + 0xfffffffffffda) - fe.limbs[0]; | ||
| 127 | fe.limbs[1] = (a.limbs[1] + 0xffffffffffffe) - fe.limbs[1]; | ||
| 128 | fe.limbs[2] = (a.limbs[2] + 0xffffffffffffe) - fe.limbs[2]; | ||
| 129 | fe.limbs[3] = (a.limbs[3] + 0xffffffffffffe) - fe.limbs[3]; | ||
| 130 | fe.limbs[4] = (a.limbs[4] + 0xffffffffffffe) - fe.limbs[4]; | ||
| 131 | |||
| 132 | return fe; | ||
| 133 | } | ||
| 134 | |||
| 135 | pub inline fn neg(a: Fe) Fe { | ||
| 136 | return zero.sub(a); | ||
| 137 | } | ||
| 138 | |||
| 139 | pub inline fn isNegative(a: Fe) bool { | ||
| 140 | return (a.toBytes()[0] & 1) != 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | pub inline fn cMov(fe: *Fe, a: Fe, c: u64) void { | ||
| 144 | const mask: u64 = 0 -% c; | ||
| 145 | var x = fe.*; | ||
| 146 | comptime var i = 0; | ||
| 147 | inline while (i < 5) : (i += 1) { | ||
| 148 | x.limbs[i] ^= a.limbs[i]; | ||
| 149 | } | ||
| 150 | i = 0; | ||
| 151 | inline while (i < 5) : (i += 1) { | ||
| 152 | x.limbs[i] &= mask; | ||
| 153 | } | ||
| 154 | i = 0; | ||
| 155 | inline while (i < 5) : (i += 1) { | ||
| 156 | fe.limbs[i] ^= x.limbs[i]; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | pub fn cSwap2(a0: *Fe, b0: *Fe, a1: *Fe, b1: *Fe, c: u64) void { | ||
| 161 | const mask: u64 = 0 -% c; | ||
| 162 | var x0 = a0.*; | ||
| 163 | var x1 = a1.*; | ||
| 164 | comptime var i = 0; | ||
| 165 | inline while (i < 5) : (i += 1) { | ||
| 166 | x0.limbs[i] ^= b0.limbs[i]; | ||
| 167 | x1.limbs[i] ^= b1.limbs[i]; | ||
| 168 | } | ||
| 169 | i = 0; | ||
| 170 | inline while (i < 5) : (i += 1) { | ||
| 171 | x0.limbs[i] &= mask; | ||
| 172 | x1.limbs[i] &= mask; | ||
| 173 | } | ||
| 174 | i = 0; | ||
| 175 | inline while (i < 5) : (i += 1) { | ||
| 176 | a0.limbs[i] ^= x0.limbs[i]; | ||
| 177 | b0.limbs[i] ^= x0.limbs[i]; | ||
| 178 | a1.limbs[i] ^= x1.limbs[i]; | ||
| 179 | b1.limbs[i] ^= x1.limbs[i]; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | inline fn _carry128(r: *[5]u128) Fe { | ||
| 184 | var rs: [5]u64 = undefined; | ||
| 185 | comptime var i = 0; | ||
| 186 | inline while (i < 4) : (i += 1) { | ||
| 187 | rs[i] = @truncate(u64, r[i]) & MASK51; | ||
| 188 | r[i + 1] += @intCast(u64, r[i] >> 51); | ||
| 189 | } | ||
| 190 | rs[4] = @truncate(u64, r[4]) & MASK51; | ||
| 191 | var carry = @intCast(u64, r[4] >> 51); | ||
| 192 | rs[0] += 19 * carry; | ||
| 193 | carry = rs[0] >> 51; | ||
| 194 | rs[0] &= MASK51; | ||
| 195 | rs[1] += carry; | ||
| 196 | carry = rs[1] >> 51; | ||
| 197 | rs[1] &= MASK51; | ||
| 198 | rs[2] += carry; | ||
| 199 | |||
| 200 | return .{ .limbs = rs }; | ||
| 201 | } | ||
| 202 | |||
| 203 | pub inline fn mul(a: Fe, b: Fe) Fe { | ||
| 204 | var ax: [5]u128 = undefined; | ||
| 205 | var bx: [5]u128 = undefined; | ||
| 206 | var a19: [5]u128 = undefined; | ||
| 207 | var r: [5]u128 = undefined; | ||
| 208 | comptime var i = 0; | ||
| 209 | inline while (i < 5) : (i += 1) { | ||
| 210 | ax[i] = @intCast(u128, a.limbs[i]); | ||
| 211 | bx[i] = @intCast(u128, b.limbs[i]); | ||
| 212 | } | ||
| 213 | i = 1; | ||
| 214 | inline while (i < 5) : (i += 1) { | ||
| 215 | a19[i] = 19 * ax[i]; | ||
| 216 | } | ||
| 217 | r[0] = ax[0] * bx[0] + a19[1] * bx[4] + a19[2] * bx[3] + a19[3] * bx[2] + a19[4] * bx[1]; | ||
| 218 | r[1] = ax[0] * bx[1] + ax[1] * bx[0] + a19[2] * bx[4] + a19[3] * bx[3] + a19[4] * bx[2]; | ||
| 219 | r[2] = ax[0] * bx[2] + ax[1] * bx[1] + ax[2] * bx[0] + a19[3] * bx[4] + a19[4] * bx[3]; | ||
| 220 | r[3] = ax[0] * bx[3] + ax[1] * bx[2] + ax[2] * bx[1] + ax[3] * bx[0] + a19[4] * bx[4]; | ||
| 221 | r[4] = ax[0] * bx[4] + ax[1] * bx[3] + ax[2] * bx[2] + ax[3] * bx[1] + ax[4] * bx[0]; | ||
| 222 | |||
| 223 | return _carry128(&r); | ||
| 224 | } | ||
| 225 | |||
| 226 | inline fn _sq(a: Fe, double: comptime bool) Fe { | ||
| 227 | var ax: [5]u128 = undefined; | ||
| 228 | var r: [5]u128 = undefined; | ||
| 229 | comptime var i = 0; | ||
| 230 | inline while (i < 5) : (i += 1) { | ||
| 231 | ax[i] = @intCast(u128, a.limbs[i]); | ||
| 232 | } | ||
| 233 | const a0_2 = 2 * ax[0]; | ||
| 234 | const a1_2 = 2 * ax[1]; | ||
| 235 | const a1_38 = 38 * ax[1]; | ||
| 236 | const a2_38 = 38 * ax[2]; | ||
| 237 | const a3_38 = 38 * ax[3]; | ||
| 238 | const a3_19 = 19 * ax[3]; | ||
| 239 | const a4_19 = 19 * ax[4]; | ||
| 240 | r[0] = ax[0] * ax[0] + a1_38 * ax[4] + a2_38 * ax[3]; | ||
| 241 | r[1] = a0_2 * ax[1] + a2_38 * ax[4] + a3_19 * ax[3]; | ||
| 242 | r[2] = a0_2 * ax[2] + ax[1] * ax[1] + a3_38 * ax[4]; | ||
| 243 | r[3] = a0_2 * ax[3] + a1_2 * ax[2] + a4_19 * ax[4]; | ||
| 244 | r[4] = a0_2 * ax[4] + a1_2 * ax[3] + ax[2] * ax[2]; | ||
| 245 | if (double) { | ||
| 246 | i = 0; | ||
| 247 | inline while (i < 5) : (i += 1) { | ||
| 248 | r[i] *= 2; | ||
| 249 | } | ||
| 250 | } | ||
| 251 | return _carry128(&r); | ||
| 252 | } | ||
| 253 | |||
| 254 | pub inline fn sq(a: Fe) Fe { | ||
| 255 | return _sq(a, false); | ||
| 256 | } | ||
| 257 | |||
| 258 | pub inline fn sq2(a: Fe) Fe { | ||
| 259 | return _sq(a, true); | ||
| 260 | } | ||
| 261 | |||
| 262 | pub inline fn mul32(a: Fe, comptime n: u32) Fe { | ||
| 263 | const sn = @intCast(u128, n); | ||
| 264 | var fe: Fe = undefined; | ||
| 265 | var x: u128 = 0; | ||
| 266 | comptime var i = 0; | ||
| 267 | inline while (i < 5) : (i += 1) { | ||
| 268 | x = a.limbs[i] * sn + (x >> 51); | ||
| 269 | fe.limbs[i] = @truncate(u64, x) & MASK51; | ||
| 270 | } | ||
| 271 | fe.limbs[0] += @intCast(u64, x >> 51) * 19; | ||
| 272 | |||
| 273 | return fe; | ||
| 274 | } | ||
| 275 | |||
| 276 | inline fn sqn(a: Fe, comptime n: comptime_int) Fe { | ||
| 277 | var i: usize = 0; | ||
| 278 | var fe = a; | ||
| 279 | while (i < n) : (i += 1) { | ||
| 280 | fe = fe.sq(); | ||
| 281 | } | ||
| 282 | return fe; | ||
| 283 | } | ||
| 284 | |||
| 285 | pub fn invert(a: Fe) Fe { | ||
| 286 | var t0 = a.sq(); | ||
| 287 | var t1 = t0.sqn(2).mul(a); | ||
| 288 | t0 = t0.mul(t1); | ||
| 289 | t1 = t1.mul(t0.sq()); | ||
| 290 | t1 = t1.mul(t1.sqn(5)); | ||
| 291 | var t2 = t1.sqn(10).mul(t1); | ||
| 292 | t2 = t2.mul(t2.sqn(20)).sqn(10); | ||
| 293 | t1 = t1.mul(t2); | ||
| 294 | t2 = t1.sqn(50).mul(t1); | ||
| 295 | return t1.mul(t2.mul(t2.sqn(100)).sqn(50)).sqn(5).mul(t0); | ||
| 296 | } | ||
| 297 | |||
| 298 | pub fn pow2523(a: Fe) Fe { | ||
| 299 | var c = a; | ||
| 300 | var i: usize = 0; | ||
| 301 | while (i < 249) : (i += 1) { | ||
| 302 | c = c.sq().mul(a); | ||
| 303 | } | ||
| 304 | return c.sq().sq().mul(a); | ||
| 305 | } | ||
| 306 | |||
| 307 | pub fn abs(a: Fe) Fe { | ||
| 308 | var r = a; | ||
| 309 | r.cMov(a.neg(), @boolToInt(a.isNegative())); | ||
| 310 | return r; | ||
| 311 | } | ||
| 312 | }; | ||
lib/std/crypto/25519/ristretto255.zig created+144| ... | @@ -0,0 +1,144 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const fmt = std.fmt; | ||
| 3 | |||
| 4 | /// Group operations over Edwards25519. | ||
| 5 | pub const Ristretto255 = struct { | ||
| 6 | /// The underlying elliptic curve. | ||
| 7 | pub const Curve = @import("edwards25519.zig").Edwards25519; | ||
| 8 | /// The underlying prime field. | ||
| 9 | pub const Fe = Curve.Fe; | ||
| 10 | /// Field arithmetic mod the order of the main subgroup. | ||
| 11 | pub const scalar = Curve.scalar; | ||
| 12 | |||
| 13 | p: Curve, | ||
| 14 | |||
| 15 | fn sqrtRatioM1(u: Fe, v: Fe) !Fe { | ||
| 16 | const v3 = v.sq().mul(v); // v^3 | ||
| 17 | var x = v3.sq().mul(u).mul(v).pow2523().mul(v3).mul(u); // uv^3(uv^7)^((q-5)/8) | ||
| 18 | const vxx = x.sq().mul(v); // vx^2 | ||
| 19 | const m_root_check = vxx.sub(u); // vx^2-u | ||
| 20 | const p_root_check = vxx.add(u); // vx^2+u | ||
| 21 | const f_root_check = u.mul(Fe.sqrtm1).add(vxx); // vx^2+u*sqrt(-1) | ||
| 22 | const has_m_root = m_root_check.isZero(); | ||
| 23 | const has_p_root = p_root_check.isZero(); | ||
| 24 | const has_f_root = f_root_check.isZero(); | ||
| 25 | const x_sqrtm1 = x.mul(Fe.sqrtm1); // x*sqrt(-1) | ||
| 26 | x.cMov(x_sqrtm1, @boolToInt(has_p_root) | @boolToInt(has_f_root)); | ||
| 27 | const xa = x.abs(); | ||
| 28 | if ((@boolToInt(has_m_root) | @boolToInt(has_p_root)) == 0) { | ||
| 29 | return error.NoRoot; | ||
| 30 | } | ||
| 31 | return xa; | ||
| 32 | } | ||
| 33 | |||
| 34 | fn rejectNonCanonical(s: [32]u8) !void { | ||
| 35 | if ((s[0] & 1) != 0) { | ||
| 36 | return error.NonCanonical; | ||
| 37 | } | ||
| 38 | try Fe.rejectNonCanonical(s, false); | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Reject the neutral element. | ||
| 42 | pub inline fn rejectIdentity(p: Ristretto255) !void { | ||
| 43 | return p.p.rejectIdentity(); | ||
| 44 | } | ||
| 45 | |||
| 46 | /// The base point (Ristretto is a curve in desguise). | ||
| 47 | pub const basePoint = Ristretto255{ .p = Curve.basePoint }; | ||
| 48 | |||
| 49 | /// Decode a Ristretto255 representative. | ||
| 50 | pub fn fromBytes(s: [32]u8) !Ristretto255 { | ||
| 51 | try rejectNonCanonical(s); | ||
| 52 | const s_ = Fe.fromBytes(s); | ||
| 53 | const ss = s_.sq(); // s^2 | ||
| 54 | const u1_ = Fe.one.sub(ss); // (1-s^2) | ||
| 55 | const u1u1 = u1_.sq(); // (1-s^2)^2 | ||
| 56 | const u2_ = Fe.one.add(ss); // (1+s^2) | ||
| 57 | const u2u2 = u2_.sq(); // (1+s^2)^2 | ||
| 58 | const v = Fe.edwards25519d.mul(u1u1).neg().sub(u2u2); // -(d*u1^2)-u2^2 | ||
| 59 | const v_u2u2 = v.mul(u2u2); // v*u2^2 | ||
| 60 | const inv_sqrt = sqrtRatioM1(Fe.one, v_u2u2) catch |e| { | ||
| 61 | return error.InvalidEncoding; | ||
| 62 | }; | ||
| 63 | var x = inv_sqrt.mul(u2_); | ||
| 64 | const y = inv_sqrt.mul(x).mul(v).mul(u1_); | ||
| 65 | x = x.mul(s_); | ||
| 66 | x = x.add(x).abs(); | ||
| 67 | const t = x.mul(y); | ||
| 68 | if ((@boolToInt(t.isNegative()) | @boolToInt(y.isZero())) != 0) { | ||
| 69 | return error.InvalidEncoding; | ||
| 70 | } | ||
| 71 | const p: Curve = .{ | ||
| 72 | .x = x, | ||
| 73 | .y = y, | ||
| 74 | .z = Fe.one, | ||
| 75 | .t = t, | ||
| 76 | }; | ||
| 77 | return Ristretto255{ .p = p }; | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Encode to a Ristretto255 representative. | ||
| 81 | pub fn toBytes(e: Ristretto255) [32]u8 { | ||
| 82 | const p = &e.p; | ||
| 83 | var u1_ = p.z.add(p.y); // Z+Y | ||
| 84 | const zmy = p.z.sub(p.y); // Z-Y | ||
| 85 | u1_ = u1_.mul(zmy); // (Z+Y)*(Z-Y) | ||
| 86 | const u2_ = p.x.mul(p.y); // X*Y | ||
| 87 | const u1_u2u2 = u2_.sq().mul(u1_); // u1*u2^2 | ||
| 88 | const inv_sqrt = sqrtRatioM1(Fe.one, u1_u2u2) catch unreachable; | ||
| 89 | const den1 = inv_sqrt.mul(u1_); | ||
| 90 | const den2 = inv_sqrt.mul(u2_); | ||
| 91 | const z_inv = den1.mul(den2).mul(p.t); // den1*den2*T | ||
| 92 | const ix = p.x.mul(Fe.sqrtm1); // X*sqrt(-1) | ||
| 93 | const iy = p.y.mul(Fe.sqrtm1); // Y*sqrt(-1) | ||
| 94 | const eden = den1.mul(Fe.edwards25519sqrtamd); // den1/sqrt(a-d) | ||
| 95 | const t_z_inv = p.t.mul(z_inv); // T*z_inv | ||
| 96 | |||
| 97 | const rotate = @boolToInt(t_z_inv.isNegative()); | ||
| 98 | var x = p.x; | ||
| 99 | var y = p.y; | ||
| 100 | var den_inv = den2; | ||
| 101 | x.cMov(iy, rotate); | ||
| 102 | y.cMov(ix, rotate); | ||
| 103 | den_inv.cMov(eden, rotate); | ||
| 104 | |||
| 105 | const x_z_inv = x.mul(z_inv); | ||
| 106 | const yneg = y.neg(); | ||
| 107 | y.cMov(yneg, @boolToInt(x_z_inv.isNegative())); | ||
| 108 | |||
| 109 | return p.z.sub(y).mul(den_inv).abs().toBytes(); | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Double a Ristretto255 element. | ||
| 113 | pub inline fn dbl(p: Ristretto255) Ristretto255 { | ||
| 114 | return .{ .p = p.p.dbl() }; | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Add two Ristretto255 elements. | ||
| 118 | pub inline fn add(p: Ristretto255, q: Ristretto255) Ristretto255 { | ||
| 119 | return .{ .p = p.p.add(q.p) }; | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Multiply a Ristretto255 element with a scalar. | ||
| 123 | /// Return error.WeakPublicKey if the resulting element is | ||
| 124 | /// the identity element. | ||
| 125 | pub inline fn mul(p: Ristretto255, s: [32]u8) !Ristretto255 { | ||
| 126 | return Ristretto255{ .p = try p.p.mul(s) }; | ||
| 127 | } | ||
| 128 | }; | ||
| 129 | |||
| 130 | test "ristretto255" { | ||
| 131 | const p = Ristretto255.basePoint; | ||
| 132 | var buf: [256]u8 = undefined; | ||
| 133 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76"); | ||
| 134 | |||
| 135 | var r: [32]u8 = undefined; | ||
| 136 | try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); | ||
| 137 | var q = try Ristretto255.fromBytes(r); | ||
| 138 | q = q.dbl().add(p); | ||
| 139 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E"); | ||
| 140 | |||
| 141 | const s = [_]u8{15} ++ [_]u8{0} ** 31; | ||
| 142 | const w = try p.mul(s); | ||
| 143 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{w.toBytes()}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E"); | ||
| 144 | } | ||
lib/std/crypto/25519/scalar.zig created+177| ... | @@ -0,0 +1,177 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | |||
| 4 | const field_size = [32]u8{ | ||
| 5 | 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // 2^252+27742317777372353535851937790883648493 | ||
| 6 | }; | ||
| 7 | |||
| 8 | const ScalarExpanded = struct { | ||
| 9 | limbs: [64]i64 = [_]i64{0} ** 64, | ||
| 10 | |||
| 11 | fn fromBytes(s: [32]u8) ScalarExpanded { | ||
| 12 | var limbs: [64]i64 = undefined; | ||
| 13 | for (s) |x, idx| { | ||
| 14 | limbs[idx] = @as(i64, x); | ||
| 15 | } | ||
| 16 | mem.set(i64, limbs[32..], 0); | ||
| 17 | return .{ .limbs = limbs }; | ||
| 18 | } | ||
| 19 | |||
| 20 | fn fromBytes64(s: [64]u8) ScalarExpanded { | ||
| 21 | var limbs: [64]i64 = undefined; | ||
| 22 | for (s) |x, idx| { | ||
| 23 | limbs[idx] = @as(i64, x); | ||
| 24 | } | ||
| 25 | return .{ .limbs = limbs }; | ||
| 26 | } | ||
| 27 | |||
| 28 | fn reduce(e: *ScalarExpanded) void { | ||
| 29 | const limbs = &e.limbs; | ||
| 30 | var carry: i64 = undefined; | ||
| 31 | var i: usize = 63; | ||
| 32 | while (i >= 32) : (i -= 1) { | ||
| 33 | carry = 0; | ||
| 34 | const k = i - 12; | ||
| 35 | const xi = limbs[i]; | ||
| 36 | var j = i - 32; | ||
| 37 | while (j < k) : (j += 1) { | ||
| 38 | const xj = limbs[j] + carry - 16 * xi * @as(i64, field_size[j - (i - 32)]); | ||
| 39 | carry = (xj + 128) >> 8; | ||
| 40 | limbs[j] = xj - carry * 256; | ||
| 41 | } | ||
| 42 | limbs[k] += carry; | ||
| 43 | limbs[i] = 0; | ||
| 44 | } | ||
| 45 | carry = 0; | ||
| 46 | comptime var j: usize = 0; | ||
| 47 | inline while (j < 32) : (j += 1) { | ||
| 48 | const xi = limbs[j] + carry - (limbs[31] >> 4) * @as(i64, field_size[j]); | ||
| 49 | carry = xi >> 8; | ||
| 50 | limbs[j] = xi & 255; | ||
| 51 | } | ||
| 52 | j = 0; | ||
| 53 | inline while (j < 32) : (j += 1) { | ||
| 54 | limbs[j] -= carry * @as(i64, field_size[j]); | ||
| 55 | } | ||
| 56 | j = 0; | ||
| 57 | inline while (j < 32) : (j += 1) { | ||
| 58 | limbs[j + 1] += limbs[j] >> 8; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | fn toBytes(e: *ScalarExpanded) [32]u8 { | ||
| 63 | e.reduce(); | ||
| 64 | var r: [32]u8 = undefined; | ||
| 65 | var i: usize = 0; | ||
| 66 | while (i < 32) : (i += 1) { | ||
| 67 | r[i] = @intCast(u8, e.limbs[i]); | ||
| 68 | } | ||
| 69 | return r; | ||
| 70 | } | ||
| 71 | |||
| 72 | fn add(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded { | ||
| 73 | var r = ScalarExpanded{}; | ||
| 74 | comptime var i = 0; | ||
| 75 | inline while (i < 64) : (i += 1) { | ||
| 76 | r.limbs[i] = a.limbs[i] + b.limbs[i]; | ||
| 77 | } | ||
| 78 | return r; | ||
| 79 | } | ||
| 80 | |||
| 81 | fn mul(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded { | ||
| 82 | var r = ScalarExpanded{}; | ||
| 83 | var i: usize = 0; | ||
| 84 | while (i < 32) : (i += 1) { | ||
| 85 | const ai = a.limbs[i]; | ||
| 86 | comptime var j = 0; | ||
| 87 | inline while (j < 32) : (j += 1) { | ||
| 88 | r.limbs[i + j] += ai * b.limbs[j]; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | r.reduce(); | ||
| 92 | return r; | ||
| 93 | } | ||
| 94 | |||
| 95 | fn sq(a: ScalarExpanded) ScalarExpanded { | ||
| 96 | return a.mul(a); | ||
| 97 | } | ||
| 98 | |||
| 99 | fn mulAdd(a: ScalarExpanded, b: ScalarExpanded, c: ScalarExpanded) ScalarExpanded { | ||
| 100 | var r: ScalarExpanded = .{ .limbs = c.limbs }; | ||
| 101 | var i: usize = 0; | ||
| 102 | while (i < 32) : (i += 1) { | ||
| 103 | const ai = a.limbs[i]; | ||
| 104 | comptime var j = 0; | ||
| 105 | inline while (j < 32) : (j += 1) { | ||
| 106 | r.limbs[i + j] += ai * b.limbs[j]; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | r.reduce(); | ||
| 110 | return r; | ||
| 111 | } | ||
| 112 | }; | ||
| 113 | |||
| 114 | /// Reject a scalar whose encoding is not canonical. | ||
| 115 | pub fn rejectNonCanonical(s: [32]u8) !void { | ||
| 116 | var c: u8 = 0; | ||
| 117 | var n: u8 = 1; | ||
| 118 | var i: usize = 31; | ||
| 119 | while (true) : (i -= 1) { | ||
| 120 | const xs = @as(u16, s[i]); | ||
| 121 | const xfield_size = @as(u16, field_size[i]); | ||
| 122 | c |= @intCast(u8, ((xs -% xfield_size) >> 8) & n); | ||
| 123 | n &= @intCast(u8, ((xs ^ xfield_size) -% 1) >> 8); | ||
| 124 | if (i == 0) break; | ||
| 125 | } | ||
| 126 | if (c == 0) { | ||
| 127 | return error.NonCanonical; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Reduce a scalar to the field size. | ||
| 132 | pub fn reduce(s: [32]u8) [32]u8 { | ||
| 133 | return ScalarExpanded.fromBytes(s).toBytes(); | ||
| 134 | } | ||
| 135 | |||
| 136 | /// Reduce a 64-bytes scalar to the field size. | ||
| 137 | pub fn reduce64(s: [64]u8) [32]u8 { | ||
| 138 | return ScalarExpanded.fromBytes64(s).toBytes(); | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Perform the X25519 "clamping" operation. | ||
| 142 | /// The scalar is then guaranteed to be a multiple of the cofactor. | ||
| 143 | pub inline fn clamp(s: *[32]u8) void { | ||
| 144 | s[0] &= 248; | ||
| 145 | s[31] = (s[31] & 127) | 64; | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Return a*b+c (mod L) | ||
| 149 | pub fn mulAdd(a: [32]u8, b: [32]u8, c: [32]u8) [32]u8 { | ||
| 150 | return ScalarExpanded.fromBytes(a).mulAdd(ScalarExpanded.fromBytes(b), ScalarExpanded.fromBytes(c)).toBytes(); | ||
| 151 | } | ||
| 152 | |||
| 153 | test "scalar25519" { | ||
| 154 | const bytes: [32]u8 = .{ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 255 }; | ||
| 155 | var x = ScalarExpanded.fromBytes(bytes); | ||
| 156 | var y = x.toBytes(); | ||
| 157 | try rejectNonCanonical(y); | ||
| 158 | var buf: [128]u8 = undefined; | ||
| 159 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{y}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F"); | ||
| 160 | |||
| 161 | const reduced = reduce(field_size); | ||
| 162 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{reduced}), "0000000000000000000000000000000000000000000000000000000000000000"); | ||
| 163 | } | ||
| 164 | |||
| 165 | test "non-canonical scalar25519" { | ||
| 166 | const too_targe: [32]u8 = .{ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 }; | ||
| 167 | std.testing.expectError(error.NonCanonical, rejectNonCanonical(too_targe)); | ||
| 168 | } | ||
| 169 | |||
| 170 | test "mulAdd overflow check" { | ||
| 171 | const a: [32]u8 = [_]u8{0xff} ** 32; | ||
| 172 | const b: [32]u8 = [_]u8{0xff} ** 32; | ||
| 173 | const c: [32]u8 = [_]u8{0xff} ** 32; | ||
| 174 | const x = mulAdd(a, b, c); | ||
| 175 | var buf: [128]u8 = undefined; | ||
| 176 | std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{x}), "D14DF91389432C25AD60FF9791B9FD1D67BEF517D273ECCE3D9A307C1B419903"); | ||
| 177 | } | ||
lib/std/crypto/25519/x25519.zig created+146| ... | @@ -0,0 +1,146 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const fmt = std.fmt; | ||
| 4 | |||
| 5 | /// X25519 DH function. | ||
| 6 | pub const X25519 = struct { | ||
| 7 | /// The underlying elliptic curve. | ||
| 8 | pub const Curve = @import("curve25519.zig").Curve25519; | ||
| 9 | /// Length (in bytes) of a secret key. | ||
| 10 | pub const secret_length = 32; | ||
| 11 | /// Length (in bytes) of the output of the DH function. | ||
| 12 | pub const minimum_key_length = 32; | ||
| 13 | |||
| 14 | /// Compute the public key for a given private key. | ||
| 15 | pub fn createPublicKey(public_key: []u8, private_key: []const u8) bool { | ||
| 16 | std.debug.assert(private_key.len >= minimum_key_length); | ||
| 17 | std.debug.assert(public_key.len >= minimum_key_length); | ||
| 18 | var s: [32]u8 = undefined; | ||
| 19 | mem.copy(u8, &s, private_key[0..32]); | ||
| 20 | if (Curve.basePoint.clampedMul(s)) |q| { | ||
| 21 | mem.copy(u8, public_key, q.toBytes()[0..]); | ||
| 22 | return true; | ||
| 23 | } else |_| { | ||
| 24 | return false; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Compute the scalar product of a public key and a secret scalar. | ||
| 29 | /// Note that the output should not be used as a shared secret without | ||
| 30 | /// hashing it first. | ||
| 31 | pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool { | ||
| 32 | std.debug.assert(out.len >= secret_length); | ||
| 33 | std.debug.assert(private_key.len >= minimum_key_length); | ||
| 34 | std.debug.assert(public_key.len >= minimum_key_length); | ||
| 35 | var s: [32]u8 = undefined; | ||
| 36 | var b: [32]u8 = undefined; | ||
| 37 | mem.copy(u8, &s, private_key[0..32]); | ||
| 38 | mem.copy(u8, &b, public_key[0..32]); | ||
| 39 | if (Curve.fromBytes(b).clampedMul(s)) |q| { | ||
| 40 | mem.copy(u8, out, q.toBytes()[0..]); | ||
| 41 | return true; | ||
| 42 | } else |_| { | ||
| 43 | return false; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | }; | ||
| 47 | |||
| 48 | test "x25519 public key calculation from secret key" { | ||
| 49 | var sk: [32]u8 = undefined; | ||
| 50 | var pk_expected: [32]u8 = undefined; | ||
| 51 | var pk_calculated: [32]u8 = undefined; | ||
| 52 | try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | ||
| 53 | try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); | ||
| 54 | std.testing.expect(X25519.createPublicKey(pk_calculated[0..], &sk)); | ||
| 55 | std.testing.expectEqual(pk_calculated, pk_expected); | ||
| 56 | } | ||
| 57 | |||
| 58 | test "x25519 rfc7748 vector1" { | ||
| 59 | const secret_key = [32]u8{ 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4 }; | ||
| 60 | const public_key = [32]u8{ 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c }; | ||
| 61 | |||
| 62 | const expected_output = [32]u8{ 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52 }; | ||
| 63 | |||
| 64 | var output: [32]u8 = undefined; | ||
| 65 | |||
| 66 | std.testing.expect(X25519.create(output[0..], secret_key[0..], public_key[0..])); | ||
| 67 | std.testing.expectEqual(output, expected_output); | ||
| 68 | } | ||
| 69 | |||
| 70 | test "x25519 rfc7748 vector2" { | ||
| 71 | const secret_key = [32]u8{ 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d }; | ||
| 72 | const public_key = [32]u8{ 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93 }; | ||
| 73 | |||
| 74 | const expected_output = [32]u8{ 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57 }; | ||
| 75 | |||
| 76 | var output: [32]u8 = undefined; | ||
| 77 | |||
| 78 | std.testing.expect(X25519.create(output[0..], secret_key[0..], public_key[0..])); | ||
| 79 | std.testing.expectEqual(output, expected_output); | ||
| 80 | } | ||
| 81 | |||
| 82 | test "x25519 rfc7748 one iteration" { | ||
| 83 | const initial_value = [32]u8{ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| 84 | const expected_output = [32]u8{ 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79 }; | ||
| 85 | |||
| 86 | var k: [32]u8 = initial_value; | ||
| 87 | var u: [32]u8 = initial_value; | ||
| 88 | |||
| 89 | var i: usize = 0; | ||
| 90 | while (i < 1) : (i += 1) { | ||
| 91 | var output: [32]u8 = undefined; | ||
| 92 | std.testing.expect(X25519.create(output[0..], &k, &u)); | ||
| 93 | |||
| 94 | mem.copy(u8, u[0..], k[0..]); | ||
| 95 | mem.copy(u8, k[0..], output[0..]); | ||
| 96 | } | ||
| 97 | |||
| 98 | std.testing.expectEqual(k, expected_output); | ||
| 99 | } | ||
| 100 | |||
| 101 | test "x25519 rfc7748 1,000 iterations" { | ||
| 102 | // These iteration tests are slow so we always skip them. Results have been verified. | ||
| 103 | if (true) { | ||
| 104 | return error.SkipZigTest; | ||
| 105 | } | ||
| 106 | |||
| 107 | const initial_value = [32]u8{ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| 108 | const expected_output = [32]u8{ 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51 }; | ||
| 109 | |||
| 110 | var k: [32]u8 = initial_value.*; | ||
| 111 | var u: [32]u8 = initial_value.*; | ||
| 112 | |||
| 113 | var i: usize = 0; | ||
| 114 | while (i < 1000) : (i += 1) { | ||
| 115 | var output: [32]u8 = undefined; | ||
| 116 | std.testing.expect(X25519.create(output[0..], &k, &u)); | ||
| 117 | |||
| 118 | mem.copy(u8, u[0..], k[0..]); | ||
| 119 | mem.copy(u8, k[0..], output[0..]); | ||
| 120 | } | ||
| 121 | |||
| 122 | std.testing.expectEqual(k, expected_output); | ||
| 123 | } | ||
| 124 | |||
| 125 | test "x25519 rfc7748 1,000,000 iterations" { | ||
| 126 | if (true) { | ||
| 127 | return error.SkipZigTest; | ||
| 128 | } | ||
| 129 | |||
| 130 | const initial_value = [32]u8{ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| 131 | const expected_output = [32]u8{ 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24 }; | ||
| 132 | |||
| 133 | var k: [32]u8 = initial_value.*; | ||
| 134 | var u: [32]u8 = initial_value.*; | ||
| 135 | |||
| 136 | var i: usize = 0; | ||
| 137 | while (i < 1000000) : (i += 1) { | ||
| 138 | var output: [32]u8 = undefined; | ||
| 139 | std.testing.expect(X25519.create(output[0..], &k, &u)); | ||
| 140 | |||
| 141 | mem.copy(u8, u[0..], k[0..]); | ||
| 142 | mem.copy(u8, k[0..], output[0..]); | ||
| 143 | } | ||
| 144 | |||
| 145 | std.testing.expectEqual(k[0..], expected_output); | ||
| 146 | } | ||
lib/std/crypto/benchmark.zig+31-1| ... | @@ -90,7 +90,6 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c | ... | @@ -90,7 +90,6 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c |
| 90 | var out: [DhKeyExchange.minimum_key_length]u8 = undefined; | 90 | var out: [DhKeyExchange.minimum_key_length]u8 = undefined; |
| 91 | prng.random.bytes(out[0..]); | 91 | prng.random.bytes(out[0..]); |
| 92 | 92 | ||
| 93 | var offset: usize = 0; | ||
| 94 | var timer = try Timer.start(); | 93 | var timer = try Timer.start(); |
| 95 | const start = timer.lap(); | 94 | const start = timer.lap(); |
| 96 | { | 95 | { |
| ... | @@ -107,6 +106,30 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c | ... | @@ -107,6 +106,30 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c |
| 107 | return throughput; | 106 | return throughput; |
| 108 | } | 107 | } |
| 109 | 108 | ||
| 109 | const signatures = [_]Crypto{Crypto{ .ty = crypto.Ed25519, .name = "ed25519" }}; | ||
| 110 | |||
| 111 | pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 { | ||
| 112 | var seed: [Signature.seed_length]u8 = undefined; | ||
| 113 | prng.random.bytes(seed[0..]); | ||
| 114 | const msg = [_]u8{0} ** 64; | ||
| 115 | const key_pair = try Signature.createKeyPair(seed); | ||
| 116 | |||
| 117 | var timer = try Timer.start(); | ||
| 118 | const start = timer.lap(); | ||
| 119 | { | ||
| 120 | var i: usize = 0; | ||
| 121 | while (i < signatures_count) : (i += 1) { | ||
| 122 | _ = try Signature.sign(&msg, key_pair, null); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | const end = timer.read(); | ||
| 126 | |||
| 127 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; | ||
| 128 | const throughput = @floatToInt(u64, signatures_count / elapsed_s); | ||
| 129 | |||
| 130 | return throughput; | ||
| 131 | } | ||
| 132 | |||
| 110 | fn usage() void { | 133 | fn usage() void { |
| 111 | std.debug.warn( | 134 | std.debug.warn( |
| 112 | \\throughput_test [options] | 135 | \\throughput_test [options] |
| ... | @@ -183,4 +206,11 @@ pub fn main() !void { | ... | @@ -183,4 +206,11 @@ pub fn main() !void { |
| 183 | try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput }); | 206 | try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput }); |
| 184 | } | 207 | } |
| 185 | } | 208 | } |
| 209 | |||
| 210 | inline for (signatures) |E| { | ||
| 211 | if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { | ||
| 212 | const throughput = try benchmarkSignatures(E.ty, mode(1000)); | ||
| 213 | try stdout.print("{:>11}: {:5} signatures/s\n", .{ E.name, throughput }); | ||
| 214 | } | ||
| 215 | } | ||
| 186 | } | 216 | } |
lib/std/crypto/x25519.zig deleted-675| ... | @@ -1,675 +0,0 @@ | ||
| 1 | // Translated from monocypher which is licensed under CC-0/BSD-3. | ||
| 2 | // | ||
| 3 | // https://monocypher.org/ | ||
| 4 | |||
| 5 | const std = @import("../std.zig"); | ||
| 6 | const builtin = @import("builtin"); | ||
| 7 | const fmt = std.fmt; | ||
| 8 | |||
| 9 | const Endian = builtin.Endian; | ||
| 10 | const readIntLittle = std.mem.readIntLittle; | ||
| 11 | const writeIntLittle = std.mem.writeIntLittle; | ||
| 12 | |||
| 13 | // Based on Supercop's ref10 implementation. | ||
| 14 | pub const X25519 = struct { | ||
| 15 | pub const secret_length = 32; | ||
| 16 | pub const minimum_key_length = 32; | ||
| 17 | |||
| 18 | fn trimScalar(s: []u8) void { | ||
| 19 | s[0] &= 248; | ||
| 20 | s[31] &= 127; | ||
| 21 | s[31] |= 64; | ||
| 22 | } | ||
| 23 | |||
| 24 | fn scalarBit(s: []const u8, i: usize) i32 { | ||
| 25 | return (s[i >> 3] >> @intCast(u3, i & 7)) & 1; | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool { | ||
| 29 | std.debug.assert(out.len >= secret_length); | ||
| 30 | std.debug.assert(private_key.len >= minimum_key_length); | ||
| 31 | std.debug.assert(public_key.len >= minimum_key_length); | ||
| 32 | |||
| 33 | var storage: [7]Fe = undefined; | ||
| 34 | var x1 = &storage[0]; | ||
| 35 | var x2 = &storage[1]; | ||
| 36 | var z2 = &storage[2]; | ||
| 37 | var x3 = &storage[3]; | ||
| 38 | var z3 = &storage[4]; | ||
| 39 | var t0 = &storage[5]; | ||
| 40 | var t1 = &storage[6]; | ||
| 41 | |||
| 42 | // computes the scalar product | ||
| 43 | Fe.fromBytes(x1, public_key); | ||
| 44 | |||
| 45 | // restrict the possible scalar values | ||
| 46 | var e: [32]u8 = undefined; | ||
| 47 | for (e[0..]) |_, i| { | ||
| 48 | e[i] = private_key[i]; | ||
| 49 | } | ||
| 50 | trimScalar(e[0..]); | ||
| 51 | |||
| 52 | // computes the actual scalar product (the result is in x2 and z2) | ||
| 53 | |||
| 54 | // Montgomery ladder | ||
| 55 | // In projective coordinates, to avoid divisions: x = X / Z | ||
| 56 | // We don't care about the y coordinate, it's only 1 bit of information | ||
| 57 | Fe.init1(x2); | ||
| 58 | Fe.init0(z2); // "zero" point | ||
| 59 | Fe.copy(x3, x1); | ||
| 60 | Fe.init1(z3); | ||
| 61 | |||
| 62 | var swap: i32 = 0; | ||
| 63 | var pos: isize = 254; | ||
| 64 | while (pos >= 0) : (pos -= 1) { | ||
| 65 | // constant time conditional swap before ladder step | ||
| 66 | const b = scalarBit(&e, @intCast(usize, pos)); | ||
| 67 | swap ^= b; // xor trick avoids swapping at the end of the loop | ||
| 68 | Fe.cswap(x2, x3, swap); | ||
| 69 | Fe.cswap(z2, z3, swap); | ||
| 70 | swap = b; // anticipates one last swap after the loop | ||
| 71 | |||
| 72 | // Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3) | ||
| 73 | // with differential addition | ||
| 74 | Fe.sub(t0, x3, z3); | ||
| 75 | Fe.sub(t1, x2, z2); | ||
| 76 | Fe.add(x2, x2, z2); | ||
| 77 | Fe.add(z2, x3, z3); | ||
| 78 | Fe.mul(z3, t0, x2); | ||
| 79 | Fe.mul(z2, z2, t1); | ||
| 80 | Fe.sq(t0, t1); | ||
| 81 | Fe.sq(t1, x2); | ||
| 82 | Fe.add(x3, z3, z2); | ||
| 83 | Fe.sub(z2, z3, z2); | ||
| 84 | Fe.mul(x2, t1, t0); | ||
| 85 | Fe.sub(t1, t1, t0); | ||
| 86 | Fe.sq(z2, z2); | ||
| 87 | Fe.mulSmall(z3, t1, 121666); | ||
| 88 | Fe.sq(x3, x3); | ||
| 89 | Fe.add(t0, t0, z3); | ||
| 90 | Fe.mul(z3, x1, z2); | ||
| 91 | Fe.mul(z2, t1, t0); | ||
| 92 | } | ||
| 93 | |||
| 94 | // last swap is necessary to compensate for the xor trick | ||
| 95 | // Note: after this swap, P3 == P2 + P1. | ||
| 96 | Fe.cswap(x2, x3, swap); | ||
| 97 | Fe.cswap(z2, z3, swap); | ||
| 98 | |||
| 99 | // normalises the coordinates: x == X / Z | ||
| 100 | Fe.invert(z2, z2); | ||
| 101 | Fe.mul(x2, x2, z2); | ||
| 102 | Fe.toBytes(out, x2); | ||
| 103 | |||
| 104 | x1.secureZero(); | ||
| 105 | x2.secureZero(); | ||
| 106 | x3.secureZero(); | ||
| 107 | t0.secureZero(); | ||
| 108 | t1.secureZero(); | ||
| 109 | z2.secureZero(); | ||
| 110 | z3.secureZero(); | ||
| 111 | std.mem.secureZero(u8, e[0..]); | ||
| 112 | |||
| 113 | // Returns false if the output is all zero | ||
| 114 | // (happens with some malicious public keys) | ||
| 115 | return !zerocmp(u8, out); | ||
| 116 | } | ||
| 117 | |||
| 118 | pub fn createPublicKey(public_key: []u8, private_key: []const u8) bool { | ||
| 119 | var base_point = [_]u8{9} ++ [_]u8{0} ** 31; | ||
| 120 | return create(public_key, private_key, &base_point); | ||
| 121 | } | ||
| 122 | }; | ||
| 123 | |||
| 124 | // Constant time compare to zero. | ||
| 125 | fn zerocmp(comptime T: type, a: []const T) bool { | ||
| 126 | var s: T = 0; | ||
| 127 | for (a) |b| { | ||
| 128 | s |= b; | ||
| 129 | } | ||
| 130 | return s == 0; | ||
| 131 | } | ||
| 132 | |||
| 133 | //////////////////////////////////// | ||
| 134 | /// Arithmetic modulo 2^255 - 19 /// | ||
| 135 | //////////////////////////////////// | ||
| 136 | // Taken from Supercop's ref10 implementation. | ||
| 137 | // A bit bigger than TweetNaCl, over 4 times faster. | ||
| 138 | |||
| 139 | // field element | ||
| 140 | const Fe = struct { | ||
| 141 | b: [10]i32, | ||
| 142 | |||
| 143 | fn secureZero(self: *Fe) void { | ||
| 144 | std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Fe)]); | ||
| 145 | } | ||
| 146 | |||
| 147 | fn init0(h: *Fe) void { | ||
| 148 | for (h.b) |*e| { | ||
| 149 | e.* = 0; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | fn init1(h: *Fe) void { | ||
| 154 | for (h.b[1..]) |*e| { | ||
| 155 | e.* = 0; | ||
| 156 | } | ||
| 157 | h.b[0] = 1; | ||
| 158 | } | ||
| 159 | |||
| 160 | fn copy(h: *Fe, f: *const Fe) void { | ||
| 161 | for (h.b) |_, i| { | ||
| 162 | h.b[i] = f.b[i]; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | fn neg(h: *Fe, f: *const Fe) void { | ||
| 167 | for (h.b) |_, i| { | ||
| 168 | h.b[i] = -f.b[i]; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | fn add(h: *Fe, f: *const Fe, g: *const Fe) void { | ||
| 173 | for (h.b) |_, i| { | ||
| 174 | h.b[i] = f.b[i] + g.b[i]; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | fn sub(h: *Fe, f: *const Fe, g: *const Fe) void { | ||
| 179 | for (h.b) |_, i| { | ||
| 180 | h.b[i] = f.b[i] - g.b[i]; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | fn cswap(f: *Fe, g: *Fe, b: i32) void { | ||
| 185 | for (f.b) |_, i| { | ||
| 186 | const x = (f.b[i] ^ g.b[i]) & -b; | ||
| 187 | f.b[i] ^= x; | ||
| 188 | g.b[i] ^= x; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | fn ccopy(f: *Fe, g: *const Fe, b: i32) void { | ||
| 193 | for (f.b) |_, i| { | ||
| 194 | const x = (f.b[i] ^ g.b[i]) & -b; | ||
| 195 | f.b[i] ^= x; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | inline fn carryRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void { | ||
| 200 | const j = (i + 1) % 10; | ||
| 201 | |||
| 202 | c[i] = (t[i] + (@as(i64, 1) << shift)) >> (shift + 1); | ||
| 203 | t[j] += c[i] * mult; | ||
| 204 | t[i] -= c[i] * (@as(i64, 1) << (shift + 1)); | ||
| 205 | } | ||
| 206 | |||
| 207 | fn carry1(h: *Fe, t: []i64) void { | ||
| 208 | var c: [10]i64 = undefined; | ||
| 209 | |||
| 210 | var sc = c[0..]; | ||
| 211 | var st = t[0..]; | ||
| 212 | |||
| 213 | carryRound(sc, st, 9, 24, 19); | ||
| 214 | carryRound(sc, st, 1, 24, 1); | ||
| 215 | carryRound(sc, st, 3, 24, 1); | ||
| 216 | carryRound(sc, st, 5, 24, 1); | ||
| 217 | carryRound(sc, st, 7, 24, 1); | ||
| 218 | carryRound(sc, st, 0, 25, 1); | ||
| 219 | carryRound(sc, st, 2, 25, 1); | ||
| 220 | carryRound(sc, st, 4, 25, 1); | ||
| 221 | carryRound(sc, st, 6, 25, 1); | ||
| 222 | carryRound(sc, st, 8, 25, 1); | ||
| 223 | |||
| 224 | for (h.b) |_, i| { | ||
| 225 | h.b[i] = @intCast(i32, t[i]); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | fn carry2(h: *Fe, t: []i64) void { | ||
| 230 | var c: [10]i64 = undefined; | ||
| 231 | |||
| 232 | var sc = c[0..]; | ||
| 233 | var st = t[0..]; | ||
| 234 | |||
| 235 | carryRound(sc, st, 0, 25, 1); | ||
| 236 | carryRound(sc, st, 4, 25, 1); | ||
| 237 | carryRound(sc, st, 1, 24, 1); | ||
| 238 | carryRound(sc, st, 5, 24, 1); | ||
| 239 | carryRound(sc, st, 2, 25, 1); | ||
| 240 | carryRound(sc, st, 6, 25, 1); | ||
| 241 | carryRound(sc, st, 3, 24, 1); | ||
| 242 | carryRound(sc, st, 7, 24, 1); | ||
| 243 | carryRound(sc, st, 4, 25, 1); | ||
| 244 | carryRound(sc, st, 8, 25, 1); | ||
| 245 | carryRound(sc, st, 9, 24, 19); | ||
| 246 | carryRound(sc, st, 0, 25, 1); | ||
| 247 | |||
| 248 | for (h.b) |_, i| { | ||
| 249 | h.b[i] = @intCast(i32, t[i]); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | fn fromBytes(h: *Fe, s: []const u8) void { | ||
| 254 | std.debug.assert(s.len >= 32); | ||
| 255 | |||
| 256 | var t: [10]i64 = undefined; | ||
| 257 | |||
| 258 | t[0] = readIntLittle(u32, s[0..4]); | ||
| 259 | t[1] = @as(u32, readIntLittle(u24, s[4..7])) << 6; | ||
| 260 | t[2] = @as(u32, readIntLittle(u24, s[7..10])) << 5; | ||
| 261 | t[3] = @as(u32, readIntLittle(u24, s[10..13])) << 3; | ||
| 262 | t[4] = @as(u32, readIntLittle(u24, s[13..16])) << 2; | ||
| 263 | t[5] = readIntLittle(u32, s[16..20]); | ||
| 264 | t[6] = @as(u32, readIntLittle(u24, s[20..23])) << 7; | ||
| 265 | t[7] = @as(u32, readIntLittle(u24, s[23..26])) << 5; | ||
| 266 | t[8] = @as(u32, readIntLittle(u24, s[26..29])) << 4; | ||
| 267 | t[9] = (@as(u32, readIntLittle(u24, s[29..32])) & 0x7fffff) << 2; | ||
| 268 | |||
| 269 | carry1(h, t[0..]); | ||
| 270 | } | ||
| 271 | |||
| 272 | fn mulSmall(h: *Fe, f: *const Fe, comptime g: comptime_int) void { | ||
| 273 | var t: [10]i64 = undefined; | ||
| 274 | |||
| 275 | for (t[0..]) |_, i| { | ||
| 276 | t[i] = @as(i64, f.b[i]) * g; | ||
| 277 | } | ||
| 278 | |||
| 279 | carry1(h, t[0..]); | ||
| 280 | } | ||
| 281 | |||
| 282 | fn mul(h: *Fe, f1: *const Fe, g1: *const Fe) void { | ||
| 283 | const f = f1.b; | ||
| 284 | const g = g1.b; | ||
| 285 | |||
| 286 | var F: [10]i32 = undefined; | ||
| 287 | var G: [10]i32 = undefined; | ||
| 288 | |||
| 289 | F[1] = f[1] * 2; | ||
| 290 | F[3] = f[3] * 2; | ||
| 291 | F[5] = f[5] * 2; | ||
| 292 | F[7] = f[7] * 2; | ||
| 293 | F[9] = f[9] * 2; | ||
| 294 | |||
| 295 | G[1] = g[1] * 19; | ||
| 296 | G[2] = g[2] * 19; | ||
| 297 | G[3] = g[3] * 19; | ||
| 298 | G[4] = g[4] * 19; | ||
| 299 | G[5] = g[5] * 19; | ||
| 300 | G[6] = g[6] * 19; | ||
| 301 | G[7] = g[7] * 19; | ||
| 302 | G[8] = g[8] * 19; | ||
| 303 | G[9] = g[9] * 19; | ||
| 304 | |||
| 305 | // t's become h | ||
| 306 | var t: [10]i64 = undefined; | ||
| 307 | |||
| 308 | t[0] = f[0] * @as(i64, g[0]) + F[1] * @as(i64, G[9]) + f[2] * @as(i64, G[8]) + F[3] * @as(i64, G[7]) + f[4] * @as(i64, G[6]) + F[5] * @as(i64, G[5]) + f[6] * @as(i64, G[4]) + F[7] * @as(i64, G[3]) + f[8] * @as(i64, G[2]) + F[9] * @as(i64, G[1]); | ||
| 309 | t[1] = f[0] * @as(i64, g[1]) + f[1] * @as(i64, g[0]) + f[2] * @as(i64, G[9]) + f[3] * @as(i64, G[8]) + f[4] * @as(i64, G[7]) + f[5] * @as(i64, G[6]) + f[6] * @as(i64, G[5]) + f[7] * @as(i64, G[4]) + f[8] * @as(i64, G[3]) + f[9] * @as(i64, G[2]); | ||
| 310 | t[2] = f[0] * @as(i64, g[2]) + F[1] * @as(i64, g[1]) + f[2] * @as(i64, g[0]) + F[3] * @as(i64, G[9]) + f[4] * @as(i64, G[8]) + F[5] * @as(i64, G[7]) + f[6] * @as(i64, G[6]) + F[7] * @as(i64, G[5]) + f[8] * @as(i64, G[4]) + F[9] * @as(i64, G[3]); | ||
| 311 | t[3] = f[0] * @as(i64, g[3]) + f[1] * @as(i64, g[2]) + f[2] * @as(i64, g[1]) + f[3] * @as(i64, g[0]) + f[4] * @as(i64, G[9]) + f[5] * @as(i64, G[8]) + f[6] * @as(i64, G[7]) + f[7] * @as(i64, G[6]) + f[8] * @as(i64, G[5]) + f[9] * @as(i64, G[4]); | ||
| 312 | t[4] = f[0] * @as(i64, g[4]) + F[1] * @as(i64, g[3]) + f[2] * @as(i64, g[2]) + F[3] * @as(i64, g[1]) + f[4] * @as(i64, g[0]) + F[5] * @as(i64, G[9]) + f[6] * @as(i64, G[8]) + F[7] * @as(i64, G[7]) + f[8] * @as(i64, G[6]) + F[9] * @as(i64, G[5]); | ||
| 313 | t[5] = f[0] * @as(i64, g[5]) + f[1] * @as(i64, g[4]) + f[2] * @as(i64, g[3]) + f[3] * @as(i64, g[2]) + f[4] * @as(i64, g[1]) + f[5] * @as(i64, g[0]) + f[6] * @as(i64, G[9]) + f[7] * @as(i64, G[8]) + f[8] * @as(i64, G[7]) + f[9] * @as(i64, G[6]); | ||
| 314 | t[6] = f[0] * @as(i64, g[6]) + F[1] * @as(i64, g[5]) + f[2] * @as(i64, g[4]) + F[3] * @as(i64, g[3]) + f[4] * @as(i64, g[2]) + F[5] * @as(i64, g[1]) + f[6] * @as(i64, g[0]) + F[7] * @as(i64, G[9]) + f[8] * @as(i64, G[8]) + F[9] * @as(i64, G[7]); | ||
| 315 | t[7] = f[0] * @as(i64, g[7]) + f[1] * @as(i64, g[6]) + f[2] * @as(i64, g[5]) + f[3] * @as(i64, g[4]) + f[4] * @as(i64, g[3]) + f[5] * @as(i64, g[2]) + f[6] * @as(i64, g[1]) + f[7] * @as(i64, g[0]) + f[8] * @as(i64, G[9]) + f[9] * @as(i64, G[8]); | ||
| 316 | t[8] = f[0] * @as(i64, g[8]) + F[1] * @as(i64, g[7]) + f[2] * @as(i64, g[6]) + F[3] * @as(i64, g[5]) + f[4] * @as(i64, g[4]) + F[5] * @as(i64, g[3]) + f[6] * @as(i64, g[2]) + F[7] * @as(i64, g[1]) + f[8] * @as(i64, g[0]) + F[9] * @as(i64, G[9]); | ||
| 317 | t[9] = f[0] * @as(i64, g[9]) + f[1] * @as(i64, g[8]) + f[2] * @as(i64, g[7]) + f[3] * @as(i64, g[6]) + f[4] * @as(i64, g[5]) + f[5] * @as(i64, g[4]) + f[6] * @as(i64, g[3]) + f[7] * @as(i64, g[2]) + f[8] * @as(i64, g[1]) + f[9] * @as(i64, g[0]); | ||
| 318 | |||
| 319 | carry2(h, t[0..]); | ||
| 320 | } | ||
| 321 | |||
| 322 | // we could use Fe.mul() for this, but this is significantly faster | ||
| 323 | fn sq(h: *Fe, fz: *const Fe) void { | ||
| 324 | const f0 = fz.b[0]; | ||
| 325 | const f1 = fz.b[1]; | ||
| 326 | const f2 = fz.b[2]; | ||
| 327 | const f3 = fz.b[3]; | ||
| 328 | const f4 = fz.b[4]; | ||
| 329 | const f5 = fz.b[5]; | ||
| 330 | const f6 = fz.b[6]; | ||
| 331 | const f7 = fz.b[7]; | ||
| 332 | const f8 = fz.b[8]; | ||
| 333 | const f9 = fz.b[9]; | ||
| 334 | |||
| 335 | const f0_2 = f0 * 2; | ||
| 336 | const f1_2 = f1 * 2; | ||
| 337 | const f2_2 = f2 * 2; | ||
| 338 | const f3_2 = f3 * 2; | ||
| 339 | const f4_2 = f4 * 2; | ||
| 340 | const f5_2 = f5 * 2; | ||
| 341 | const f6_2 = f6 * 2; | ||
| 342 | const f7_2 = f7 * 2; | ||
| 343 | const f5_38 = f5 * 38; | ||
| 344 | const f6_19 = f6 * 19; | ||
| 345 | const f7_38 = f7 * 38; | ||
| 346 | const f8_19 = f8 * 19; | ||
| 347 | const f9_38 = f9 * 38; | ||
| 348 | |||
| 349 | var t: [10]i64 = undefined; | ||
| 350 | |||
| 351 | t[0] = f0 * @as(i64, f0) + f1_2 * @as(i64, f9_38) + f2_2 * @as(i64, f8_19) + f3_2 * @as(i64, f7_38) + f4_2 * @as(i64, f6_19) + f5 * @as(i64, f5_38); | ||
| 352 | t[1] = f0_2 * @as(i64, f1) + f2 * @as(i64, f9_38) + f3_2 * @as(i64, f8_19) + f4 * @as(i64, f7_38) + f5_2 * @as(i64, f6_19); | ||
| 353 | t[2] = f0_2 * @as(i64, f2) + f1_2 * @as(i64, f1) + f3_2 * @as(i64, f9_38) + f4_2 * @as(i64, f8_19) + f5_2 * @as(i64, f7_38) + f6 * @as(i64, f6_19); | ||
| 354 | t[3] = f0_2 * @as(i64, f3) + f1_2 * @as(i64, f2) + f4 * @as(i64, f9_38) + f5_2 * @as(i64, f8_19) + f6 * @as(i64, f7_38); | ||
| 355 | t[4] = f0_2 * @as(i64, f4) + f1_2 * @as(i64, f3_2) + f2 * @as(i64, f2) + f5_2 * @as(i64, f9_38) + f6_2 * @as(i64, f8_19) + f7 * @as(i64, f7_38); | ||
| 356 | t[5] = f0_2 * @as(i64, f5) + f1_2 * @as(i64, f4) + f2_2 * @as(i64, f3) + f6 * @as(i64, f9_38) + f7_2 * @as(i64, f8_19); | ||
| 357 | t[6] = f0_2 * @as(i64, f6) + f1_2 * @as(i64, f5_2) + f2_2 * @as(i64, f4) + f3_2 * @as(i64, f3) + f7_2 * @as(i64, f9_38) + f8 * @as(i64, f8_19); | ||
| 358 | t[7] = f0_2 * @as(i64, f7) + f1_2 * @as(i64, f6) + f2_2 * @as(i64, f5) + f3_2 * @as(i64, f4) + f8 * @as(i64, f9_38); | ||
| 359 | t[8] = f0_2 * @as(i64, f8) + f1_2 * @as(i64, f7_2) + f2_2 * @as(i64, f6) + f3_2 * @as(i64, f5_2) + f4 * @as(i64, f4) + f9 * @as(i64, f9_38); | ||
| 360 | t[9] = f0_2 * @as(i64, f9) + f1_2 * @as(i64, f8) + f2_2 * @as(i64, f7) + f3_2 * @as(i64, f6) + f4 * @as(i64, f5_2); | ||
| 361 | |||
| 362 | carry2(h, t[0..]); | ||
| 363 | } | ||
| 364 | |||
| 365 | fn sq2(h: *Fe, f: *const Fe) void { | ||
| 366 | Fe.sq(h, f); | ||
| 367 | Fe.mul_small(h, h, 2); | ||
| 368 | } | ||
| 369 | |||
| 370 | // This could be simplified, but it would be slower | ||
| 371 | fn invert(out: *Fe, z: *const Fe) void { | ||
| 372 | var i: usize = undefined; | ||
| 373 | |||
| 374 | var t: [4]Fe = undefined; | ||
| 375 | var t0 = &t[0]; | ||
| 376 | var t1 = &t[1]; | ||
| 377 | var t2 = &t[2]; | ||
| 378 | var t3 = &t[3]; | ||
| 379 | |||
| 380 | Fe.sq(t0, z); | ||
| 381 | Fe.sq(t1, t0); | ||
| 382 | Fe.sq(t1, t1); | ||
| 383 | Fe.mul(t1, z, t1); | ||
| 384 | Fe.mul(t0, t0, t1); | ||
| 385 | |||
| 386 | Fe.sq(t2, t0); | ||
| 387 | Fe.mul(t1, t1, t2); | ||
| 388 | |||
| 389 | Fe.sq(t2, t1); | ||
| 390 | i = 1; | ||
| 391 | while (i < 5) : (i += 1) Fe.sq(t2, t2); | ||
| 392 | Fe.mul(t1, t2, t1); | ||
| 393 | |||
| 394 | Fe.sq(t2, t1); | ||
| 395 | i = 1; | ||
| 396 | while (i < 10) : (i += 1) Fe.sq(t2, t2); | ||
| 397 | Fe.mul(t2, t2, t1); | ||
| 398 | |||
| 399 | Fe.sq(t3, t2); | ||
| 400 | i = 1; | ||
| 401 | while (i < 20) : (i += 1) Fe.sq(t3, t3); | ||
| 402 | Fe.mul(t2, t3, t2); | ||
| 403 | |||
| 404 | Fe.sq(t2, t2); | ||
| 405 | i = 1; | ||
| 406 | while (i < 10) : (i += 1) Fe.sq(t2, t2); | ||
| 407 | Fe.mul(t1, t2, t1); | ||
| 408 | |||
| 409 | Fe.sq(t2, t1); | ||
| 410 | i = 1; | ||
| 411 | while (i < 50) : (i += 1) Fe.sq(t2, t2); | ||
| 412 | Fe.mul(t2, t2, t1); | ||
| 413 | |||
| 414 | Fe.sq(t3, t2); | ||
| 415 | i = 1; | ||
| 416 | while (i < 100) : (i += 1) Fe.sq(t3, t3); | ||
| 417 | Fe.mul(t2, t3, t2); | ||
| 418 | |||
| 419 | Fe.sq(t2, t2); | ||
| 420 | i = 1; | ||
| 421 | while (i < 50) : (i += 1) Fe.sq(t2, t2); | ||
| 422 | Fe.mul(t1, t2, t1); | ||
| 423 | |||
| 424 | Fe.sq(t1, t1); | ||
| 425 | i = 1; | ||
| 426 | while (i < 5) : (i += 1) Fe.sq(t1, t1); | ||
| 427 | Fe.mul(out, t1, t0); | ||
| 428 | |||
| 429 | t0.secureZero(); | ||
| 430 | t1.secureZero(); | ||
| 431 | t2.secureZero(); | ||
| 432 | t3.secureZero(); | ||
| 433 | } | ||
| 434 | |||
| 435 | // This could be simplified, but it would be slower | ||
| 436 | fn pow22523(out: *Fe, z: *const Fe) void { | ||
| 437 | var i: usize = undefined; | ||
| 438 | |||
| 439 | var t: [3]Fe = undefined; | ||
| 440 | var t0 = &t[0]; | ||
| 441 | var t1 = &t[1]; | ||
| 442 | var t2 = &t[2]; | ||
| 443 | |||
| 444 | Fe.sq(t0, z); | ||
| 445 | Fe.sq(t1, t0); | ||
| 446 | Fe.sq(t1, t1); | ||
| 447 | Fe.mul(t1, z, t1); | ||
| 448 | Fe.mul(t0, t0, t1); | ||
| 449 | |||
| 450 | Fe.sq(t0, t0); | ||
| 451 | Fe.mul(t0, t1, t0); | ||
| 452 | |||
| 453 | Fe.sq(t1, t0); | ||
| 454 | i = 1; | ||
| 455 | while (i < 5) : (i += 1) Fe.sq(t1, t1); | ||
| 456 | Fe.mul(t0, t1, t0); | ||
| 457 | |||
| 458 | Fe.sq(t1, t0); | ||
| 459 | i = 1; | ||
| 460 | while (i < 10) : (i += 1) Fe.sq(t1, t1); | ||
| 461 | Fe.mul(t1, t1, t0); | ||
| 462 | |||
| 463 | Fe.sq(t2, t1); | ||
| 464 | i = 1; | ||
| 465 | while (i < 20) : (i += 1) Fe.sq(t2, t2); | ||
| 466 | Fe.mul(t1, t2, t1); | ||
| 467 | |||
| 468 | Fe.sq(t1, t1); | ||
| 469 | i = 1; | ||
| 470 | while (i < 10) : (i += 1) Fe.sq(t1, t1); | ||
| 471 | Fe.mul(t0, t1, t0); | ||
| 472 | |||
| 473 | Fe.sq(t1, t0); | ||
| 474 | i = 1; | ||
| 475 | while (i < 50) : (i += 1) Fe.sq(t1, t1); | ||
| 476 | Fe.mul(t1, t1, t0); | ||
| 477 | |||
| 478 | Fe.sq(t2, t1); | ||
| 479 | i = 1; | ||
| 480 | while (i < 100) : (i += 1) Fe.sq(t2, t2); | ||
| 481 | Fe.mul(t1, t2, t1); | ||
| 482 | |||
| 483 | Fe.sq(t1, t1); | ||
| 484 | i = 1; | ||
| 485 | while (i < 50) : (i += 1) Fe.sq(t1, t1); | ||
| 486 | Fe.mul(t0, t1, t0); | ||
| 487 | |||
| 488 | Fe.sq(t0, t0); | ||
| 489 | i = 1; | ||
| 490 | while (i < 2) : (i += 1) Fe.sq(t0, t0); | ||
| 491 | Fe.mul(out, t0, z); | ||
| 492 | |||
| 493 | t0.secureZero(); | ||
| 494 | t1.secureZero(); | ||
| 495 | t2.secureZero(); | ||
| 496 | } | ||
| 497 | |||
| 498 | inline fn toBytesRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int) void { | ||
| 499 | c[i] = t[i] >> shift; | ||
| 500 | if (i + 1 < 10) { | ||
| 501 | t[i + 1] += c[i]; | ||
| 502 | } | ||
| 503 | t[i] -= c[i] * (@as(i32, 1) << shift); | ||
| 504 | } | ||
| 505 | |||
| 506 | fn toBytes(s: []u8, h: *const Fe) void { | ||
| 507 | std.debug.assert(s.len >= 32); | ||
| 508 | |||
| 509 | var t: [10]i64 = undefined; | ||
| 510 | for (h.b[0..]) |_, i| { | ||
| 511 | t[i] = h.b[i]; | ||
| 512 | } | ||
| 513 | |||
| 514 | var q = (19 * t[9] + ((@as(i32, 1) << 24))) >> 25; | ||
| 515 | { | ||
| 516 | var i: usize = 0; | ||
| 517 | while (i < 5) : (i += 1) { | ||
| 518 | q += t[2 * i]; | ||
| 519 | q >>= 26; | ||
| 520 | q += t[2 * i + 1]; | ||
| 521 | q >>= 25; | ||
| 522 | } | ||
| 523 | } | ||
| 524 | t[0] += 19 * q; | ||
| 525 | |||
| 526 | var c: [10]i64 = undefined; | ||
| 527 | |||
| 528 | var st = t[0..]; | ||
| 529 | var sc = c[0..]; | ||
| 530 | |||
| 531 | toBytesRound(sc, st, 0, 26); | ||
| 532 | toBytesRound(sc, st, 1, 25); | ||
| 533 | toBytesRound(sc, st, 2, 26); | ||
| 534 | toBytesRound(sc, st, 3, 25); | ||
| 535 | toBytesRound(sc, st, 4, 26); | ||
| 536 | toBytesRound(sc, st, 5, 25); | ||
| 537 | toBytesRound(sc, st, 6, 26); | ||
| 538 | toBytesRound(sc, st, 7, 25); | ||
| 539 | toBytesRound(sc, st, 8, 26); | ||
| 540 | toBytesRound(sc, st, 9, 25); | ||
| 541 | |||
| 542 | var ut: [10]u32 = undefined; | ||
| 543 | for (ut[0..]) |_, i| { | ||
| 544 | ut[i] = @bitCast(u32, @intCast(i32, t[i])); | ||
| 545 | } | ||
| 546 | |||
| 547 | writeIntLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26)); | ||
| 548 | writeIntLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19)); | ||
| 549 | writeIntLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13)); | ||
| 550 | writeIntLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6)); | ||
| 551 | writeIntLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25)); | ||
| 552 | writeIntLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19)); | ||
| 553 | writeIntLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12)); | ||
| 554 | writeIntLittle(u32, s[28..32], (ut[8] >> 20) | (ut[9] << 6)); | ||
| 555 | |||
| 556 | std.mem.secureZero(i64, t[0..]); | ||
| 557 | } | ||
| 558 | |||
| 559 | // Parity check. Returns 0 if even, 1 if odd | ||
| 560 | fn isNegative(f: *const Fe) bool { | ||
| 561 | var s: [32]u8 = undefined; | ||
| 562 | Fe.toBytes(s[0..], f); | ||
| 563 | const isneg = s[0] & 1; | ||
| 564 | s.secureZero(); | ||
| 565 | return isneg; | ||
| 566 | } | ||
| 567 | |||
| 568 | fn isNonZero(f: *const Fe) bool { | ||
| 569 | var s: [32]u8 = undefined; | ||
| 570 | Fe.toBytes(s[0..], f); | ||
| 571 | const isnonzero = zerocmp(u8, s[0..]); | ||
| 572 | s.secureZero(); | ||
| 573 | return isneg; | ||
| 574 | } | ||
| 575 | }; | ||
| 576 | |||
| 577 | test "x25519 public key calculation from secret key" { | ||
| 578 | var sk: [32]u8 = undefined; | ||
| 579 | var pk_expected: [32]u8 = undefined; | ||
| 580 | var pk_calculated: [32]u8 = undefined; | ||
| 581 | try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); | ||
| 582 | try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); | ||
| 583 | std.testing.expect(X25519.createPublicKey(pk_calculated[0..], &sk)); | ||
| 584 | std.testing.expect(std.mem.eql(u8, &pk_calculated, &pk_expected)); | ||
| 585 | } | ||
| 586 | |||
| 587 | test "x25519 rfc7748 vector1" { | ||
| 588 | const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4"; | ||
| 589 | const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c"; | ||
| 590 | |||
| 591 | const expected_output = "\xc3\xda\x55\x37\x9d\xe9\xc6\x90\x8e\x94\xea\x4d\xf2\x8d\x08\x4f\x32\xec\xcf\x03\x49\x1c\x71\xf7\x54\xb4\x07\x55\x77\xa2\x85\x52"; | ||
| 592 | |||
| 593 | var output: [32]u8 = undefined; | ||
| 594 | |||
| 595 | std.testing.expect(X25519.create(output[0..], secret_key, public_key)); | ||
| 596 | std.testing.expect(std.mem.eql(u8, &output, expected_output)); | ||
| 597 | } | ||
| 598 | |||
| 599 | test "x25519 rfc7748 vector2" { | ||
| 600 | const secret_key = "\x4b\x66\xe9\xd4\xd1\xb4\x67\x3c\x5a\xd2\x26\x91\x95\x7d\x6a\xf5\xc1\x1b\x64\x21\xe0\xea\x01\xd4\x2c\xa4\x16\x9e\x79\x18\xba\x0d"; | ||
| 601 | const public_key = "\xe5\x21\x0f\x12\x78\x68\x11\xd3\xf4\xb7\x95\x9d\x05\x38\xae\x2c\x31\xdb\xe7\x10\x6f\xc0\x3c\x3e\xfc\x4c\xd5\x49\xc7\x15\xa4\x93"; | ||
| 602 | |||
| 603 | const expected_output = "\x95\xcb\xde\x94\x76\xe8\x90\x7d\x7a\xad\xe4\x5c\xb4\xb8\x73\xf8\x8b\x59\x5a\x68\x79\x9f\xa1\x52\xe6\xf8\xf7\x64\x7a\xac\x79\x57"; | ||
| 604 | |||
| 605 | var output: [32]u8 = undefined; | ||
| 606 | |||
| 607 | std.testing.expect(X25519.create(output[0..], secret_key, public_key)); | ||
| 608 | std.testing.expect(std.mem.eql(u8, &output, expected_output)); | ||
| 609 | } | ||
| 610 | |||
| 611 | test "x25519 rfc7748 one iteration" { | ||
| 612 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*; | ||
| 613 | const expected_output = "\x42\x2c\x8e\x7a\x62\x27\xd7\xbc\xa1\x35\x0b\x3e\x2b\xb7\x27\x9f\x78\x97\xb8\x7b\xb6\x85\x4b\x78\x3c\x60\xe8\x03\x11\xae\x30\x79"; | ||
| 614 | |||
| 615 | var k: [32]u8 = initial_value; | ||
| 616 | var u: [32]u8 = initial_value; | ||
| 617 | |||
| 618 | var i: usize = 0; | ||
| 619 | while (i < 1) : (i += 1) { | ||
| 620 | var output: [32]u8 = undefined; | ||
| 621 | std.testing.expect(X25519.create(output[0..], &k, &u)); | ||
| 622 | |||
| 623 | std.mem.copy(u8, u[0..], k[0..]); | ||
| 624 | std.mem.copy(u8, k[0..], output[0..]); | ||
| 625 | } | ||
| 626 | |||
| 627 | std.testing.expect(std.mem.eql(u8, k[0..], expected_output)); | ||
| 628 | } | ||
| 629 | |||
| 630 | test "x25519 rfc7748 1,000 iterations" { | ||
| 631 | // These iteration tests are slow so we always skip them. Results have been verified. | ||
| 632 | if (true) { | ||
| 633 | return error.SkipZigTest; | ||
| 634 | } | ||
| 635 | |||
| 636 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; | ||
| 637 | const expected_output = "\x68\x4c\xf5\x9b\xa8\x33\x09\x55\x28\x00\xef\x56\x6f\x2f\x4d\x3c\x1c\x38\x87\xc4\x93\x60\xe3\x87\x5f\x2e\xb9\x4d\x99\x53\x2c\x51"; | ||
| 638 | |||
| 639 | var k: [32]u8 = initial_value.*; | ||
| 640 | var u: [32]u8 = initial_value.*; | ||
| 641 | |||
| 642 | var i: usize = 0; | ||
| 643 | while (i < 1000) : (i += 1) { | ||
| 644 | var output: [32]u8 = undefined; | ||
| 645 | std.testing.expect(X25519.create(output[0..], &k, &u)); | ||
| 646 | |||
| 647 | std.mem.copy(u8, u[0..], k[0..]); | ||
| 648 | std.mem.copy(u8, k[0..], output[0..]); | ||
| 649 | } | ||
| 650 | |||
| 651 | std.testing.expect(std.mem.eql(u8, k[0..], expected_output)); | ||
| 652 | } | ||
| 653 | |||
| 654 | test "x25519 rfc7748 1,000,000 iterations" { | ||
| 655 | if (true) { | ||
| 656 | return error.SkipZigTest; | ||
| 657 | } | ||
| 658 | |||
| 659 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; | ||
| 660 | const expected_output = "\x7c\x39\x11\xe0\xab\x25\x86\xfd\x86\x44\x97\x29\x7e\x57\x5e\x6f\x3b\xc6\x01\xc0\x88\x3c\x30\xdf\x5f\x4d\xd2\xd2\x4f\x66\x54\x24"; | ||
| 661 | |||
| 662 | var k: [32]u8 = initial_value.*; | ||
| 663 | var u: [32]u8 = initial_value.*; | ||
| 664 | |||
| 665 | var i: usize = 0; | ||
| 666 | while (i < 1000000) : (i += 1) { | ||
| 667 | var output: [32]u8 = undefined; | ||
| 668 | std.testing.expect(X25519.create(output[0..], &k, &u)); | ||
| 669 | |||
| 670 | std.mem.copy(u8, u[0..], k[0..]); | ||
| 671 | std.mem.copy(u8, k[0..], output[0..]); | ||
| 672 | } | ||
| 673 | |||
| 674 | std.testing.expect(std.mem.eql(u8, k[0..], expected_output)); | ||
| 675 | } | ||