ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ecdsa.cpp File Reference

ECDSA over NIST P-256 for ecdsa-sha2-nistp256 (RFC 5656 / FIPS 186-4). More...

#include "crypto/asymmetric/ecdsa.h"
#include "crypto/hash/sha256.h"
#include <string.h>
#include "sdkconfig.h"
#include <esp_random.h>
#include <mbedtls/ecdh.h>
#include <mbedtls/ecdsa.h>
#include <mbedtls/ecp.h>
#include "crypto/crypto_opt.h"

Go to the source code of this file.

Namespaces

namespace  PC_CRYPTO_HOT
 

Functions

int PC_CRYPTO_HOT::ecdsa_rng (void *ctx, unsigned char *buf, size_t len)
 
bool pc_ecdsa_p256_pubkey (uint8_t pub[PC_ECDSA_P256_PUB_LEN], const uint8_t priv[PC_ECDSA_P256_PRIV_LEN])
 Derive the uncompressed public point Q = d*G from a P-256 private scalar.
 
bool pc_ecdsa_p256_sign (uint8_t sig[PC_ECDSA_P256_SIG_LEN], const uint8_t *msg, size_t mlen, const uint8_t priv[PC_ECDSA_P256_PRIV_LEN])
 Sign mlen bytes of msg with a P-256 private key (ECDSA, SHA-256).
 
bool pc_ecdsa_p256_verify (const uint8_t pub[PC_ECDSA_P256_PUB_LEN], const uint8_t *msg, size_t mlen, const uint8_t sig[PC_ECDSA_P256_SIG_LEN])
 Verify a P-256 ECDSA signature (SHA-256) against an uncompressed public point.
 
bool pc_ecdsa_p256_ecdh (uint8_t shared_x[PC_ECDSA_P256_COORD_LEN], const uint8_t peer_pub[PC_ECDSA_P256_PUB_LEN], const uint8_t priv[PC_ECDSA_P256_PRIV_LEN])
 P-256 ECDH: the shared-secret X coordinate of d * Q_peer (RFC 5656 §4 / RFC 5903).
 

Detailed Description

ECDSA over NIST P-256 for ecdsa-sha2-nistp256 (RFC 5656 / FIPS 186-4).

═══════════════════════════════════════════════════════════════════════════ THREE BUILD PATHS ═══════════════════════════════════════════════════════════════════════════

