structstd.crypto.25519.ed25519.Ed25519.KeyPair[src]

An Ed25519 key pair.

Fields

public_key: PublicKey

Public part.

secret_key: SecretKey

Secret scalar.

Values

Constantnoise_length[src]

Length (in bytes) of optional random bytes, for non-deterministic signatures.

Source Code

Source code
pub const noise_length = 32

Functions

FunctiongenerateDeterministic[src]

pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair

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.

Parameters

seed: [seed_length]u8

Source Code

Source code
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),
    };
}

Functiongenerate[src]

pub fn generate() KeyPair

Generate a new, random key pair.

crypto.random.bytes must be supported by the target.

Source Code

Source code
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;
        };
    }
}

FunctionfromSecretKey[src]

pub fn fromSecretKey(secret_key: SecretKey) (NonCanonicalError || EncodingError || IdentityElementError)!KeyPair

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().

Parameters

secret_key: SecretKey

Source Code

Source code
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,
    };
}

Functionsign[src]

pub fn sign(key_pair: KeyPair, msg: []const u8, noise: ?[noise_length]u8) (IdentityElementError || NonCanonicalError || KeyMismatchError || WeakPublicKeyError)!Signature

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.

Parameters

key_pair: KeyPair
msg: []const u8
noise: ?[noise_length]u8

Source Code

Source code
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,
    );
}

Functionsigner[src]

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.

Parameters

key_pair: KeyPair
noise: ?[noise_length]u8

Source Code

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

Source Code

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