silk.crypto.p521.
Key agreement (ECDH) and ECDSA signatures on the NIST P-521 curve (secp521r1).
When to use#
Use P521 for an ECDH key exchange where a protocol needs P-521, such as a TLS key share
secp521r1, and to sign with ECDSA, as in ES512 JSON Web Tokens or Node.js crypto.sign. Use
EcdsaP521Sha512 to verify a DER signature with SHA-512, for example in a certificate chain,
and EcdsaP521 to verify with another hash or in the fixed 132-byte form.
Details#
Public keys are 133 bytes in uncompressed SEC1 form: 0x04, then the X and Y coordinates, each
66 bytes big-endian. P521.compressPublicKey and P521.decompressPublicKey convert to and
from the 67-byte compressed form. Secrets are 66 bytes big-endian. P521.agree returns the X
coordinate of the shared point and consumes the key. P521.sign returns r and s, each 66
bytes, with the deterministic nonce of RFC 6979. EcdsaP521.toDer and EcdsaP521.fromDer
convert signatures to and from DER, which uses the long length form once it reaches 128 bytes.
Nothing allocates.
Errors#
P521Errorfor a secret or a peer public key that is not valid.P521SignatureErrorfor 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. To check a certificate chain, use the silk.tls.trust_*
modules. 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.p521 { EcdsaP521Sha512, P521SignatureError }
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\x01\x09\x35\x81\x03\x35\x7f\xdc\xf8\x0c\x5e\x61\x84\x4d\x16\x04\x0d\xc8\x11\x6d\x07\x6c\x5d\xe7\x6e\xbf\xb8\x62\x0b\xfb\xb7\xed\xe4\x4f\xac\x0e\x14\x3b\x38\x84\x86\xd5\xee\xba\x44\x08\x04\xb9\xb5\x2b\xfb\xa8\x0d\xb5\x6f\x6d\x05\xab\xf0\xa6\xab\x57\x5f\x64\xb5\x0a\x00\x9b\x1f\x6d\x81\xb3\x9a\xed\x92\xda\xf5\x57\xd6\xb0\xee\x52\x97\x08\x68\x9e\x57\x96\xd2\xce\x4f\xbf\x27\xdd\xd4\x84\x4b\x7c\x36\xc4\xc7\x4c\x70\x93\x75\x66\x6b\x19\x86\x30\xb2\xc4\xce\x07\x09\xd7\x49\x7f\xcc\x84\x2d\xe9\xf2\x7f\xd1\xd7\xb5\x1e\x42\x1f\xb4\xbc"
let signature = b"\x30\x81\x88\x02\x42\x01\xc3\x1d\x1f\x68\x33\x73\xf0\x26\x23\xd2\xf5\x43\xc7\x26\x69\xa8\x52\x5a\x28\x27\x44\x48\xdd\xfb\x54\x55\x33\xc3\xc8\x9f\x15\x0b\xea\x81\x53\xac\xc6\xa6\xd8\xcc\xa5\xaf\xcf\x7a\x59\x79\xc1\x2f\x9c\xe3\x4b\xe9\xeb\xae\x41\x4c\x18\xe8\xdb\x20\xf1\x1e\x09\xc9\x24\x02\x42\x00\xe3\xe5\xcf\x10\x5b\x99\xc0\xd6\xc7\x69\x43\x50\x07\xd5\x21\xb5\xa5\x69\xe8\x21\x49\xb4\xc1\x8f\xcc\x40\xfb\x4c\x96\x63\x31\x62\xd7\xc8\x88\xd4\x1a\x94\xd8\x08\xe5\x2e\xf1\xb0\x09\x85\xfe\x40\x8f\x66\xce\x08\xa1\x47\x17\x48\x1e\x60\x03\xd6\xf6\x50\x07\x5f\x84"
match EcdsaP521Sha512.verify(publicKey, b"hello", signature) {
Result<(), P521SignatureError>.Success {..} => {}
Result<(), P521SignatureError>.Failure {..} => {
return 1
}
}
// The same signature does not match another message.
return match EcdsaP521Sha512.verify(publicKey, b"hellp", signature) {
Result<(), P521SignatureError>.Failure { error: P521SignatureError.AuthenticationFailed } => 0
_ => 1
}
}Sign and verify#
import silk.crypto.p521 { EcdsaP521, P521, P521Error, P521SignatureError }
import silk.crypto.sha2 { Sha512 }
import silk.core.result { Result }
pub fn main() -> i32 {
// Real programs use `P521.generate()` with a secure `Random` provider.
let mut secret: [u8; 66] = [0; 66]
secret[65] = 7
let key = match P521.fromBytes(&secret) {
Result<P521, P521Error>.Success { value } => move value
Result<P521, P521Error>.Failure {..} => {
return 1
}
}
let publicKey = key.publicKey()
let signature = key.sign<Sha512>(b"hello")
match EcdsaP521.verify<Sha512>(&publicKey, b"hello", &signature) {
Result<(), P521SignatureError>.Success {..} => {}
Result<(), P521SignatureError>.Failure {..} => {
return 1
}
}
// The DER form, as in X.509 and TLS, has at most 139 bytes.
let mut der: [u8; 139] = [0; 139]
let length = EcdsaP521.toDer(&signature, &mut der)
if der[0] != 0x30 || length > 139 {
return 1
}
return 0
}Import as P521 with import silk.crypto.p521 { P521 }.
Public declarations: 5.
P521Error#
pub enum P521ErrorThe reason that a P-521 secret or public key is not valid. It holds no secret bytes.
InvalidLength#
InvalidLengthA secret is not 66 bytes, or a public key is not 133 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 P521Error#
impl Debug for P521ErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as InvalidLength.
P521#
pub struct P521A P-521 secret, for key agreement and for ECDSA signatures. It is not Copy.
When to use#
Use it for an ECDH key exchange where a protocol needs P-521, such as a TLS 1.3 key share
secp521r1, or to sign with ECDSA.
Details#
P521.agree consumes it, so make a new one for each exchange. P521.sign does not. Public keys are 133 bytes in
uncompressed SEC1 form: 0x04, then the X and Y coordinates, each 66 bytes big-endian. Secrets
are 66 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 P521#
impl P521Associated function validatePublicKey#
pub fn validatePublicKey(publicKey: &[u8]) -> Result<(), P521Error>Checks that publicKey is a valid uncompressed P-521 public key.
It must be 133 bytes: 0x04, then X and Y, each 66 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<P521, P521Error>Returns the secret for the 66 big-endian bytes bytes.
Errors#
InvalidLengthifbytesis not 66 bytes.InvalidScalarif the value is 0, or not less than the order of the curve.
Associated function generate#
pub effect fn generate() -> P521 ? &mut RandomReturns a new random secret, with bytes from the ambient Random.
It draws 66 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; 133]Returns the 133-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; 66], P521Error>Returns the 66-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; 66]Returns the 66 big-endian bytes of the secret, the input of P521.fromBytes.
Gotchas#
The bytes are the private key. Keep them secret.
Method sign#
pub fn sign<H: Digest>(self: &Self, message: &[u8]) -> [u8; 132]Returns the ECDSA signature of message, hashed with H, as 132 bytes: r, then s, each
66 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. EcdsaP521.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; 132]Returns the ECDSA signature of a message whose digest is digest, as 132 bytes: r, then
s, each 66 bytes big-endian.
H is the hash for the RFC 6979 nonce, normally the hash that made digest. A digest longer
than 521 bits is cut to its leftmost 521 bits, as ECDSA requires.
Associated function compressPublicKey#
pub fn compressPublicKey(publicKey: &[u8]) -> Result<[u8; 67], P521Error>Returns the 67-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; 133], P521Error>Returns the 133-byte uncompressed form of the compressed public key publicKey.
Errors#
InvalidLengthifpublicKeyis not 67 bytes.InvalidEncodingif it does not start with0x02or0x03.InvalidPointif X is not less than the field prime, or no point has that X.
P521SignatureError#
pub enum P521SignatureErrorThe reason that an ECDSA P-521 verification failed.
InvalidKey#
InvalidKeyThe public key is not a valid uncompressed P-521 key.
InvalidEncoding#
InvalidEncodingThe signature is not strict DER, or r or s is not between 1 and the order of the curve.
UnsupportedAlgorithm#
UnsupportedAlgorithmA certificate names a key or signature algorithm other than ECDSA P-521 with SHA-512.
InvalidParameters#
InvalidParametersA certificate has wrong algorithm parameters, or a BIT STRING with unused bits.
AuthenticationFailed#
AuthenticationFailedThe inputs are valid, but the signature does not match the message and the key.
Implementation Debug for P521SignatureError#
impl Debug for P521SignatureErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as InvalidKey.
EcdsaP521Sha512#
pub struct EcdsaP521Sha512Functions that verify ECDSA signatures on P-521 with SHA-512.
When to use#
Use it to verify a P-521 signature, for example in a certificate chain or a JSON Web Token with
ES521. To sign, use P521.sign and EcdsaP521.toDer.
Details#
Public keys are 133 bytes in uncompressed SEC1 form: 0x04, then the X and Y coordinates, each
66 bytes big-endian. Signatures are in DER form. 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. To check a certificate chain, use the silk.tls.trust_*
modules.
Implementation EcdsaP521Sha512#
impl EcdsaP521Sha512Associated function validatePublicKey#
pub fn validatePublicKey(publicKey: &[u8]) -> Result<(), P521SignatureError>Checks that publicKey is a valid uncompressed P-521 public key.
It must be 133 bytes: 0x04, then X and Y, each 66 bytes big-endian and less than the field
prime, and the point must be on the curve.
Errors#
InvalidKeyif it is not valid.
Associated function verify#
pub fn verify(publicKey: &[u8], message: &[u8], signature: &[u8]) -> Result<
(),
P521SignatureError,
>Checks that signature is a valid signature of message by publicKey.
publicKey is 133 bytes, uncompressed. signature is DER. The function hashes message with
SHA-512, so give the message, not its digest. Both high and low s values are accepted.
Errors#
InvalidKeyorInvalidEncodingfor input that is not valid.AuthenticationFailedif the signature does not match.
Associated function verifyCertificate#
pub fn verifyCertificate<'key, 'sig>(
keyAlgorithm: &AlgorithmView<'key>,
publicKey: &BitStringView<'key>,
message: &[u8],
signatureAlgorithm: &AlgorithmView<'sig>,
signature: &BitStringView<'sig>,
) -> Result<(), P521SignatureError>Verifies a certificate signature from the parts that silk.tls.certificate reads.
message is the signed part of the certificate (the TBSCertificate DER). The key algorithm
must be id-ecPublicKey with the curve secp521r1, and the signature algorithm must be
ecdsa-with-SHA512 with no parameters.
Errors#
UnsupportedAlgorithmorInvalidParametersif the algorithms do not match.InvalidKeyorInvalidEncodingif the key or the signature is not valid.AuthenticationFailedif the signature does not match.
Gotchas#
It checks only this one signature. It does not check the chain, the dates, or the names.
EcdsaP521#
pub struct EcdsaP521ECDSA on P-521 with any hash, and conversion between the two signature forms.
When to use#
Use it to verify a signature made with a hash other than SHA-256, or a signature in the fixed
132-byte form (r, then s), as in JSON Web Tokens and WebCrypto. To sign, use P521.sign.
For SHA-512 with DER signatures, as in TLS and X.509, EcdsaP521Sha512 is shorter.
Details#
Public keys are 133 bytes in uncompressed SEC1 form. A fixed-form signature is 132 bytes. A DER signature is a SEQUENCE of the two INTEGERs, at most 139 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 EcdsaP521#
impl EcdsaP521Associated function verify#
pub fn verify<H: Digest>(publicKey: &[u8], message: &[u8], signature: &[u8]) -> Result<
(),
P521SignatureError,
>Checks that signature, in the fixed 132-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 132 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<
(),
P521SignatureError,
>Checks that signature, in the fixed 132-byte form, is a valid signature by publicKey of a
message whose digest is digest.
A digest longer than 521 bits is cut to its leftmost 521 bits, as ECDSA requires.
Errors#
InvalidKey,InvalidEncoding, orAuthenticationFailed, as forEcdsaP521.verify.
Associated function fromDer#
pub fn fromDer(signature: &[u8]) -> Result<[u8; 132], P521SignatureError>Returns the fixed 132-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 132-byte signature signature to the start of output, and
returns its length, at most 139 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 132 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.