struct KeyPair [src]
An Ed25519 key pair.
Fields
public_key: PublicKeyPublic part.
secret_key: SecretKeySecret scalar.
Members
- fromSecretKey (Function)
- generate (Function)
- generateDeterministic (Function)
- seed_length (Constant)
- sign (Function)
- signer (Function)
Source
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);
}
}