nx_ecdsa_p256_sign.nx
buildroot/runtime/nx_ecdsa_p256_sign.nx
about
nx_ecdsa_p256_sign.nx -- ECDSA P-256 signature generation.
Companion to nx_ecdsa_p256 (verify, shipped 2026-05-19 with RFC 6979
KAT green). Implements signature GENERATION via RFC 6979 deterministic
nonce so the same {priv_key, msg_hash} pair always produces the same
(r, s) -- per [[feedback-bits-up-exceed-never-match]] determinism axis.
Pure NishiLang composition. No new substrate primitives invented;
every step uses an already-shipped op:
nx_drbg_hmac -- RFC 6979 HMAC-DRBG for deterministic k
nx_p256_scalar_mul -- k * G (the generator)
nx_p256_point.to_affine -- normalize R from Jacobian to (x, y)
nx_p256_modn -- mod-n arithmetic (add, mul, inv)
Algorithm (FIPS 186-5 ยง6.4.1):
Input: private key d (256-bit scalar, 1 <= d < n)
message hash e (256-bit, already SHA-256'd by caller)
1. Generate deterministic k via RFC 6979:
seed = d_bytes || e_bytes
HMAC-DRBG(seed) -> k_bytes; reject if k == 0 or k >= n
(in practice for P-256 + SHA-256 + a sane priv key,
the first DRBG output is always acceptable)
2. R = k * G (R is a point on the curve)
3. Convert R to affine: get R.x
4. r = R.x mod n (reduce x coordinate into Z/nZ)
5. Reject if r == 0 (probability ~2^-256; if hit, reseed DRBG
with extra byte and retry -- caller-visible verdict)
6. s = k^-1 * (e + r * d) mod n
7. Reject if s == 0 (same probability)
8. Return (r, s) as the signature
Public API:
nx_ecdsa_p256_sign(priv_key: *i64, -- 256-bit scalar d
hash_e: *i64, -- 256-bit msg digest
out_r: *i64, out_s: *i64) -> verdict
Unlocks:
dependencies 8 imports · 10 importers
imports: nx_syscalls.nxnx_u256.nxnx_p256_modn.nxnx_p256_point.nxnx_p256_scalar_mul.nxnx_p256_comb.nxnx_drbg_hmac.nxnx_ecdsa_p256.nx
imported by: nx_bench_sign.nxnx_bench_verify.nxnx_crypto_bench.nxnx_crypto_bench_full.nxnx_ecdsa_p256_sign_test.nxnx_jose_es256.nxnx_p256_sign_bench.nxnx_p256_verify_bench.nxnx_tls13_server_session_emit_cv.nxnx_x509_build.nx
structs
| none |
consts
| 64 | const NX_ECDSA_SIGN_OK: i64 = 1 |
| 65 | const NX_ECDSA_SIGN_BAD_PRIVKEY: i64 = 2 // d == 0 or d >= n |
| 66 | const NX_ECDSA_SIGN_R_ZERO: i64 = 3 // r == 0; reseed + retry |
| 67 | const NX_ECDSA_SIGN_S_ZERO: i64 = 4 // s == 0; reseed + retry |
| 68 | const NX_ECDSA_SIGN_K_ZERO: i64 = 5 // DRBG produced k == 0 |
| 69 | const NX_ECDSA_SIGN_VERDICT_N: i64 = 6 |
functions
| 92 | func _ecdsa_sign_derive_k(priv_be: *u8, hash_be: *u8, out_k_limbs: *i64) -> i64 |
| 137 | func _nx_ecdsa_p256_sign_impl(priv_key: *i64, called by 1: nx_ecdsa_p256_sign calls 18: u256_is_zerou256_allocp256_modn_load_nu256_copyp256_modn_reduceu256_eq+12 |
| 226 | func nx_ecdsa_p256_sign(priv_key: *i64, |
| 235 | func nx_ecdsa_p256_sign_verdict_is_valid(v: i64) -> i64 |