cheatah
Source

stdlib/p384/p384.cpp

1// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).
2// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.
3// p384.cpp — NIST P-384 (secp384r1) ECDSA verification, from scratch. See p384.hpp.
4//
5// All the arithmetic is the width-generic Weierstrass core shared with p256
6// (p256/ec_core.hpp), instantiated at 6 limbs; this file supplies only the P-384
7// curve constants (SEC 2 / FIPS 186-4 — a typo here is caught by the base-point
8// on-curve unit test and the RFC 6979 known-answer vectors) and the SPKI point
9// extraction. Verify-only: TLS certificate validation never signs with P-384.
11#include "p384.hpp"
13#include <array>
14#include <cstddef>
16#include "ec_core.hpp"
18namespace cheatah::p384 {
20namespace {
22namespace ec = cheatah::ec;
24// ---- P-384 curve traits (normal form, little-endian limbs) -----------------
25// p = 2^384 - 2^128 - 2^96 + 2^32 - 1
26struct P384Curve {
27 static constexpr std::size_t kLimbs = 6;
28 static constexpr std::array<ec::u64, 6> P = {0x00000000FFFFFFFFull, 0xFFFFFFFF00000000ull,
29 0xFFFFFFFFFFFFFFFEull, 0xFFFFFFFFFFFFFFFFull,
30 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull};
31 static constexpr std::array<ec::u64, 6> N = {0xECEC196ACCC52973ull, 0x581A0DB248B0A77Aull,
32 0xC7634D81F4372DDFull, 0xFFFFFFFFFFFFFFFFull,
33 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFFull};
34 static constexpr std::array<ec::u64, 6> B = {0x2A85C8EDD3EC2AEFull, 0xC656398D8A2ED19Dull,
35 0x0314088F5013875Aull, 0x181D9C6EFE814112ull,
36 0x988E056BE3F82D19ull, 0xB3312FA7E23EE7E4ull};
37 static constexpr std::array<ec::u64, 6> GX = {0x3A545E3872760AB7ull, 0x5502F25DBF55296Cull,
38 0x59F741E082542A38ull, 0x6E1D3B628BA79B98ull,
39 0x8EB1C71EF320AD74ull, 0xAA87CA22BE8B0537ull};
40 static constexpr std::array<ec::u64, 6> GY = {0x7A431D7C90EA0E5Full, 0x0A60B1CE1D7E819Dull,
41 0xE9DA3113B5F0B8C0ull, 0xF8F41DBD289A147Cull,
42 0x5D9E98BF9292DC29ull, 0x3617DE4A96262C6Full};
43};
44static_assert(ec::WeierstrassCurve<P384Curve>);
46} // namespace
48bool verify_raw(const std::string& pubkey_xy, const std::string& msg_hash,
49 const std::string& sig_raw) {
50 return ec::verify_raw<P384Curve>(pubkey_xy, msg_hash, sig_raw);
53bool verify_der(const std::string& pubkey_xy, const std::string& msg_hash,
54 const std::string& sig_der) {
55 return ec::verify_der<P384Curve>(pubkey_xy, msg_hash, sig_der);
58std::string spki_ec_point(std::string_view der) {
59 // Find the uncompressed-point marker: BIT STRING (03) <len> 00 04 <X(48)><Y(48)>.
60 // The OID id-ecPublicKey + secp384r1 precedes it; we anchor on the 0x04 point and
61 // the P-384 BIT STRING length 98 (0x62) — mutually exclusive with p256's 66.
62 const unsigned char* p = (const unsigned char*)der.data();
63 const std::size_t n = der.size();
64 for (std::size_t i = 0; i + 2 + 97 <= n; ++i) {
65 // BIT STRING tag, then a length, then 00 (unused bits), then 04 (uncompressed)
66 if (p[i] == 0x03 && p[i + 2] == 0x00 && p[i + 3] == 0x04) {
67 const std::size_t len = p[i + 1];
68 if (len == 98 && i + 4 + 96 <= n) return std::string((const char*)p + i + 4, 96);
69 }
70 }
71 return std::string();
74} // namespace cheatah::p384