ESP32-S3 (Arduino): a self-contained P-256 whose every 256-bit field / scalar multiply is one modular multiply on the RSA/MPI hardware accelerator (the same engine pc_fe25519.h drives for X25519 / Ed25519) - the MODMULT is modulus-generic, so it serves both the field domain (mod p) and the scalar domain (mod n) by swapping the {M, m', R^2} constants. Point arithmetic uses the exception-free complete formulas (Renes-Costello-Batina 2016, EFD add/dbl-2015-rcb, a = -3) driven by a constant-time 4-bit fixed-window scalar multiply (uniform op sequence, full-table masked select, no input-dependent branches). Signing is RFC 6979 deterministic, so the on-device output is byte-exact to the published vectors (same KATs as native). This is the production path (PC_ECDSA_MPI_HW); sign / verify / ecdh run ~2.7-2.9x faster than the mbedTLS ECP path it replaces on non-S3 targets.

Native: the identical complete-formula / RFC 6979 code, but each field multiply is a software schoolbook product reduced bit-serially. Only fp_mul differs from the S3 path, so the native KATs validate the exact point / scalar arithmetic the accelerator runs. Test-only, not in firmware.

Other Arduino (classic ESP32 etc.): mbedTLS (mbedtls_ecdsa_*, mbedtls_ecp_*) - hardware big-integer math and side-channel hardening, signing with the ESP32 hardware RNG. The MODMULT register layout is an S3 specialization, so non-S3 targets keep the portable mbedTLS path (no perf regression).

═══════════════════════════════════════════════════════════════════════════ WIRE FORMATS (assembled by the SSH transport/auth layers, not here) ═══════════════════════════════════════════════════════════════════════════

Public-key blob: string("ecdsa-sha2-nistp256") || string("nistp256") || string(Q), Q = 0x04||X||Y. Signature blob: string("ecdsa-sha2-nistp256") || string( mpint(r) || mpint(s) ); this module exposes raw r||s (32+32 big-endian) and the layers mpint-wrap them. ECDH shared secret (RFC 5656 sec 4): K = X coordinate of d*Q_peer, raw 32-byte big-endian.

Author
Douglas Quigg (dstroy0)
Date
2026

Definition in file ecdsa.cpp.

Function Documentation

◆ pc_ecdsa_p256_pubkey()

bool pc_ecdsa_p256_pubkey ( uint8_t  pub[PC_ECDSA_P256_PUB_LEN],
const uint8_t  priv[PC_ECDSA_P256_PRIV_LEN] 
)

Derive the uncompressed public point Q = d*G from a P-256 private scalar.

Parameters
[out]pub65-byte uncompressed point 0x04 || X || Y.
[in]priv32-byte big-endian private scalar d (must satisfy 1 <= d < n).
Returns
true on success, false if priv is 0 or >= the group order n.

Definition at line 98 of file ecdsa.cpp.

References PC_ECDSA_P256_PRIV_LEN, and PC_ECDSA_P256_PUB_LEN.

Referenced by pc_ssh_hostkey_ecdsa_set(), ssh_kex_generate(), and ssh_kexdh_handle().

◆ pc_ecdsa_p256_sign()

bool pc_ecdsa_p256_sign ( uint8_t  sig[PC_ECDSA_P256_SIG_LEN],
const uint8_t *  msg,
size_t  mlen,
const uint8_t  priv[PC_ECDSA_P256_PRIV_LEN] 
)

Sign mlen bytes of msg with a P-256 private key (ECDSA, SHA-256).

The message is hashed with SHA-256 internally. Native builds sign deterministically (RFC 6979); Arduino builds sign with the hardware RNG. Both produce a valid signature.

Parameters
[out]sig64-byte raw signature r || s (big-endian, 32 + 32).
[in]msgMessage to sign (typically the KEX exchange hash H).
[in]mlenLength of msg.
[in]priv32-byte big-endian private scalar d.
Returns
true on success, false on invalid key or internal failure.

Definition at line 127 of file ecdsa.cpp.

References PC_ECDSA_P256_COORD_LEN, PC_ECDSA_P256_PRIV_LEN, pc_sha256(), and PC_SHA256_DIGEST_LEN.

◆ pc_ecdsa_p256_verify()

bool pc_ecdsa_p256_verify ( const uint8_t  pub[PC_ECDSA_P256_PUB_LEN],
const uint8_t *  msg,
size_t  mlen,
const uint8_t  sig[PC_ECDSA_P256_SIG_LEN] 
)

Verify a P-256 ECDSA signature (SHA-256) against an uncompressed public point.

Parameters
[in]pub65-byte uncompressed point 0x04 || X || Y (rejected if not on-curve).
[in]msgSigned message.
[in]mlenLength of msg.
[in]sig64-byte raw signature r || s (big-endian, 32 + 32).
Returns
true if the signature is valid, false otherwise.

Definition at line 159 of file ecdsa.cpp.

References PC_ECDSA_P256_COORD_LEN, PC_ECDSA_P256_PUB_LEN, pc_sha256(), and PC_SHA256_DIGEST_LEN.

◆ pc_ecdsa_p256_ecdh()

bool pc_ecdsa_p256_ecdh ( uint8_t  shared_x[PC_ECDSA_P256_COORD_LEN],
const uint8_t  peer_pub[PC_ECDSA_P256_PUB_LEN],
const uint8_t  priv[PC_ECDSA_P256_PRIV_LEN] 
)

P-256 ECDH: the shared-secret X coordinate of d * Q_peer (RFC 5656 §4 / RFC 5903).

Backs the ecdh-sha2-nistp256 SSH key exchange. Validates that peer_pub is a valid on-curve point and that the product is not the identity; the returned 32-byte big-endian X coordinate is the shared field element the transport encodes as an mpint.

Parameters
[out]shared_x32-byte big-endian X coordinate of d * Q_peer.
[in]peer_pub65-byte uncompressed peer point 0x04 || X || Y.
[in]priv32-byte big-endian private scalar d (1 <= d < n).
Returns
true on success, false on an invalid peer point / scalar or an identity result.

Definition at line 191 of file ecdsa.cpp.

References PC_ECDSA_P256_COORD_LEN, PC_ECDSA_P256_PRIV_LEN, and PC_ECDSA_P256_PUB_LEN.

Referenced by ssh_kexdh_handle().