iyi

Standard library, 0.18.0

std/ecdsa

ECDSA signatures checked (FIPS 186-5, SEC 1), written in iyi, on the NIST curves P-256, P-384 and P-521.

import std/ecdsa to use it. Source: src/std/ecdsa.iyi at v0.18.0.

  import std/ecdsa::{EcdsaPublicKey, EcdsaCurve, EcdsaHash, EcdsaError}
  key = EcdsaPublicKey.from_spki(spki_der).or_panic     # what X.509 carries
  key.verify(EcdsaHash::SHA256, message, signature)     # Bool
  key = EcdsaPublicKey.from_point(EcdsaCurve::P256, point).or_panic

Checking only, as std/rsa is. A TLS client checks a server's signatures and a certificate's, and makes none: there is no private key here and no secret, so the arithmetic is std/big's, plain and variable-time. The key a client agrees on is X25519's (std/curve25519), which is constant-time.

All three curves, not only P-256: of the roots a system trusts, most that are not RSA are P-384 (35 of the 39 on the machine this was written on), and one is P-521. A curve is named by its OID in the key's AlgorithmIdentifier (RFC 5480): prime256v1, secp384r1, secp521r1. Explicit curve parameters are refused, as RFC 5480 says a certificate must not carry them.

A key is a point on its curve. from_point takes the uncompressed encoding (0x04, then x and y at the curve's width) and refuses the rest: a compressed point, which RFC 5480 lets an implementation decline; a coordinate not below the field's prime; a point that does not satisfy the curve's equation. Every one of these curves has cofactor 1, so a point on the curve that is not the identity is a point of the group.

verify is SEC 1 section 4.1.4. The signature is the DER of Ecdsa-Sig-Value, SEQUENCE { r INTEGER, s INTEGER }, as both X.509 and TLS 1.3 carry it, read through std/asn1's strict DER: no trailing bytes, no integer written longer than it is, no negative r or s. Each of r and s must be in [1, n - 1]. The digest is cut to n's length in bits when it is longer (P-256 under SHA-384). u1·G + u2·Q is taken in one pass over the bits of both (Shamir's trick) in Jacobian coordinates, with the cases a general addition misses handled by name: a point added to itself, a point added to its negative, the identity on either side.

A signature that is not one is false, whatever the reason.

Pure computation: std/big's arithmetic, std/digest's hashes.

EcdsaCurveenum

The curves a key can be on.

std/ecdsaiyi doc src/std/ecdsa.iyi
pub enum EcdsaCurve : Int32
  P256 = 0
  P384 = 1
  P521 = 2
  # The length of the group's order n in bits.
  def bits : Int32
  def self.from_oid(oid : String) : EcdsaCurve | ::Nil
  # The curve's OID, as a key's AlgorithmIdentifier names it.
  def oid : String
  # The bytes a coordinate, or a scalar, is written in.
  def size : Int32
end

EcdsaErrorstruct

What reading a key answers for bytes that are not an ECDSA public key.

std/ecdsaiyi doc src/std/ecdsa.iyi
pub struct EcdsaError
  def initialize(message : String)
  def to_s : String
end

EcdsaHashenum

The hashes a signature is made over.

std/ecdsaiyi doc src/std/ecdsa.iyi
pub enum EcdsaHash : Int32
  SHA256 = 0
  SHA384 = 1
  SHA512 = 2
  def digest(data : Array(UInt8)) : Array(UInt8)
end

EcdsaPublicKeystruct

std/ecdsaiyi doc src/std/ecdsa.iyi
pub struct EcdsaPublicKey
  def curve : EcdsaCurve
  # The key from a point in SEC 1's uncompressed encoding (section 2.3.3):
  # 0x04, then x and y big-endian at the curve's width.
  def self.from_point(curve : EcdsaCurve, point : Array(UInt8)) : EcdsaPublicKey | EcdsaError
  # A SubjectPublicKeyInfo (RFC 5280 section 4.1.2.7) naming
  # id-ecPublicKey and one of the three curves (RFC 5480 section 2.1.1).
  def self.from_spki(der : Array(UInt8)) : EcdsaPublicKey | EcdsaError
  def self.from_spki(value : DerValue) : EcdsaPublicKey | EcdsaError
  def initialize(curve : EcdsaCurve, x : BigInt, y : BigInt, group : EcdsaGroup)
  # Whether *signature*, the DER of an Ecdsa-Sig-Value, is this key's
  # signature of *message* under *hash*.
  def verify(hash : EcdsaHash, message : Array(UInt8) | String, signature : Array(UInt8)) : Bool
  def x : BigInt
  def y : BigInt
end

Error for EcdsaErrorimpl

std/ecdsaiyi doc src/std/ecdsa.iyi
impl Error for EcdsaError
  def message : String
end