- This avoids having multiple `init()` functions for every combination of optional parameters - The API is consistent across all hash functions - New options can be added later without breaking existing applications. For example, this is going to come in handy if we implement parallelization for BLAKE2 and BLAKE3. - We don't have a mix of snake_case and camelCase functions any more, at least in the public crypto API Support for BLAKE2 salt and personalization (more commonly called context) parameters have been implemented by the way to illustrate this.
140 lines
5.8 KiB
Zig
140 lines
5.8 KiB
Zig
// SPDX-License-Identifier: MIT
|
|
// Copyright (c) 2015-2020 Zig Contributors
|
|
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
|
// The MIT license requires this copyright notice to be included in all copies
|
|
// and substantial portions of the software.
|
|
const std = @import("std");
|
|
const fmt = std.fmt;
|
|
const mem = std.mem;
|
|
const Sha512 = std.crypto.hash.sha2.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));
|
|
}
|