Browse the docs

silk.crypto.p256.

Key agreement (ECDH) and ECDSA signature verification on the NIST P-256 curve (secp256r1).

When to use#

Use P256 for an ECDH key exchange where a protocol needs P-256, such as a TLS 1.3 key share secp256r1. For a new design, silk.crypto.x25519 is simpler. Use EcdsaP256Sha256 to verify an ECDSA signature with SHA-256, for example the signature of a certificate or a JSON Web Token with ES256.

Details#

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. P256.agree returns the X coordinate of the shared point and consumes the key. Signatures are in DER form, as in X.509 and TLS. Nothing allocates. This module cannot sign.

Errors#

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

  • P256SignatureError for a 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#

Agree on a shared secret#

silk
import silk.crypto.p256 { P256, P256Error }
import silk.core.result { Result }

pub fn main() -> i32 {
  // Real programs use `P256.generate()` with a secure `Random` provider.
  let mut one: [u8; 32] = [0; 32]
  one[31] = 1
  let mut two: [u8; 32] = [0; 32]
  two[31] = 2
  let alice = match P256.fromBytes(&one) {
    Result<P256, P256Error>.Success { value } => move value
    Result<P256, P256Error>.Failure { error: _ } => {
      return 1
    }
  }
  let bob = match P256.fromBytes(&two) {
    Result<P256, P256Error>.Success { value } => move value
    Result<P256, P256Error>.Failure { error: _ } => {
      return 1
    }
  }
  let alicePublic = P256.publicKey(&alice)
  let bobPublic = P256.publicKey(&bob)

  // The public key of the secret 1 is the base point, with X = 6b17d1f2...
  if alicePublic[0] != 0x04 || alicePublic[1] != 0x6b {
    return 1
  }
  let fromAlice = match P256.agree(move alice, &bobPublic) {
    Result<[u8; 32], P256Error>.Success { value } => value
    Result<[u8; 32], P256Error>.Failure { error: _ } => {
      return 1
    }
  }
  let fromBob = match P256.agree(move bob, &alicePublic) {
    Result<[u8; 32], P256Error>.Success { value } => value
    Result<[u8; 32], P256Error>.Failure { error: _ } => {
      return 1
    }
  }
  if fromAlice[0] != fromBob[0] || fromAlice[31] != fromBob[31] {
    return 1
  }
  return 0
}

Verify a signature#

silk
import silk.crypto.p256 { EcdsaP256Sha256, P256SignatureError }
import silk.core.result { Result }

pub fn main() -> i32 {
  // A public key and a signature of "hello" that another program made.
  let publicKey: [u8; 65] = [
    0x04,
    0x26,
    0xef,
    0xce,
    0xbd,
    0x0e,
    0xe9,
    0xe3,
    0x4a,
    0x66,
    0x91,
    0x87,
    0xe1,
    0x8b,
    0x3a,
    0x91,
    0x22,
    0xb2,
    0xf7,
    0x33,
    0x94,
    0x5b,
    0x64,
    0x9c,
    0xc9,
    0xf9,
    0xf9,
    0x21,
    0xe9,
    0xf9,
    0xda,
    0xd8,
    0x12,
    0x90,
    0x23,
    0x8b,
    0xde,
    0x9c,
    0xc7,
    0xbb,
    0x33,
    0x0d,
    0x15,
    0x0c,
    0x67,
    0x70,
    0x4d,
    0xd2,
    0x5a,
    0xe7,
    0x05,
    0x52,
    0x05,
    0x74,
    0x4b,
    0x6f,
    0x31,
    0xbf,
    0x40,
    0x70,
    0x74,
    0x58,
    0x72,
    0xd0,
    0xe6
  ]
  let signature: [u8; 72] = [
    0x30,
    0x46,
    0x02,
    0x21,
    0x00,
    0xc9,
    0x70,
    0x75,
    0x86,
    0x11,
    0x4e,
    0x93,
    0x49,
    0xd8,
    0x2b,
    0x8d,
    0x3d,
    0x97,
    0x52,
    0x86,
    0x0c,
    0x9f,
    0xab,
    0x24,
    0xf2,
    0x9e,
    0xff,
    0x83,
    0x6e,
    0x0a,
    0x6f,
    0xbf,
    0xc5,
    0x7a,
    0x30,
    0x9d,
    0x1c,
    0x02,
    0x21,
    0x00,
    0x88,
    0x3f,
    0xb1,
    0x3c,
    0x3b,
    0x44,
    0x28,
    0x70,
    0x2e,
    0xc4,
    0x88,
    0x52,
    0xf8,
    0x5e,
    0xe5,
    0x63,
    0x1a,
    0xec,
    0x2f,
    0x25,
    0x23,
    0xe3,
    0xd7,
    0x18,
    0xf8,
    0xae,
    0x33,
    0x25,
    0xe0,
    0x00,
    0xdc,
    0xa8
  ]
  match EcdsaP256Sha256.verify(&publicKey, b"hello", &signature) {
    Result<(), P256SignatureError>.Success { value: _ } => {}
    Result<(), P256SignatureError>.Failure { error: _ } => {
      return 1
    }
  }

  // The same signature does not match another message.
  return match EcdsaP256Sha256.verify(&publicKey, b"hellp", &signature) {
    Result<(), P256SignatureError>.Failure { error: P256SignatureError.AuthenticationFailed } => 0
    _ => 1
  }
}

