DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_ecdsa.cpp File Reference

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

#include "network_drivers/presentation/ssh/crypto/ssh_ecdsa.h"
#include "network_drivers/presentation/ssh/crypto/ssh_sha256.h"
#include <string.h>
#include "sdkconfig.h"
#include <esp_random.h>
#include <mbedtls/ecdh.h>
#include <mbedtls/ecdsa.h>
#include <mbedtls/ecp.h>

Go to the source code of this file.

Functions

bool ssh_ecdsa_p256_pubkey (uint8_t pub[SSH_ECDSA_P256_PUB_LEN], const uint8_t priv[SSH_ECDSA_P256_PRIV_LEN])
 Derive the uncompressed public point Q = d*G from a P-256 private scalar.
 
bool ssh_ecdsa_p256_sign (uint8_t sig[SSH_ECDSA_P256_SIG_LEN], const uint8_t *msg, size_t mlen, const uint8_t priv[SSH_ECDSA_P256_PRIV_LEN])
 Sign mlen bytes of msg with a P-256 private key (ECDSA, SHA-256).
 
bool ssh_ecdsa_p256_verify (const uint8_t pub[SSH_ECDSA_P256_PUB_LEN], const uint8_t *msg, size_t mlen, const uint8_t sig[SSH_ECDSA_P256_SIG_LEN])
 Verify a P-256 ECDSA signature (SHA-256) against an uncompressed public point.
 
bool ssh_ecdsa_p256_ecdh (uint8_t shared_x[SSH_ECDSA_P256_COORD_LEN], const uint8_t peer_pub[SSH_ECDSA_P256_PUB_LEN], const uint8_t priv[SSH_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 ssh_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 (DETWS_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 ssh_ecdsa.cpp.

Function Documentation

◆ ssh_ecdsa_p256_pubkey()

bool ssh_ecdsa_p256_pubkey ( uint8_t  pub[SSH_ECDSA_P256_PUB_LEN],
const uint8_t  priv[SSH_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 79 of file ssh_ecdsa.cpp.

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

◆ ssh_ecdsa_p256_sign()

bool ssh_ecdsa_p256_sign ( uint8_t  sig[SSH_ECDSA_P256_SIG_LEN],
const uint8_t *  msg,
size_t  mlen,
const uint8_t  priv[SSH_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 106 of file ssh_ecdsa.cpp.

References ssh_sha256(), and SSH_SHA256_DIGEST_LEN.

◆ ssh_ecdsa_p256_verify()

bool ssh_ecdsa_p256_verify ( const uint8_t  pub[SSH_ECDSA_P256_PUB_LEN],
const uint8_t *  msg,
size_t  mlen,
const uint8_t  sig[SSH_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 136 of file ssh_ecdsa.cpp.

References ssh_sha256(), and SSH_SHA256_DIGEST_LEN.

◆ ssh_ecdsa_p256_ecdh()

bool ssh_ecdsa_p256_ecdh ( uint8_t  shared_x[SSH_ECDSA_P256_COORD_LEN],
const uint8_t  peer_pub[SSH_ECDSA_P256_PUB_LEN],
const uint8_t  priv[SSH_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 166 of file ssh_ecdsa.cpp.

Referenced by ssh_kexdh_handle().