struct KeyPair [src]

An Ed25519 key pair.

Fields

public_key: PublicKeyPublic part.
secret_key: SecretKeySecret scalar.

Members

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); } }