std/ecdsa
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_panicChecking 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.
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
endEcdsaErrorstruct
What reading a key answers for bytes that are not an ECDSA public key.
pub struct EcdsaError
def initialize(message : String)
def to_s : String
endEcdsaHashenum
The hashes a signature is made over.
pub enum EcdsaHash : Int32
SHA256 = 0
SHA384 = 1
SHA512 = 2
def digest(data : Array(UInt8)) : Array(UInt8)
endEcdsaPublicKeystruct
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
endError for EcdsaErrorimpl
impl Error for EcdsaError
def message : String
end
ECDSA signatures checked (FIPS 186-5, SEC 1), written in iyi, on the NIST curves P-256, P-384 and P-521.