silk.crypto.secp256k1.
Key agreement (ECDH) and ECDSA signatures on the curve secp256k1 of SEC 2.
When to use#
Use Secp256k1 for an ECDH key exchange or an ECDSA signature on secp256k1, such as Node.js
crypto.createECDH('secp256k1'), crypto.sign with a secp256k1 key, or an ES256K JSON Web
Token. Use EcdsaSecp256k1 to verify.
Details#
Public keys are 65 bytes in uncompressed SEC1 form: 0x04, then the X and Y coordinates, each
32 bytes big-endian. Secp256k1.compressPublicKey and Secp256k1.decompressPublicKey
convert to and from the 33-byte compressed form. Secrets are 32 bytes big-endian.
Secp256k1.agree returns the X coordinate of the shared point and consumes the key.
Secp256k1.sign returns r and s, each 32 bytes, with the deterministic nonce of RFC 6979,
as OpenSSL does; it does not lower s as Bitcoin requires. EcdsaSecp256k1.toDer and
EcdsaSecp256k1.fromDer convert signatures to and from DER. Nothing allocates.
Errors#
Secp256k1Errorfor a secret or a peer public key that is not valid.Secp256k1SignatureErrorfor a key or signature that is not valid, or that does not match.
Gotchas#
A valid signature proves only that the holder of the private key signed the message. It does not
prove that you can trust the key. Both high and low s values verify. The key exchange does not
authenticate the peer. Pass the shared secret through silk.crypto.hkdf before you use it as a
key. The secret is not erased when it is dropped.
Examples#
Verify a signature#
import silk.crypto.secp256k1 { EcdsaSecp256k1, Secp256k1SignatureError }
import silk.crypto.sha2 { Sha256 }
import silk.core.result { Result }
pub fn main() -> i32 {
// A public key and a signature of "hello" that another program made.
let publicKey = b"\x04\x8d\x3a\x31\x0b\x0d\xfa\x35\xd0\x46\x47\x18\x5c\x42\x12\xca\xe0\xcf\xb0\x3b\xf6\xa2\x9c\x77\xce\xef\x34\xdf\x21\x05\x63\x4d\xa8\x9b\x8d\x7a\x59\x80\x90\x82\xf5\x78\x25\x8a\xa3\x02\xed\xdd\x75\x0c\x2e\xff\xa8\x88\x86\xc3\x1e\x48\xe9\x28\xd1\x26\x06\x9e\x29"
let signature = b"\xcc\x0f\x61\x5e\xc2\xc1\x99\x4f\xcf\xfc\xb5\xb2\xf0\x06\xda\x4b\xe4\x95\x62\x5f\x1d\xcb\x59\xcc\xed\xbc\xdb\xfa\xf9\x0b\x4a\x19\x58\x1b\x69\x32\x83\xad\x38\x65\x3f\x3f\xe6\x13\xbf\x23\xd8\x17\x2a\x22\x94\xdd\x3d\x1c\x4f\x38\xa1\xfb\xe8\x8a\x79\x28\x6b\x9d"
match EcdsaSecp256k1.verify<Sha256>(publicKey, b"hello", signature) {
Result<(), Secp256k1SignatureError>.Success {..} => {}
Result<(), Secp256k1SignatureError>.Failure {..} => {
return 1
}
}
return match EcdsaSecp256k1.verify<Sha256>(publicKey, b"hellp", signature) {
Result<(), Secp256k1SignatureError>.Failure { error: Secp256k1SignatureError.AuthenticationFailed } => 0
_ => 1
}
}Sign and agree#
import silk.crypto.secp256k1 { EcdsaSecp256k1, Secp256k1, Secp256k1Error, Secp256k1SignatureError }
import silk.crypto.sha2 { Sha256 }
import silk.core.result { Result }
pub fn main() -> i32 {
// Real programs use `Secp256k1.generate()` with a secure `Random` provider.
let mut secret: [u8; 32] = [0; 32]
secret[31] = 7
let key = match Secp256k1.fromBytes(&secret) {
Result<Secp256k1, Secp256k1Error>.Success { value } => move value
Result<Secp256k1, Secp256k1Error>.Failure {..} => {
return 1
}
}
let publicKey = key.publicKey()
let signature = key.sign<Sha256>(b"hello")
return match EcdsaSecp256k1.verify<Sha256>(&publicKey, b"hello", &signature) {
Result<(), Secp256k1SignatureError>.Success {..} => 0
Result<(), Secp256k1SignatureError>.Failure {..} => 1
}
}Import as Secp256k1 with import silk.crypto.secp256k1 { Secp256k1 }.
Public declarations: 4.
Secp256k1Error#
pub enum Secp256k1ErrorThe reason that a secp256k1 secret or public key is not valid. It holds no secret bytes.
InvalidLength#
InvalidLengthA secret is not 32 bytes, or a public key is not 65 bytes.
InvalidScalar#
InvalidScalarThe secret is 0, or not less than the order of the curve.
InvalidEncoding#
InvalidEncodingThe public key does not start with 0x04. Compressed keys are not supported.
InvalidPoint#
InvalidPointA coordinate is not less than the field prime, or the point is not on the curve.
Implementation Debug for Secp256k1Error#
impl Debug for Secp256k1ErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as InvalidLength.
Secp256k1#
pub struct Secp256k1A secp256k1 secret, for key agreement and for ECDSA signatures. It is not Copy.
When to use#
Use it for an ECDH key exchange or an ECDSA signature on secp256k1, such as Node.js
crypto.createECDH('secp256k1') or an ES256K JSON Web Token.
Details#
Secp256k1.agree consumes it, so make a new one for each exchange. Secp256k1.sign does
not. Public keys are 65 bytes in uncompressed SEC1 form: 0x04, then the X and Y coordinates, each 32 bytes big-endian. Secrets
are 32 bytes big-endian. Nothing allocates.
Gotchas#
The key exchange does not authenticate the peer. Pass the shared secret through
silk.crypto.hkdf before you use it as a key. The secret is not erased when it is dropped.
Implementation Secp256k1#
impl Secp256k1Associated function validatePublicKey#
pub fn validatePublicKey(publicKey: &[u8]) -> Result<(), Secp256k1Error>Checks that publicKey is a valid uncompressed secp256k1 public key.
It must be 65 bytes: 0x04, then X and Y, each 32 bytes big-endian and less than the field
prime, and the point must be on the curve.
Errors#
InvalidLength,InvalidEncoding, orInvalidPoint.
Associated function fromBytes#
pub fn fromBytes(bytes: &[u8]) -> Result<Secp256k1, Secp256k1Error>Returns the secret for the 32 big-endian bytes bytes.
Errors#
InvalidLengthifbytesis not 32 bytes.InvalidScalarif the value is 0, or not less than the order of the curve.
Associated function generate#
pub effect fn generate() -> Secp256k1 ? &mut RandomReturns a new random secret, with bytes from the ambient Random.
It draws 32 bytes again until the value is valid, so the result is uniform. The Random
provider must be cryptographically secure. If it fails, the program stops.
Method publicKey#
pub fn publicKey(self: &Self) -> [u8; 65]Returns the 65-byte uncompressed public key to send to the peer. It does not consume the secret.
Method agree#
pub fn agree(self: Self, peer: &[u8]) -> Result<[u8; 32], Secp256k1Error>Returns the 32-byte shared secret for the public key peer, and consumes the secret.
The result is the big-endian X coordinate of the shared point.
Errors#
InvalidLength,InvalidEncoding, orInvalidPointifpeeris not a valid uncompressed public key. The secret is consumed also when it fails.
Gotchas#
Pass the result through silk.crypto.hkdf before you use it as a key.
Method toBytes#
pub fn toBytes(self: &Self) -> [u8; 32]Returns the 32 big-endian bytes of the secret, the input of Secp256k1.fromBytes.
Gotchas#
The bytes are the private key. Keep them secret.
Method sign#
pub fn sign<H: Digest>(self: &Self, message: &[u8]) -> [u8; 64]Returns the ECDSA signature of message, hashed with H, as 64 bytes: r, then s, each
32 bytes big-endian.
The nonce is deterministic (RFC 6979) with HMAC over H, so the same key, hash, and message
always give the same signature. EcdsaSecp256k1.toDer converts the result to DER.
Gotchas#
The program traps if message is longer than H allows.
Method signDigest#
pub fn signDigest<H: Digest>(self: &Self, digest: &[u8]) -> [u8; 64]Returns the ECDSA signature of a message whose digest is digest, as 64 bytes: r, then
s, each 32 bytes big-endian.
H is the hash for the RFC 6979 nonce, normally the hash that made digest. A digest longer
than 32 bytes is cut to its first 32 bytes, as ECDSA requires.
Associated function compressPublicKey#
pub fn compressPublicKey(publicKey: &[u8]) -> Result<[u8; 33], Secp256k1Error>Returns the 33-byte compressed form of the uncompressed public key publicKey: 0x02 or
0x03 for the parity of Y, then X.
Errors#
InvalidLength,InvalidEncoding, orInvalidPointifpublicKeyis not a valid uncompressed public key.
Associated function decompressPublicKey#
pub fn decompressPublicKey(publicKey: &[u8]) -> Result<[u8; 65], Secp256k1Error>Returns the 65-byte uncompressed form of the compressed public key publicKey.
Errors#
InvalidLengthifpublicKeyis not 33 bytes.InvalidEncodingif it does not start with0x02or0x03.InvalidPointif X is not less than the field prime, or no point has that X.
Secp256k1SignatureError#
pub enum Secp256k1SignatureErrorThe reason that an ECDSA secp256k1 verification failed.
InvalidKey#
InvalidKeyThe public key is not a valid uncompressed secp256k1 key.
InvalidEncoding#
InvalidEncodingThe signature is not strict DER, or r or s is not between 1 and the order of the curve.
AuthenticationFailed#
AuthenticationFailedThe inputs are valid, but the signature does not match the message and the key.
Implementation Debug for Secp256k1SignatureError#
impl Debug for Secp256k1SignatureErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as InvalidKey.
EcdsaSecp256k1#
pub struct EcdsaSecp256k1ECDSA verification on secp256k1 with any hash, and conversion between the two signature forms.
When to use#
Use it to verify a signature in the fixed 64-byte form (r, then s), as in ES256K JSON Web
Tokens. EcdsaSecp256k1.fromDer turns a DER signature, as Node.js crypto.verify takes it,
into that form. To sign, use Secp256k1.sign.
Details#
Public keys are 65 bytes in uncompressed SEC1 form. A fixed-form signature is 64 bytes. A DER signature is a SEQUENCE of the two INTEGERs, at most 72 bytes. Nothing allocates.
Gotchas#
A valid signature proves only that the holder of the private key signed the message. It does not prove that you can trust the key.
Implementation EcdsaSecp256k1#
impl EcdsaSecp256k1Associated function verify#
pub fn verify<H: Digest>(publicKey: &[u8], message: &[u8], signature: &[u8]) -> Result<
(),
Secp256k1SignatureError,
>Checks that signature, in the fixed 64-byte form, is a valid signature of message,
hashed with H, by publicKey.
Both high and low s values are accepted.
Errors#
InvalidKeyifpublicKeyis not a valid uncompressed key.InvalidEncodingifsignatureis not 64 bytes, orrorsis not between 1 and the order of the curve.AuthenticationFailedif the signature does not match.
Gotchas#
The program traps if message is longer than H allows.
Associated function verifyDigest#
pub fn verifyDigest(publicKey: &[u8], digest: &[u8], signature: &[u8]) -> Result<
(),
Secp256k1SignatureError,
>Checks that signature, in the fixed 64-byte form, is a valid signature by publicKey of a
message whose digest is digest.
A digest longer than 32 bytes is cut to its first 32 bytes, as ECDSA requires.
Errors#
InvalidKey,InvalidEncoding, orAuthenticationFailed, as forEcdsaSecp256k1.verify.
Associated function fromDer#
pub fn fromDer(signature: &[u8]) -> Result<[u8; 64], Secp256k1SignatureError>Returns the fixed 64-byte form of the DER signature signature.
Errors#
InvalidEncodingifsignatureis not strict DER, orrorsis not between 1 and the order of the curve.
Associated function toDer#
pub fn toDer(signature: &[u8], output: &mut [u8]) -> usizeWrites the DER form of the fixed 64-byte signature signature to the start of output, and
returns its length, at most 72 bytes.
It does not check the values of r and s. The bytes of output after the DER do not
change.
Gotchas#
The program traps if signature is not 64 bytes, or output is shorter than the DER.
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.