silk.crypto.ec.
Elliptic-curve keys, ECDSA, and ECDH on every named curve over a prime field that OpenSSL and
Node.js list, such as brainpoolP256r1, secp256k1, secp224r1, and SM2.
When to use#
Use EcPrivateKey and EcPublicKey for Node.js crypto.createECDH, generateKeyPair('ec'),
and crypto.sign with an ec key on any curve of silk.crypto.ec_key.EcCurve. For P-256,
P-384, P-521, or secp256k1 alone, silk.crypto.p256, silk.crypto.p384, silk.crypto.p521,
and silk.crypto.secp256k1 are faster.
Details#
An EcGroup holds the parameters of a curve y^2 = x^3 + ax + b modulo a prime p and its
base point G of prime order n. A private key is a number d from 1 to n - 1 and its public
key is the point dG. Points are read and written in the SEC1 forms: uncompressed 04 || x || y,
compressed 02 or 03 || x, and hybrid 06 or 07 || x || y.
The signing functions take the digest of the message, not the message. A digest longer than n
keeps its leftmost bits, as OpenSSL does. A signature is r || s, each big-endian and
EcGroup.secretLength bytes, the IEEE P1363 form that Node.js calls ieee-p1363.
EcSignature converts it to and from DER, the default of Node.js. EcPrivateKey.agree gives
the x-coordinate of dQ, as Node.js ecdh.computeSecret does.
Field arithmetic uses Montgomery multiplication on 64-bit limbs, and points use the complete
projective formulas of Renes, Costello, and Batina. Operations with a secret, such as
EcPrivateKey.generate, EcPrivateKey.signDigest, and EcPrivateKey.agree, take the
same time for every secret of a curve: they have no branch and no memory access that depends on
the secret.
Errors#
EcErrorfor a secret, a point, a length, or a signature that is not valid.
Gotchas#
On a curve with a cofactor other than 1, such as secp112r2, EcPublicKey.fromBytes also
checks that the point is in the subgroup of the base point. Each signature needs a fresh secret
from a cryptographically secure Random; a repeated or predictable one reveals the private key.
The values stay in memory after use; nothing erases them.
Examples#
Agree on a secret on brainpoolP256r1#
import silk.core.effect { Effect }
import silk.core.result { Result }
import silk.crypto.ec { EcError, EcGroup, EcPrivateKey }
import silk.crypto.ec_key { EcCurve }
import silk.num.u64
import silk.system.random { Random }
// A test provider. Never use it for keys.
struct Counter {
state: u64,
}
impl Random for Counter {
effect fn fillBytes(self: &mut Self, output: &mut [u8]) -> () {
let mut index: usize = 0
while index < output.length {
self.state = u64.wrappingAdd(u64.wrappingMultiply(self.state, 6364136223846793005), 1)
output[index] = u64.toU8(u64.shiftRight(self.state, 56))
index = index + 1
}
}
}
effect fn agreeBoth() -> i32 ! EcError ? &mut Random {
let group = EcGroup.named(EcCurve.BrainpoolP256r1)
let alice = run EcPrivateKey.generate(&group)
let bob = run EcPrivateKey.generate(&group)
let mut fromAlice: [u8; 32] = [0; 32]
let mut fromBob: [u8; 32] = [0; 32]
if let Result<(), EcError>.Failure { error } = alice.agree(&bob.publicKey(), &mut fromAlice) {
fail error
}
if let Result<(), EcError>.Failure { error } = bob.agree(&alice.publicKey(), &mut fromBob) {
fail error
}
let mut i: usize = 0
while i < 32 {
if fromAlice[i] != fromBob[i] {
return 1
}
i = i + 1
}
return 0
}
pub effect fn main() -> i32 ! EcError {
let mut random = Counter { state: 1 }
return run agreeBoth() |> Effect.provideMut<Random>(&mut random)
}Import as EcPrivateKey with import silk.crypto.ec { EcPrivateKey }.
Public declarations: 6.
EcError#
pub enum EcErrorThe reason that an elliptic-curve secret, point, length, or signature was rejected.
InvalidKey#
InvalidKeyA secret is 0 or not less than the order n of the base point.
InvalidPoint#
InvalidPointA point encoding is not valid: the first byte is not a SEC1 form, the length is wrong, a
coordinate is not less than p, the point is not on the curve or not in the subgroup of the
base point, the hybrid parity does not match, or the point is the point at infinity.
InvalidLength#
InvalidLengthAn input or output does not have the required length.
InvalidEncoding#
InvalidEncodingA DER signature is not strict DER of two non-negative integers.
InvalidSignature#
InvalidSignatureThe signature does not match the digest and the key.
InvalidSharedSecret#
InvalidSharedSecretThe shared point of a key agreement is the point at infinity.
Implementation Debug for EcError#
impl Debug for EcErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
! WriterError
? &mut WriterWrites the name of the variant, such as InvalidKey.
EcPointFormat#
pub enum EcPointFormatThe SEC1 form of an encoded public point.
Compressed#
Compressed02 or 03, by the parity of y, then x.
Uncompressed#
Uncompressed04, then x and y.
Hybrid#
Hybrid06 or 07, by the parity of y, then x and y.
Implementation Debug for EcPointFormat#
impl Debug for EcPointFormatOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
! WriterError
? &mut WriterWrites the name of the variant, such as Compressed.
EcGroup#
pub struct EcGroupThe parameters of a named curve over a prime field, with its base point and order.
When to use#
Make one with EcGroup.curveName and pass it to the key functions of this module. Keep it for
all the operations on that curve.
Details#
Making a group converts the parameters to Montgomery form, which takes a few microseconds.
Gotchas#
It is a fixed-size value of about 1.5 KiB. Pass it by reference.
Implementation Copy for EcGroup#
impl Copy for EcGroupEcPublicKey#
pub struct EcPublicKeyAn elliptic-curve public key: a point of the subgroup of the base point other than the point at infinity.
When to use#
Use it to verify ECDSA signatures and as the peer key of EcPrivateKey.agree. Read one with
EcPublicKey.fromBytes, or get one with EcPrivateKey.publicKey.
Gotchas#
It is a fixed-size value of about 1.7 KiB. Pass it by reference.
Implementation Copy for EcPublicKey#
impl Copy for EcPublicKeyEcPrivateKey#
pub struct EcPrivateKeyAn elliptic-curve private key: a secret d from 1 to n - 1 and its public point dG.
When to use#
Use it to sign digests with ECDSA and to agree on a secret with ECDH. Generate one with
EcPrivateKey.generate, or read one with EcPrivateKey.fromBytes.
Gotchas#
It does not erase its memory when dropped. It is a fixed-size value of about 1.8 KiB. Pass it by reference.
Implementation Copy for EcPrivateKey#
impl Copy for EcPrivateKeyEcSignature#
pub struct EcSignatureConversions between the r || s form of an ECDSA signature and its DER form.
When to use#
Use it for Node.js dsaEncoding: der, the default, is the DER SEQUENCE of two INTEGERs,
and ieee-p1363 is r || s, which EcPrivateKey.signDigest writes.
Implementation EcGroup#
impl EcGroupAssociated function named#
pub fn named(curve: EcCurve) -> EcGroupReturns the group of the named curve curve.
Method curve#
pub fn curve(self: &Self) -> EcCurveReturns the named curve of the group.
Method fieldBits#
pub fn fieldBits(self: &Self) -> usizeReturns the number of bits of the prime p, such as 256 for brainpoolP256r1.
Method orderBits#
pub fn orderBits(self: &Self) -> usizeReturns the number of bits of the order n of the base point, such as 161 for secp160r1.
Method cofactor#
pub fn cofactor(self: &Self) -> u32Returns the cofactor: the number of points of the curve divided by n, such as 1 for most
curves and 4 for secp112r2.
Method fieldLength#
pub fn fieldLength(self: &Self) -> usizeReturns the length in bytes of a field element: the length of a shared secret from
EcPrivateKey.agree and of each coordinate of an encoded point.
Method secretLength#
pub fn secretLength(self: &Self) -> usizeReturns the length in bytes of a secret, which is the byte length of n.
Method signatureLength#
pub fn signatureLength(self: &Self) -> usizeReturns the length in bytes of a signature in the r || s form: twice
EcGroup.secretLength.
Method encodedLength#
pub fn encodedLength(self: &Self, format: EcPointFormat) -> usizeReturns the length in bytes of a point encoded in format: EcGroup.fieldBytes plus 1
when compressed, and twice it plus 1 otherwise.
Implementation EcPublicKey#
impl EcPublicKeyAssociated function fromBytes#
pub fn fromBytes(group: &EcGroup, encoded: &[u8]) -> Result<EcPublicKey, EcError>Returns the public key for the SEC1 point encoded on group: compressed, uncompressed,
or hybrid, as Node.js ecdh.computeSecret and ECDH.convertKey accept.
The time depends on the point, which is public.
Errors#
InvalidPointif the encoding is not valid, a coordinate is not less thanp, the point is not on the curve, a hybrid parity does not matchy, or, on a curve with a cofactor other than 1, the point is not in the subgroup of the base point. The point at infinity is rejected.
Method group#
pub fn group(self: &Self) -> EcGroupReturns the group of the key.
Method write#
pub fn write(self: &Self, format: EcPointFormat, output: &mut [u8]) -> Result<usize, EcError>Writes the point in format to the start of output, and returns its length, which is
EcGroup.encodedLength. The bytes of output after it do not change.
Errors#
InvalidLengthifoutputis shorter than the encoding.
Method verifyDigest#
pub fn verifyDigest(self: &Self, digest: &[u8], signature: &[u8]) -> Result<(), EcError>Checks that signature is a valid ECDSA signature in the r || s form of the message with
the digest digest.
A digest longer than n keeps its leftmost EcGroup.orderBits bits, as OpenSSL does.
Errors#
InvalidLengthifsignatureis notEcGroup.signatureLengthbytes.InvalidSignatureifrorsis not from 1 ton - 1, or the signature does not match.
Implementation EcPrivateKey#
impl EcPrivateKeyAssociated function generate#
pub effect fn generate(group: &EcGroup) -> EcPrivateKey
? &mut RandomReturns a new key on group, with a secret from the ambient Random.
The secret is uniform from 1 to n - 1. The time of the public point does not depend on
the secret. The Random provider must be cryptographically secure.
Associated function fromBytes#
pub fn fromBytes(group: &EcGroup, secret: &[u8]) -> Result<EcPrivateKey, EcError>Returns the private key for the big-endian secret secret on group, and computes its
public point, as Node.js ecdh.setPrivateKey does. Leading zero bytes are allowed.
The time depends only on the length of secret.
Errors#
InvalidKeyif the secret is 0 or not less thann.
Method group#
pub fn group(self: &Self) -> EcGroupReturns the group of the key.
Method publicKey#
pub fn publicKey(self: &Self) -> EcPublicKeyReturns the public half of the key.
Method writeSecret#
pub fn writeSecret(self: &Self, output: &mut [u8]) -> Result<(), EcError>Writes the secret to all of output, big-endian with leading zero bytes, as Node.js
ecdh.getPrivateKey does.
Errors#
InvalidLengthifoutputis notEcGroup.secretLengthbytes.
Gotchas#
The bytes are the private key. Keep them secret.
Method signDigest#
pub effect fn signDigest(self: &Self, digest: &[u8], signature: &mut [u8]) -> ()
! EcError
? &mut RandomSigns the message with the digest digest with ECDSA and writes the signature to
signature in the r || s form. It reads the per-signature secret k from the ambient
Random.
A digest longer than n keeps its leftmost EcGroup.orderBits bits, as OpenSSL does. The
time does not depend on the secret or on k.
Errors#
InvalidLengthifsignatureis notEcGroup.signatureLengthbytes.
Gotchas#
The Random provider must be cryptographically secure: a repeated or biased k reveals
the private key.
Method agree#
pub fn agree(self: &Self, peer: &EcPublicKey, output: &mut [u8]) -> Result<(), EcError>Writes the shared secret with the peer key peer to output: the x-coordinate of dQ,
big-endian and EcGroup.fieldBytes bytes, as Node.js ecdh.computeSecret and
crypto.diffieHellman do.
The time does not depend on the secret.
Errors#
InvalidLengthifoutputis notEcGroup.fieldBytesbytes.InvalidPointifpeeris on another curve.InvalidSharedSecretif the shared point is the point at infinity.
Gotchas#
Pass the secret through a key derivation function, such as silk.crypto.hkdf, before you
use it as a key.
Implementation EcSignature#
impl EcSignatureAssociated function derLength#
pub fn derLength(signature: &[u8]) -> usizeReturns the length of the DER form of the r || s signature signature.
Gotchas#
The program traps if signature has an odd length or is longer than 144 bytes.
Associated function toDer#
pub fn toDer(signature: &[u8], output: &mut [u8]) -> Result<usize, EcError>Writes the DER form of the r || s signature signature to the start of output, and
returns its length, which is EcSignature.derLength. The bytes of output after the DER
do not change.
Errors#
InvalidLengthifoutputis shorter than the DER.
Gotchas#
The program traps if signature has an odd length or is longer than 144 bytes.
Associated function fromDer#
pub fn fromDer(der: &[u8], output: &mut [u8]) -> Result<(), EcError>Reads the DER signature der and writes it to all of output in the r || s form, each
half output.length / 2 bytes, such as EcGroup.signatureLength.
Errors#
InvalidEncodingifderis not strict DER of two non-negative integers.InvalidSignatureifrorsis 0 or does not fit in half ofoutput.InvalidLengthifoutputhas an odd length.
Category: Cryptography.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.