diff options
| author | Frank Denis <github@pureftpd.org> | 2020-10-26 12:51:57 +0100 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-10-29 14:39:58 -0400 |
| commit | e59dd7eecfe9ff3a2eb82c630d5021fbd280011d (patch) | |
| tree | 543cd7f00fdb323b804d092d3462d339a70a2091 /lib/std | |
| parent | ad6e095ef676ab50799a49eb40419b1ff926e6d7 (diff) | |
| download | zig-e59dd7eecfe9ff3a2eb82c630d5021fbd280011d.tar.gz zig-e59dd7eecfe9ff3a2eb82c630d5021fbd280011d.zip | |
std/crypto/x25519: return encoded points directly + ed->mont map
Leverage result location semantics for X25519 like we do everywhere
else in 25519/*
Also add the edwards25519->curve25519 map by the way since many
applications seem to use this to share the same key pair for encryption
and signature.
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/crypto/25519/curve25519.zig | 8 | ||||
| -rw-r--r-- | lib/std/crypto/25519/edwards25519.zig | 6 | ||||
| -rw-r--r-- | lib/std/crypto/25519/ristretto255.zig | 12 | ||||
| -rw-r--r-- | lib/std/crypto/25519/x25519.zig | 65 | ||||
| -rw-r--r-- | lib/std/crypto/benchmark.zig | 12 | ||||
| -rw-r--r-- | lib/std/crypto/salsa20.zig | 3 |
6 files changed, 70 insertions, 36 deletions
diff --git a/lib/std/crypto/25519/curve25519.zig b/lib/std/crypto/25519/curve25519.zig index 7f180eac2f..3ca7af7a41 100644 --- a/lib/std/crypto/25519/curve25519.zig +++ b/lib/std/crypto/25519/curve25519.zig @@ -100,6 +100,14 @@ pub const Curve25519 = struct { _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey; return try ladder(p, s, 256); } + + /// Compute the Curve25519 equivalent to an Edwards25519 point. + pub fn fromEdwards25519(p: std.crypto.ecc.Edwards25519) !Curve25519 { + try p.clearCofactor().rejectIdentity(); + const one = std.crypto.ecc.Edwards25519.Fe.one; + const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd) + return Curve25519{ .x = x }; + } }; test "curve25519" { diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig index 74ea89a952..3e34576f78 100644 --- a/lib/std/crypto/25519/edwards25519.zig +++ b/lib/std/crypto/25519/edwards25519.zig @@ -12,6 +12,8 @@ pub const Edwards25519 = struct { pub const Fe = @import("field.zig").Fe; /// Field arithmetic mod the order of the main subgroup. pub const scalar = @import("scalar.zig"); + /// Length in bytes of a compressed representation of a point. + pub const encoded_length: usize = 32; x: Fe, y: Fe, @@ -21,7 +23,7 @@ pub const Edwards25519 = struct { is_base: bool = false, /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates. - pub fn fromBytes(s: [32]u8) !Edwards25519 { + pub fn fromBytes(s: [encoded_length]u8) !Edwards25519 { const z = Fe.one; const y = Fe.fromBytes(s); var u = y.sq(); @@ -43,7 +45,7 @@ pub const Edwards25519 = struct { } /// Encode an Edwards25519 point. - pub fn toBytes(p: Edwards25519) [32]u8 { + pub fn toBytes(p: Edwards25519) [encoded_length]u8 { const zi = p.z.invert(); var s = p.y.mul(zi).toBytes(); s[31] ^= @as(u8, @boolToInt(p.x.mul(zi).isNegative())) << 7; diff --git a/lib/std/crypto/25519/ristretto255.zig b/lib/std/crypto/25519/ristretto255.zig index 4e6494ed38..16d301592a 100644 --- a/lib/std/crypto/25519/ristretto255.zig +++ b/lib/std/crypto/25519/ristretto255.zig @@ -14,6 +14,8 @@ pub const Ristretto255 = struct { pub const Fe = Curve.Fe; /// Field arithmetic mod the order of the main subgroup. pub const scalar = Curve.scalar; + /// Length in byte of an encoded element. + pub const encoded_length: usize = 32; p: Curve, @@ -32,7 +34,7 @@ pub const Ristretto255 = struct { return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() }; } - fn rejectNonCanonical(s: [32]u8) !void { + fn rejectNonCanonical(s: [encoded_length]u8) !void { if ((s[0] & 1) != 0) { return error.NonCanonical; } @@ -48,7 +50,7 @@ pub const Ristretto255 = struct { pub const basePoint = Ristretto255{ .p = Curve.basePoint }; /// Decode a Ristretto255 representative. - pub fn fromBytes(s: [32]u8) !Ristretto255 { + pub fn fromBytes(s: [encoded_length]u8) !Ristretto255 { try rejectNonCanonical(s); const s_ = Fe.fromBytes(s); const ss = s_.sq(); // s^2 @@ -78,7 +80,7 @@ pub const Ristretto255 = struct { } /// Encode to a Ristretto255 representative. - pub fn toBytes(e: Ristretto255) [32]u8 { + pub fn toBytes(e: Ristretto255) [encoded_length]u8 { const p = &e.p; var u1_ = p.z.add(p.y); // Z+Y const zmy = p.z.sub(p.y); // Z-Y @@ -151,7 +153,7 @@ pub const Ristretto255 = struct { /// Multiply a Ristretto255 element with a scalar. /// Return error.WeakPublicKey if the resulting element is /// the identity element. - pub inline fn mul(p: Ristretto255, s: [32]u8) !Ristretto255 { + pub inline fn mul(p: Ristretto255, s: [encoded_length]u8) !Ristretto255 { return Ristretto255{ .p = try p.p.mul(s) }; } @@ -170,7 +172,7 @@ test "ristretto255" { var buf: [256]u8 = undefined; std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76"); - var r: [32]u8 = undefined; + var r: [Ristretto255.encoded_length]u8 = undefined; try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); var q = try Ristretto255.fromBytes(r); q = q.dbl().add(p); diff --git a/lib/std/crypto/25519/x25519.zig b/lib/std/crypto/25519/x25519.zig index 17c2e84e65..3b3ff551fe 100644 --- a/lib/std/crypto/25519/x25519.zig +++ b/lib/std/crypto/25519/x25519.zig @@ -8,6 +8,8 @@ const crypto = std.crypto; const mem = std.mem; const fmt = std.fmt; +const Sha512 = crypto.hash.sha2.Sha512; + /// X25519 DH function. pub const X25519 = struct { /// The underlying elliptic curve. @@ -37,33 +39,55 @@ pub const X25519 = struct { }; var kp: KeyPair = undefined; mem.copy(u8, &kp.secret_key, sk[0..]); - try X25519.recoverPublicKey(&kp.public_key, sk); + kp.public_key = try X25519.recoverPublicKey(sk); return kp; } + + /// Create a key pair from an Ed25519 key pair + pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) !KeyPair { + const seed = ed25519_key_pair.secret_key[0..32]; + var az: [Sha512.digest_length]u8 = undefined; + Sha512.hash(seed, &az, .{}); + var sk = az[0..32].*; + Curve.scalar.clamp(&sk); + const pk = try publicKeyFromEd25519(ed25519_key_pair.public_key); + return KeyPair{ + .public_key = pk, + .secret_key = sk, + }; + } }; /// Compute the public key for a given private key. - pub fn recoverPublicKey(public_key: *[public_length]u8, secret_key: [secret_length]u8) !void { + pub fn recoverPublicKey(secret_key: [secret_length]u8) ![public_length]u8 { const q = try Curve.basePoint.clampedMul(secret_key); - mem.copy(u8, public_key, q.toBytes()[0..]); + return q.toBytes(); + } + + /// Compute the X25519 equivalent to an Ed25519 public eky. + pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) ![public_length]u8 { + const pk_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key); + const pk = try Curve.fromEdwards25519(pk_ed); + return pk.toBytes(); } /// Compute the scalar product of a public key and a secret scalar. /// Note that the output should not be used as a shared secret without /// hashing it first. - pub fn scalarmult(out: *[shared_length]u8, secret_key: [secret_length]u8, public_key: [public_length]u8) !void { + pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) ![shared_length]u8 { const q = try Curve.fromBytes(public_key).clampedMul(secret_key); - mem.copy(u8, out, q.toBytes()[0..]); + return q.toBytes(); } }; +const htest = @import("../test.zig"); + test "x25519 public key calculation from secret key" { var sk: [32]u8 = undefined; var pk_expected: [32]u8 = undefined; - var pk_calculated: [32]u8 = undefined; try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); - try X25519.recoverPublicKey(&pk_calculated, sk); + const pk_calculated = try X25519.recoverPublicKey(sk); std.testing.expectEqual(pk_calculated, pk_expected); } @@ -73,9 +97,7 @@ test "x25519 rfc7748 vector1" { 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 }; - var output: [32]u8 = undefined; - - try X25519.scalarmult(&output, secret_key, public_key); + const output = try X25519.scalarmult(secret_key, public_key); std.testing.expectEqual(output, expected_output); } @@ -85,9 +107,7 @@ test "x25519 rfc7748 vector2" { 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 }; - var output: [32]u8 = undefined; - - try X25519.scalarmult(&output, secret_key, public_key); + const output = try X25519.scalarmult(secret_key, public_key); std.testing.expectEqual(output, expected_output); } @@ -100,9 +120,7 @@ test "x25519 rfc7748 one iteration" { var i: usize = 0; while (i < 1) : (i += 1) { - var output: [32]u8 = undefined; - try X25519.scalarmult(output[0..], k, u); - + const output = try X25519.scalarmult(k, u); mem.copy(u8, u[0..], k[0..]); mem.copy(u8, k[0..], output[0..]); } @@ -124,9 +142,7 @@ test "x25519 rfc7748 1,000 iterations" { var i: usize = 0; while (i < 1000) : (i += 1) { - var output: [32]u8 = undefined; - std.testing.expect(X25519.scalarmult(output[0..], &k, &u)); - + const output = try X25519.scalarmult(&k, &u); mem.copy(u8, u[0..], k[0..]); mem.copy(u8, k[0..], output[0..]); } @@ -147,12 +163,17 @@ test "x25519 rfc7748 1,000,000 iterations" { var i: usize = 0; while (i < 1000000) : (i += 1) { - var output: [32]u8 = undefined; - std.testing.expect(X25519.scalarmult(output[0..], &k, &u)); - + const output = try X25519.scalarmult(&k, &u); mem.copy(u8, u[0..], k[0..]); mem.copy(u8, k[0..], output[0..]); } std.testing.expectEqual(k[0..], expected_output); } + +test "edwards25519 -> curve25519 map" { + const ed_kp = try crypto.sign.Ed25519.KeyPair.create([_]u8{0x42} ** 32); + const mont_kp = try X25519.KeyPair.fromEd25519(ed_kp); + htest.assertEqual("90e7595fc89e52fdfddce9c6a43d74dbf6047025ee0462d2d172e8b6a2841d6e", &mont_kp.secret_key); + htest.assertEqual("cc4f2cdb695dd766f34118eb67b98652fed1d8bc49c330b119bbfa8a64989378", &mont_kp.public_key); +} diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index d27a4624f7..5b6a815f0c 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -98,18 +98,20 @@ const exchanges = [_]Crypto{Crypto{ .ty = crypto.dh.X25519, .name = "x25519" }}; pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_count: comptime_int) !u64 { std.debug.assert(DhKeyExchange.shared_length >= DhKeyExchange.secret_length); - var in: [DhKeyExchange.shared_length]u8 = undefined; - prng.random.bytes(in[0..]); + var secret: [DhKeyExchange.shared_length]u8 = undefined; + prng.random.bytes(secret[0..]); - var out: [DhKeyExchange.shared_length]u8 = undefined; - prng.random.bytes(out[0..]); + var public: [DhKeyExchange.shared_length]u8 = undefined; + prng.random.bytes(public[0..]); var timer = try Timer.start(); const start = timer.lap(); { var i: usize = 0; while (i < exchange_count) : (i += 1) { - try DhKeyExchange.scalarmult(&out, out, in); + const out = try DhKeyExchange.scalarmult(secret, public); + mem.copy(u8, secret[0..16], out[0..16]); + mem.copy(u8, public[0..16], out[16..32]); mem.doNotOptimizeAway(&out); } } diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig index 7d367367fe..ccfb6ecbad 100644 --- a/lib/std/crypto/salsa20.zig +++ b/lib/std/crypto/salsa20.zig @@ -485,8 +485,7 @@ pub const Box = struct { /// Compute a secret suitable for `secretbox` given a recipent's public key and a sender's secret key. pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) ![shared_length]u8 { - var p: [32]u8 = undefined; - try X25519.scalarmult(&p, secret_key, public_key); + const p = try X25519.scalarmult(secret_key, public_key); const zero = [_]u8{0} ** 16; return Salsa20Impl.hsalsa20(zero, p); } |