Import as P256 with import silk.crypto.p256 { P256 }.

Public declarations: 4.

P256Error#

silk
pub enum P256Error

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

InvalidLength#

silk
InvalidLength

A secret is not 32 bytes, or a public key is not 65 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 P256Error#

silk
impl Debug for P256Error

Operation debug#

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

Writes the name of the variant, such as InvalidLength.

P256#

silk
pub struct P256

A P-256 secret for key agreement. It is not Copy.

When to use#

Use it for an ECDH key exchange where a protocol needs P-256, such as a TLS 1.3 key share secp256r1. For a new design, silk.crypto.x25519 is simpler.

Details#

P256.agree consumes it. Make a new one for each exchange. 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 P256#

silk
impl P256

Associated function validatePublicKey#

silk
pub fn validatePublicKey(publicKey: &[u8]) -> Result<(), P256Error>

Checks that publicKey is a valid uncompressed P-256 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, or InvalidPoint.

Associated function fromBytes#

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

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

Errors#

  • InvalidLength if bytes is not 32 bytes.

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

Associated function generate#

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

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

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

silk
pub fn agree(self: Self, peer: &[u8]) -> Result<[u8; 32], P256Error>

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

P256SignatureError#

silk
pub enum P256SignatureError

The reason that an ECDSA P-256 verification failed.

InvalidLength#

silk
InvalidLength

The message is longer than SHA-256 allows (2^61 - 1 bytes).

InvalidKey#

silk
InvalidKey

The public key is not a valid uncompressed P-256 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-256 with SHA-256.

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

silk
impl Debug for P256SignatureError

Operation debug#

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

Writes the name of the variant, such as InvalidLength.

EcdsaP256Sha256#

silk
pub struct EcdsaP256Sha256

Functions that verify ECDSA signatures on P-256 with SHA-256.

When to use#

Use it to verify an ECDSA signature with SHA-256, for example the signature of a certificate or a JSON Web Token with ES256. It cannot sign.

Details#

Public keys are 65 bytes in uncompressed SEC1 form. Signatures are in DER form, as in X.509 and TLS. 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 EcdsaP256Sha256#

silk
impl EcdsaP256Sha256

Associated function verify#

silk
pub fn verify(publicKey: &[u8], message: &[u8], signature: &[u8]) -> Result<
    (),
    P256SignatureError,
  >

Checks that signature is a valid signature of message by publicKey.

publicKey is 65 bytes, uncompressed. signature is DER. The function hashes message with SHA-256, so give the message, not its digest. Both high and low s values are accepted.

Errors#

  • InvalidLength, 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<(), P256SignatureError>

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 secp256r1, and the signature algorithm must be ecdsa-with-SHA256 with no parameters.

Errors#

  • UnsupportedAlgorithm or InvalidParameters if the algorithms do not match.

  • InvalidLength, 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.

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.

View the source of this page