diff options
Diffstat (limited to 'lib/std/crypto/25519/ed25519.zig')
| -rw-r--r-- | lib/std/crypto/25519/ed25519.zig | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig new file mode 100644 index 0000000000..eb004d2607 --- /dev/null +++ b/lib/std/crypto/25519/ed25519.zig @@ -0,0 +1,134 @@ +const std = @import("std"); +const fmt = std.fmt; +const mem = std.mem; +const Sha512 = std.crypto.Sha512; + +/// Ed25519 (EdDSA) signatures. +pub const Ed25519 = struct { + /// The underlying elliptic curve. + pub const Curve = @import("edwards25519.zig").Edwards25519; + /// Length (in bytes) of a seed required to create a key pair. + pub const seed_length = 32; + /// Length (in bytes) of a compressed key pair. + pub const keypair_length = 64; + /// Length (in bytes) of a compressed public key. + pub const public_length = 32; + /// Length (in bytes) of a signature. + pub const signature_length = 64; + /// Length (in bytes) of optional random bytes, for non-deterministic signatures. + pub const noise_length = 32; + + /// Derive a key pair from a secret seed. + /// + /// As in RFC 8032, an Ed25519 public key is generated by hashing + /// the secret key using the SHA-512 function, and interpreting the + /// 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 createKeyPair(seed: [seed_length]u8) ![keypair_length]u8 { + var az: [Sha512.digest_length]u8 = undefined; + var h = Sha512.init(); + h.update(&seed); + h.final(&az); + const p = try Curve.basePoint.clampedMul(az[0..32].*); + var keypair: [keypair_length]u8 = undefined; + mem.copy(u8, &keypair, &seed); + mem.copy(u8, keypair[seed_length..], &p.toBytes()); + return keypair; + } + + /// Return the public key for a given key pair. + pub fn publicKey(key_pair: [keypair_length]u8) [public_length]u8 { + var public_key: [public_length]u8 = undefined; + mem.copy(u8, public_key[0..], key_pair[seed_length..]); + return public_key; + } + + /// Sign a message using a key pair, and optional random noise. + /// Having noise creates non-standard, non-deterministic signatures, + /// 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 { + const public_key = key_pair[32..]; + var az: [Sha512.digest_length]u8 = undefined; + var h = Sha512.init(); + h.update(key_pair[0..seed_length]); + h.final(&az); + + h = Sha512.init(); + if (noise) |*z| { + h.update(z); + } + h.update(az[32..]); + h.update(msg); + var nonce64: [64]u8 = undefined; + h.final(&nonce64); + const nonce = Curve.scalar.reduce64(nonce64); + const r = try Curve.basePoint.mul(nonce); + + var sig: [signature_length]u8 = undefined; + mem.copy(u8, sig[0..32], &r.toBytes()); + mem.copy(u8, sig[32..], public_key); + h = Sha512.init(); + h.update(&sig); + h.update(msg); + var hram64: [Sha512.digest_length]u8 = undefined; + h.final(&hram64); + const hram = Curve.scalar.reduce64(hram64); + + var x = az[0..32]; + Curve.scalar.clamp(x); + const s = Curve.scalar.mulAdd(hram, x.*, nonce); + mem.copy(u8, sig[32..], s[0..]); + return sig; + } + + /// Verify an Ed25519 signature given a message and a public key. + /// Returns error.InvalidSignature is the signature verification failed. + pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) !void { + const r = sig[0..32]; + const s = sig[32..64]; + try Curve.scalar.rejectNonCanonical(s.*); + try Curve.rejectNonCanonical(public_key); + const a = try Curve.fromBytes(public_key); + try a.rejectIdentity(); + + var h = Sha512.init(); + h.update(r); + h.update(&public_key); + h.update(msg); + var hram64: [Sha512.digest_length]u8 = undefined; + h.final(&hram64); + const hram = Curve.scalar.reduce64(hram64); + + const p = try a.neg().mul(hram); + const check = (try Curve.basePoint.mul(s.*)).add(p).toBytes(); + if (mem.eql(u8, &check, r) == false) { + return error.InvalidSignature; + } + } +}; + +test "ed25519 key pair creation" { + var seed: [32]u8 = undefined; + try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); + const key_pair = try Ed25519.createKeyPair(seed); + var buf: [256]u8 = undefined; + std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); + + const public_key = Ed25519.publicKey(key_pair); + std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); +} + +test "ed25519 signature" { + var seed: [32]u8 = undefined; + try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); + const key_pair = try Ed25519.createKeyPair(seed); + + const sig = try Ed25519.sign("test", key_pair, null); + var buf: [128]u8 = undefined; + std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{sig}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808"); + const public_key = Ed25519.publicKey(key_pair); + try Ed25519.verify(sig, "test", public_key); + std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", public_key)); +} |
