DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_curve25519.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ssh_curve25519.h
6 * @brief Curve25519 field arithmetic + X25519 (RFC 7748) for the curve25519-sha256 KEX.
7 *
8 * Field elements are GF(2^255 - 19) in a portable radix-2^16 representation (sixteen
9 * int64 limbs), so no 128-bit integer type is needed - important because 32-bit xtensa
10 * (ESP32) gcc has no __int128. The same field arithmetic backs Ed25519 (ssh_ed25519),
11 * so the field ops are exported here. Correctness is pinned to the RFC 7748 §5.2 test
12 * vectors (test_ssh_ed25519). No heap; all state is on the stack.
13 *
14 * @author Douglas Quigg (dstroy0)
15 * @date 2026
16 */
17
18#ifndef DETERMINISTICESPASYNCWEBSERVER_SSH_CURVE25519_H
19#define DETERMINISTICESPASYNCWEBSERVER_SSH_CURVE25519_H
20
21#include <stddef.h>
22#include <stdint.h>
23
24/** @brief A field element of GF(2^255 - 19): 16 limbs, radix 2^16 (limb i weighs 2^(16i)). */
25typedef int64_t ssh_gf[16];
26
27// --- Field arithmetic (shared with ssh_ed25519) ----------------------------
28
29void ssh_gf_copy(ssh_gf out, const ssh_gf in); ///< out = in
30void ssh_gf_add(ssh_gf out, const ssh_gf a, const ssh_gf b); ///< out = a + b (unreduced)
31void ssh_gf_sub(ssh_gf out, const ssh_gf a, const ssh_gf b); ///< out = a - b (unreduced)
32void ssh_gf_mul(ssh_gf out, const ssh_gf a, const ssh_gf b); ///< out = a * b mod p
33void ssh_gf_sq(ssh_gf out, const ssh_gf a); ///< out = a^2 mod p
34void ssh_gf_inv(ssh_gf out, const ssh_gf a); ///< out = a^-1 mod p (= a^(p-2))
35void ssh_gf_pack(uint8_t out[32], const ssh_gf a); ///< canonical little-endian 32-byte encoding
36void ssh_gf_unpack(ssh_gf out, const uint8_t in[32]); ///< decode 32 bytes (high bit ignored)
37void ssh_gf_cswap(ssh_gf p, ssh_gf q, int b); ///< constant-time conditional swap of p,q when b==1
38
39// --- X25519 (RFC 7748) -----------------------------------------------------
40
41/**
42 * @brief X25519 scalar multiplication: @p out = @p scalar * @p point (RFC 7748 §5).
43 *
44 * @p scalar and @p point are 32-byte little-endian; the scalar is clamped internally.
45 * @p out may alias neither input. Constant-time in the scalar (Montgomery ladder with
46 * conditional swaps).
47 */
48void ssh_x25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32]);
49
50/** @brief X25519 with the standard base point u=9: @p out = @p scalar * G. */
51void ssh_x25519_base(uint8_t out[32], const uint8_t scalar[32]);
52
53#endif // DETERMINISTICESPASYNCWEBSERVER_SSH_CURVE25519_H
int64_t ssh_gf[16]
A field element of GF(2^255 - 19): 16 limbs, radix 2^16 (limb i weighs 2^(16i)).
void ssh_gf_cswap(ssh_gf p, ssh_gf q, int b)
constant-time conditional swap of p,q when b==1
void ssh_x25519_base(uint8_t out[32], const uint8_t scalar[32])
X25519 with the standard base point u=9: out = scalar * G.
void ssh_gf_add(ssh_gf out, const ssh_gf a, const ssh_gf b)
out = a + b (unreduced)
void ssh_gf_mul(ssh_gf out, const ssh_gf a, const ssh_gf b)
out = a * b mod p
void ssh_gf_unpack(ssh_gf out, const uint8_t in[32])
decode 32 bytes (high bit ignored)
void ssh_gf_copy(ssh_gf out, const ssh_gf in)
out = in
void ssh_gf_pack(uint8_t out[32], const ssh_gf a)
canonical little-endian 32-byte encoding
void ssh_gf_sub(ssh_gf out, const ssh_gf a, const ssh_gf b)
out = a - b (unreduced)
void ssh_x25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32])
X25519 scalar multiplication: out = scalar * point (RFC 7748 §5).
void ssh_gf_sq(ssh_gf out, const ssh_gf a)
out = a^2 mod p
void ssh_gf_inv(ssh_gf out, const ssh_gf a)
out = a^-1 mod p (= a^(p-2))