cheatah
Module

p384

NIST P-384 (secp384r1) elliptic curve — ECDSA signature verification, plus the SPKI parsing needed to use it with TLS certificates. From scratch, no external libraries.

P-384 is the curve the big CAs' issuing chains are signed with (Sectigo, DigiCert, GlobalSign ECC roots): a typical CDN-fronted host serves a P-256 leaf whose intermediate and root signatures are ecdsa-with-SHA384 under P-384 keys. Adding it lets cheatah's tls client validate those real chains (api.github.com, Fastly-fronted hosts, …) and P-384 leaf certificates (ecdsa_secp384r1_sha384 CertificateVerify).

import io
import hashlib
import p384

let h = hashlib.sha384_digest("the message")
io.print(p384.verify_raw(pubkey_xy, h, sig))   # true

What's inside

  • The width-generic Weierstrass core shared with p256 (p256/ec_core.hpp): Montgomery field/scalar arithmetic with startup-derived constants, Jacobian points (a = -3), and Strauss-Shamir verification — the same battle-tested code p256 runs, instantiated at 6×64-bit limbs.

  • Parsing: DER SEQUENCE{r,s} (the TLS/X.509 form), raw r||s (the JWT ES384 form), and the uncompressed EC point out of a certificate's SPKI.

Byte conventions: scalars/coordinates are 48 big-endian bytes; a public key point is the 96 bytes X||Y; a raw signature is the 96 bytes r||s.

Correctness & security

Verified against the RFC 6979 Appendix A.2.6 P-384 test vectors (SHA-384 and SHA-256, pinning both hash-truncation semantics) and cross-checked through the X.509 suite against real OpenSSL-minted certificates (stdlib/tests/p384_test.cpp, stdlib/tests/x509_test.cpp).

Verify-only by design: certificate validation handles PUBLIC data, which is all TLS needs from this curve — there is no private key here to protect, so the straightforward (not constant-time) scalar routines are the right trade. cheatah's own TLS server signs with Ed25519, and JWT signing uses P-256/ES256.

Functions

fn bool verify_raw(const std::string &pubkey_xy, const std::string &msg_hash, const std::string &sig_raw) source#

Verify an ECDSA/P-384 signature given the raw 96-byte r||s form (the JWT ES384 layout).

Parameters
pubkey_xy

the public key (96 bytes X||Y).

msg_hash

the digest (truncated to 384 bits if longer, taken whole if shorter).

sig_raw

the signature as 96 bytes r||s.

Returns

true iff valid.

Complexity

O(1) — two scalar multiplications, computed as one Strauss-Shamir double chain.

Allocation

none.

fn bool verify_der(const std::string &pubkey_xy, const std::string &msg_hash, const std::string &sig_der) source#

Verify an ECDSA/P-384 signature.

Parameters
pubkey_xy

the public key as 96 bytes (X||Y), big-endian.

msg_hash

the message digest (e.g. 48 bytes of SHA-384); longer digests are truncated to the leftmost 384 bits (FIPS 186-4), shorter ones (e.g. SHA-256) are taken whole (X9.62 bits2int).

sig_der

the signature, DER-encoded SEQUENCE{INTEGER r, INTEGER s}.

Returns

true iff the signature is valid for pubkey_xy over msg_hash.

Complexity

O(1) — two scalar multiplications, computed as one Strauss-Shamir double chain.

Allocation

a temporary raw r||s signature string.

fn std::string spki_ec_point(std::string_view spki_or_cert_der) source#

Extract the P-384 public key point from a certificate / SubjectPublicKeyInfo DER: finds the uncompressed EC point of a secp384r1 key and returns its 96 bytes (X||Y).

Returns "" if the SPKI is not a P-384 uncompressed EC key.

Parameters
spki_or_cert_der

the certificate or SPKI DER bytes.

Returns

the 96-byte X||Y point, or "" if not a P-384 EC key.

Complexity

O(der length).

Allocation

the returned point.