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#

  • P521Error for a secret or a peer public key that is not valid.

  • P521SignatureError for 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#

silk
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#

silk
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#

silk
pub enum P521Error

The reason that a P-521 secret or public key is not valid. It holds no secret bytes.

InvalidLength#

silk
InvalidLength

A secret is not 66 bytes, or a public key is not 133 bytes.

InvalidScalar#

silk
InvalidScalar

The secret is 0, or not less than the order of the curve.

InvalidEncoding#

silk
InvalidEncoding

The public key does not start with 0x04. Compressed keys are not supported.

InvalidPoint#

silk
InvalidPoint

A coordinate is not less than the field prime, or the point is not on the curve.

Implementation Debug for P521Error#

silk
impl Debug for P521Error

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as InvalidLength.

P521#

silk
pub struct P521

A 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#

silk
impl P521

Associated function validatePublicKey#

silk
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, or InvalidPoint.

Associated function fromBytes#

silk
pub fn fromBytes(bytes: &[u8]) -> Result<P521, P521Error>

Returns the secret for the 66 big-endian bytes bytes.

Errors#

  • InvalidLength if bytes is not 66 bytes.

  • InvalidScalar if the value is 0, or not less than the order of the curve.

Associated function generate#

silk
pub effect fn generate() -> P521 ? &mut Random

Returns 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#

silk
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#

silk
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, or InvalidPoint if peer is 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#

silk
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#

silk
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#

silk
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#

silk
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, or InvalidPoint if publicKey is not a valid uncompressed public key.

Associated function decompressPublicKey#

silk
pub fn decompressPublicKey(publicKey: &[u8]) -> Result<[u8; 133], P521Error>

Returns the 133-byte uncompressed form of the compressed public key publicKey.

Errors#

  • InvalidLength if publicKey is not 67 bytes.

  • InvalidEncoding if it does not start with 0x02 or 0x03.

  • InvalidPoint if X is not less than the field prime, or no point has that X.

P521SignatureError#

silk
pub enum P521SignatureError

The reason that an ECDSA P-521 verification failed.

InvalidKey#

silk
InvalidKey

The public key is not a valid uncompressed P-521 key.

InvalidEncoding#

silk
InvalidEncoding

The signature is not strict DER, or r or s is not between 1 and the order of the curve.

UnsupportedAlgorithm#

silk
UnsupportedAlgorithm

A certificate names a key or signature algorithm other than ECDSA P-521 with SHA-512.

InvalidParameters#

silk
InvalidParameters

A certificate has wrong algorithm parameters, or a BIT STRING with unused bits.

AuthenticationFailed#

silk
AuthenticationFailed

The inputs are valid, but the signature does not match the message and the key.

Implementation Debug for P521SignatureError#

silk
impl Debug for P521SignatureError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as InvalidKey.

EcdsaP521Sha512#

silk
pub struct EcdsaP521Sha512

Functions 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#

silk
impl EcdsaP521Sha512

Associated function validatePublicKey#

silk
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#

  • InvalidKey if it is not valid.

Associated function verify#

silk
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#

  • InvalidKey or InvalidEncoding for input that is not valid.

  • AuthenticationFailed if the signature does not match.

Associated function verifyCertificate#

silk
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#

  • UnsupportedAlgorithm or InvalidParameters if the algorithms do not match.

  • InvalidKey or InvalidEncoding if the key or the signature is not valid.

  • AuthenticationFailed if the signature does not match.

Gotchas#

It checks only this one signature. It does not check the chain, the dates, or the names.

EcdsaP521#

silk
pub struct EcdsaP521

ECDSA 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#

silk
impl EcdsaP521

Associated function verify#

silk
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#

  • InvalidKey if publicKey is not a valid uncompressed key.

  • InvalidEncoding if signature is not 132 bytes, or r or s is not between 1 and the order of the curve.

  • AuthenticationFailed if the signature does not match.

Gotchas#

The program traps if message is longer than H allows.

Associated function verifyDigest#

silk
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#

Associated function fromDer#

silk
pub fn fromDer(signature: &[u8]) -> Result<[u8; 132], P521SignatureError>

Returns the fixed 132-byte form of the DER signature signature.

Errors#

  • InvalidEncoding if signature is not strict DER, or r or s is not between 1 and the order of the curve.

Associated function toDer#

silk
pub fn toDer(signature: &[u8], output: &mut [u8]) -> usize

Writes 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.

View the source of this page