mirror of
https://github.com/ziglang/zig.git
synced 2025-12-12 17:23:09 +00:00
Move ed25519 key pairs to a KeyPair structure
This commit is contained in:
parent
28fb97f188
commit
0c7a99b38d
@ -5,6 +5,7 @@
|
|||||||
// and substantial portions of the software.
|
// and substantial portions of the software.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const crypto = std.crypto;
|
const crypto = std.crypto;
|
||||||
|
const debug = std.debug;
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const Sha512 = std.crypto.hash.sha2.Sha512;
|
const Sha512 = std.crypto.hash.sha2.Sha512;
|
||||||
@ -15,8 +16,8 @@ pub const Ed25519 = struct {
|
|||||||
pub const Curve = @import("edwards25519.zig").Edwards25519;
|
pub const Curve = @import("edwards25519.zig").Edwards25519;
|
||||||
/// Length (in bytes) of a seed required to create a key pair.
|
/// Length (in bytes) of a seed required to create a key pair.
|
||||||
pub const seed_length = 32;
|
pub const seed_length = 32;
|
||||||
/// Length (in bytes) of a compressed key pair.
|
/// Length (in bytes) of a compressed secret key.
|
||||||
pub const keypair_length = 64;
|
pub const secret_length = 64;
|
||||||
/// Length (in bytes) of a compressed public key.
|
/// Length (in bytes) of a compressed public key.
|
||||||
pub const public_length = 32;
|
pub const public_length = 32;
|
||||||
/// Length (in bytes) of a signature.
|
/// Length (in bytes) of a signature.
|
||||||
@ -24,46 +25,61 @@ pub const Ed25519 = struct {
|
|||||||
/// Length (in bytes) of optional random bytes, for non-deterministic signatures.
|
/// Length (in bytes) of optional random bytes, for non-deterministic signatures.
|
||||||
pub const noise_length = 32;
|
pub const noise_length = 32;
|
||||||
|
|
||||||
/// Derive a key pair from a secret seed.
|
/// An Ed25519 key pair.
|
||||||
///
|
pub const KeyPair = struct {
|
||||||
/// As in RFC 8032, an Ed25519 public key is generated by hashing
|
/// Public part.
|
||||||
/// the secret key using the SHA-512 function, and interpreting the
|
public_key: [public_length]u8,
|
||||||
/// bit-swapped, clamped lower-half of the output as the secret scalar.
|
/// Secret part. What we expose as a secret key is, under the hood, the concatenation of the seed and the public key.
|
||||||
///
|
secret_key: [secret_length]u8,
|
||||||
/// For this reason, an EdDSA secret key is commonly called a seed,
|
|
||||||
/// from which the actual secret is derived.
|
|
||||||
pub fn createKeyPair(seed: ?[seed_length]u8) ![keypair_length]u8 {
|
|
||||||
const sk = seed orelse sk: {
|
|
||||||
var random_seed: [seed_length]u8 = undefined;
|
|
||||||
try crypto.randomBytes(&random_seed);
|
|
||||||
break :sk random_seed;
|
|
||||||
};
|
|
||||||
var az: [Sha512.digest_length]u8 = undefined;
|
|
||||||
var h = Sha512.init(.{});
|
|
||||||
h.update(&sk);
|
|
||||||
h.final(&az);
|
|
||||||
const p = try Curve.basePoint.clampedMul(az[0..32].*);
|
|
||||||
var keypair: [keypair_length]u8 = undefined;
|
|
||||||
mem.copy(u8, &keypair, &sk);
|
|
||||||
mem.copy(u8, keypair[seed_length..], &p.toBytes());
|
|
||||||
return keypair;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the public key for a given key pair.
|
/// Derive a key pair from an optional secret seed.
|
||||||
pub fn publicKey(key_pair: [keypair_length]u8) [public_length]u8 {
|
///
|
||||||
var public_key: [public_length]u8 = undefined;
|
/// As in RFC 8032, an Ed25519 public key is generated by hashing
|
||||||
mem.copy(u8, public_key[0..], key_pair[seed_length..]);
|
/// the secret key using the SHA-512 function, and interpreting the
|
||||||
return public_key;
|
/// bit-swapped, clamped lower-half of the output as the secret scalar.
|
||||||
}
|
///
|
||||||
|
/// For this reason, an EdDSA secret key is commonly called a seed,
|
||||||
|
/// from which the actual secret is derived.
|
||||||
|
pub fn create(seed: ?[seed_length]u8) !KeyPair {
|
||||||
|
const ss = seed orelse ss: {
|
||||||
|
var random_seed: [seed_length]u8 = undefined;
|
||||||
|
try crypto.randomBytes(&random_seed);
|
||||||
|
break :ss random_seed;
|
||||||
|
};
|
||||||
|
var az: [Sha512.digest_length]u8 = undefined;
|
||||||
|
var h = Sha512.init(.{});
|
||||||
|
h.update(&ss);
|
||||||
|
h.final(&az);
|
||||||
|
const p = try Curve.basePoint.clampedMul(az[0..32].*);
|
||||||
|
var sk: [secret_length]u8 = undefined;
|
||||||
|
mem.copy(u8, &sk, &ss);
|
||||||
|
const pk = p.toBytes();
|
||||||
|
mem.copy(u8, sk[seed_length..], &pk);
|
||||||
|
|
||||||
|
return KeyPair{ .public_key = pk, .secret_key = sk };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a KeyPair from a secret key.
|
||||||
|
pub fn fromSecretKey(secret_key: [secret_length]u8) KeyPair {
|
||||||
|
return KeyPair{
|
||||||
|
.secret_key = secret_key,
|
||||||
|
.public_key = secret_key[seed_length..].*,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Sign a message using a key pair, and optional random noise.
|
/// Sign a message using a key pair, and optional random noise.
|
||||||
/// Having noise creates non-standard, non-deterministic signatures,
|
/// Having noise creates non-standard, non-deterministic signatures,
|
||||||
/// but has been proven to increase resilience against fault attacks.
|
/// but has been proven to increase resilience against fault attacks.
|
||||||
pub fn sign(msg: []const u8, key_pair: [keypair_length]u8, noise: ?[noise_length]u8) ![signature_length]u8 {
|
pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) ![signature_length]u8 {
|
||||||
const public_key = key_pair[32..];
|
const seed = key_pair.secret_key[0..seed_length];
|
||||||
|
const public_key = key_pair.secret_key[seed_length..];
|
||||||
|
if (!mem.eql(u8, public_key, &key_pair.public_key)) {
|
||||||
|
return error.KeyMismatch;
|
||||||
|
}
|
||||||
var az: [Sha512.digest_length]u8 = undefined;
|
var az: [Sha512.digest_length]u8 = undefined;
|
||||||
var h = Sha512.init(.{});
|
var h = Sha512.init(.{});
|
||||||
h.update(key_pair[0..seed_length]);
|
h.update(seed);
|
||||||
h.final(&az);
|
h.final(&az);
|
||||||
|
|
||||||
h = Sha512.init(.{});
|
h = Sha512.init(.{});
|
||||||
@ -192,50 +208,44 @@ pub const Ed25519 = struct {
|
|||||||
test "ed25519 key pair creation" {
|
test "ed25519 key pair creation" {
|
||||||
var seed: [32]u8 = undefined;
|
var seed: [32]u8 = undefined;
|
||||||
try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
|
try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
|
||||||
const key_pair = try Ed25519.createKeyPair(seed);
|
const key_pair = try Ed25519.KeyPair.create(seed);
|
||||||
var buf: [256]u8 = undefined;
|
var buf: [256]u8 = undefined;
|
||||||
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
|
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.secret_key}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
|
||||||
|
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
|
||||||
const public_key = Ed25519.publicKey(key_pair);
|
|
||||||
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "ed25519 signature" {
|
test "ed25519 signature" {
|
||||||
var seed: [32]u8 = undefined;
|
var seed: [32]u8 = undefined;
|
||||||
try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
|
try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
|
||||||
const key_pair = try Ed25519.createKeyPair(seed);
|
const key_pair = try Ed25519.KeyPair.create(seed);
|
||||||
|
|
||||||
const sig = try Ed25519.sign("test", key_pair, null);
|
const sig = try Ed25519.sign("test", key_pair, null);
|
||||||
var buf: [128]u8 = undefined;
|
var buf: [128]u8 = undefined;
|
||||||
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{sig}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");
|
std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{sig}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");
|
||||||
const public_key = Ed25519.publicKey(key_pair);
|
try Ed25519.verify(sig, "test", key_pair.public_key);
|
||||||
try Ed25519.verify(sig, "test", public_key);
|
std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", key_pair.public_key));
|
||||||
std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", public_key));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "ed25519 batch verification" {
|
test "ed25519 batch verification" {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < 100) : (i += 1) {
|
while (i < 100) : (i += 1) {
|
||||||
var seed: [32]u8 = undefined;
|
const key_pair = try Ed25519.KeyPair.create(null);
|
||||||
try std.crypto.randomBytes(&seed);
|
|
||||||
const key_pair = try Ed25519.createKeyPair(seed);
|
|
||||||
var msg1: [32]u8 = undefined;
|
var msg1: [32]u8 = undefined;
|
||||||
var msg2: [32]u8 = undefined;
|
var msg2: [32]u8 = undefined;
|
||||||
try std.crypto.randomBytes(&msg1);
|
try std.crypto.randomBytes(&msg1);
|
||||||
try std.crypto.randomBytes(&msg2);
|
try std.crypto.randomBytes(&msg2);
|
||||||
const sig1 = try Ed25519.sign(&msg1, key_pair, null);
|
const sig1 = try Ed25519.sign(&msg1, key_pair, null);
|
||||||
const sig2 = try Ed25519.sign(&msg2, key_pair, null);
|
const sig2 = try Ed25519.sign(&msg2, key_pair, null);
|
||||||
const public_key = Ed25519.publicKey(key_pair);
|
|
||||||
var signature_batch = [_]Ed25519.BatchElement{
|
var signature_batch = [_]Ed25519.BatchElement{
|
||||||
Ed25519.BatchElement{
|
Ed25519.BatchElement{
|
||||||
.sig = sig1,
|
.sig = sig1,
|
||||||
.msg = &msg1,
|
.msg = &msg1,
|
||||||
.public_key = public_key,
|
.public_key = key_pair.public_key,
|
||||||
},
|
},
|
||||||
Ed25519.BatchElement{
|
Ed25519.BatchElement{
|
||||||
.sig = sig2,
|
.sig = sig2,
|
||||||
.msg = &msg2,
|
.msg = &msg2,
|
||||||
.public_key = public_key,
|
.public_key = key_pair.public_key,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
try Ed25519.verifyBatch(2, signature_batch);
|
try Ed25519.verifyBatch(2, signature_batch);
|
||||||
|
|||||||
@ -124,10 +124,8 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
|
|||||||
const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
|
const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
|
||||||
|
|
||||||
pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
|
pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
|
||||||
var seed: [Signature.seed_length]u8 = undefined;
|
|
||||||
prng.random.bytes(seed[0..]);
|
|
||||||
const msg = [_]u8{0} ** 64;
|
const msg = [_]u8{0} ** 64;
|
||||||
const key_pair = try Signature.createKeyPair(seed);
|
const key_pair = try Signature.KeyPair.create(null);
|
||||||
|
|
||||||
var timer = try Timer.start();
|
var timer = try Timer.start();
|
||||||
const start = timer.lap();
|
const start = timer.lap();
|
||||||
@ -149,11 +147,8 @@ pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count
|
|||||||
const signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
|
const signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
|
||||||
|
|
||||||
pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
|
pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
|
||||||
var seed: [Signature.seed_length]u8 = undefined;
|
|
||||||
prng.random.bytes(seed[0..]);
|
|
||||||
const msg = [_]u8{0} ** 64;
|
const msg = [_]u8{0} ** 64;
|
||||||
const key_pair = try Signature.createKeyPair(seed);
|
const key_pair = try Signature.KeyPair.create(null);
|
||||||
const public_key = Signature.publicKey(key_pair);
|
|
||||||
const sig = try Signature.sign(&msg, key_pair, null);
|
const sig = try Signature.sign(&msg, key_pair, null);
|
||||||
|
|
||||||
var timer = try Timer.start();
|
var timer = try Timer.start();
|
||||||
@ -161,7 +156,7 @@ pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime sign
|
|||||||
{
|
{
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < signatures_count) : (i += 1) {
|
while (i < signatures_count) : (i += 1) {
|
||||||
try Signature.verify(sig, &msg, public_key);
|
try Signature.verify(sig, &msg, key_pair.public_key);
|
||||||
mem.doNotOptimizeAway(&sig);
|
mem.doNotOptimizeAway(&sig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,16 +171,13 @@ pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime sign
|
|||||||
const batch_signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
|
const batch_signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
|
||||||
|
|
||||||
pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
|
pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
|
||||||
var seed: [Signature.seed_length]u8 = undefined;
|
|
||||||
prng.random.bytes(seed[0..]);
|
|
||||||
const msg = [_]u8{0} ** 64;
|
const msg = [_]u8{0} ** 64;
|
||||||
const key_pair = try Signature.createKeyPair(seed);
|
const key_pair = try Signature.KeyPair.create(null);
|
||||||
const public_key = Signature.publicKey(key_pair);
|
|
||||||
const sig = try Signature.sign(&msg, key_pair, null);
|
const sig = try Signature.sign(&msg, key_pair, null);
|
||||||
|
|
||||||
var batch: [64]Signature.BatchElement = undefined;
|
var batch: [64]Signature.BatchElement = undefined;
|
||||||
for (batch) |*element| {
|
for (batch) |*element| {
|
||||||
element.* = Signature.BatchElement{ .sig = sig, .msg = &msg, .public_key = public_key };
|
element.* = Signature.BatchElement{ .sig = sig, .msg = &msg, .public_key = key_pair.public_key };
|
||||||
}
|
}
|
||||||
|
|
||||||
var timer = try Timer.start();
|
var timer = try Timer.start();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user