An Ed25519 key pair.
Length (in bytes) of optional random bytes, for non-deterministic signatures.
pub const noise_length = 32pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPairDeterministically derive a key pair from a cryptograpically secure secret seed.
To create a new key, applications should generally call generate() instead of this function.
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.
seed: [seed_length]u8pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair {
var az: [Sha512.digest_length]u8 = undefined;
var h = Sha512.init(.{});
h.update(&seed);
h.final(&az);
const pk_p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement;
const pk_bytes = pk_p.toBytes();
var sk_bytes: [SecretKey.encoded_length]u8 = undefined;
sk_bytes[0..seed_length].* = seed;
sk_bytes[seed_length..].* = pk_bytes;
return KeyPair{
.public_key = PublicKey.fromBytes(pk_bytes) catch unreachable,
.secret_key = try SecretKey.fromBytes(sk_bytes),
};
}pub fn generate() KeyPairGenerate a new, random key pair.
crypto.random.bytes must be supported by the target.
pub fn generate() KeyPair {
var random_seed: [seed_length]u8 = undefined;
while (true) {
crypto.random.bytes(&random_seed);
return generateDeterministic(random_seed) catch {
@branchHint(.unlikely);
continue;
};
}
}pub fn fromSecretKey(secret_key: SecretKey) (NonCanonicalError || EncodingError || IdentityElementError)!KeyPairCreate a key pair from an existing secret key.
Note that with EdDSA, storing the seed, and recovering the key pair
from it is recommended over storing the entire secret key.
The seed of an exiting key pair can be obtained with
key_pair.secret_key.seed(), and the secret key can then be
recomputed using SecretKey.generateDeterministic().
secret_key: SecretKeypub fn fromSecretKey(secret_key: SecretKey) (NonCanonicalError || EncodingError || IdentityElementError)!KeyPair {
// It is critical for EdDSA to use the correct public key.
// In order to enforce this, a SecretKey implicitly includes a copy of the public key.
// With runtime safety, we can still afford checking that the public key is correct.
if (std.debug.runtime_safety) {
const pk_p = try Curve.fromBytes(secret_key.publicKeyBytes());
const recomputed_kp = try generateDeterministic(secret_key.seed());
if (!mem.eql(u8, &recomputed_kp.public_key.toBytes(), &pk_p.toBytes())) {
return error.NonCanonical;
}
}
return KeyPair{
.public_key = try PublicKey.fromBytes(secret_key.publicKeyBytes()),
.secret_key = secret_key,
};
}pub fn sign(key_pair: KeyPair, msg: []const u8, noise: ?[noise_length]u8) (IdentityElementError || NonCanonicalError || KeyMismatchError || WeakPublicKeyError)!SignatureSign a message using the key pair. The noise can be null in order to create deterministic signatures. If deterministic signatures are not required, the noise should be randomly generated instead. This helps defend against fault attacks.
pub fn sign(key_pair: KeyPair, msg: []const u8, noise: ?[noise_length]u8) (IdentityElementError || NonCanonicalError || KeyMismatchError || WeakPublicKeyError)!Signature {
if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {
return error.KeyMismatch;
}
const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
return key_pair.public_key.computeNonceAndSign(
msg,
noise,
scalar_and_prefix.scalar,
&scalar_and_prefix.prefix,
);
}pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!SignerCreate a Signer, that can be used for incremental signing. Note that the signature is not deterministic. The noise parameter, if set, should be something unique for each message, such as a random nonce, or a counter.
key_pair: KeyPairnoise: ?[noise_length]u8pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {
return error.KeyMismatch;
}
const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
var h = Sha512.init(.{});
h.update(&scalar_and_prefix.prefix);
var noise2: [noise_length]u8 = undefined;
crypto.random.bytes(&noise2);
h.update(&noise2);
if (noise) |*z| {
h.update(z);
}
var nonce64: [64]u8 = undefined;
h.final(&nonce64);
const nonce = Curve.scalar.reduce64(nonce64);
return Signer.init(scalar_and_prefix.scalar, nonce, key_pair.public_key);
}pub const KeyPair = struct {
/// Length (in bytes) of a seed required to create a key pair.
pub const seed_length = noise_length;
/// Public part.
public_key: PublicKey,
/// Secret scalar.
secret_key: SecretKey,
/// Deterministically derive a key pair from a cryptograpically secure secret seed.
///
/// To create a new key, applications should generally call `generate()` instead of this function.
///
/// 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 generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair {
var az: [Sha512.digest_length]u8 = undefined;
var h = Sha512.init(.{});
h.update(&seed);
h.final(&az);
const pk_p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement;
const pk_bytes = pk_p.toBytes();
var sk_bytes: [SecretKey.encoded_length]u8 = undefined;
sk_bytes[0..seed_length].* = seed;
sk_bytes[seed_length..].* = pk_bytes;
return KeyPair{
.public_key = PublicKey.fromBytes(pk_bytes) catch unreachable,
.secret_key = try SecretKey.fromBytes(sk_bytes),
};
}
/// Generate a new, random key pair.
///
/// `crypto.random.bytes` must be supported by the target.
pub fn generate() KeyPair {
var random_seed: [seed_length]u8 = undefined;
while (true) {
crypto.random.bytes(&random_seed);
return generateDeterministic(random_seed) catch {
@branchHint(.unlikely);
continue;
};
}
}
/// Create a key pair from an existing secret key.
///
/// Note that with EdDSA, storing the seed, and recovering the key pair
/// from it is recommended over storing the entire secret key.
/// The seed of an exiting key pair can be obtained with
/// `key_pair.secret_key.seed()`, and the secret key can then be
/// recomputed using `SecretKey.generateDeterministic()`.
pub fn fromSecretKey(secret_key: SecretKey) (NonCanonicalError || EncodingError || IdentityElementError)!KeyPair {
// It is critical for EdDSA to use the correct public key.
// In order to enforce this, a SecretKey implicitly includes a copy of the public key.
// With runtime safety, we can still afford checking that the public key is correct.
if (std.debug.runtime_safety) {
const pk_p = try Curve.fromBytes(secret_key.publicKeyBytes());
const recomputed_kp = try generateDeterministic(secret_key.seed());
if (!mem.eql(u8, &recomputed_kp.public_key.toBytes(), &pk_p.toBytes())) {
return error.NonCanonical;
}
}
return KeyPair{
.public_key = try PublicKey.fromBytes(secret_key.publicKeyBytes()),
.secret_key = secret_key,
};
}
/// Sign a message using the key pair.
/// The noise can be null in order to create deterministic signatures.
/// If deterministic signatures are not required, the noise should be randomly generated instead.
/// This helps defend against fault attacks.
pub fn sign(key_pair: KeyPair, msg: []const u8, noise: ?[noise_length]u8) (IdentityElementError || NonCanonicalError || KeyMismatchError || WeakPublicKeyError)!Signature {
if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {
return error.KeyMismatch;
}
const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
return key_pair.public_key.computeNonceAndSign(
msg,
noise,
scalar_and_prefix.scalar,
&scalar_and_prefix.prefix,
);
}
/// Create a Signer, that can be used for incremental signing.
/// Note that the signature is not deterministic.
/// The noise parameter, if set, should be something unique for each message,
/// such as a random nonce, or a counter.
pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {
return error.KeyMismatch;
}
const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
var h = Sha512.init(.{});
h.update(&scalar_and_prefix.prefix);
var noise2: [noise_length]u8 = undefined;
crypto.random.bytes(&noise2);
h.update(&noise2);
if (noise) |*z| {
h.update(z);
}
var nonce64: [64]u8 = undefined;
h.final(&nonce64);
const nonce = Curve.scalar.reduce64(nonce64);
return Signer.init(scalar_and_prefix.scalar, nonce, key_pair.public_key);
}
}