ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
bignum.cpp
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 bignum.cpp
6 * @brief 2048-bit Montgomery modular exponentiation for DH-group14.
7 *
8 * ─── Montgomery parameter for group-14 ───────────────────────────────────
9 * The group-14 prime p ends in ...FFFFFFFF FFFFFFFF (little-endian d[0]=d[1]=
10 * 0xFFFFFFFF). The Montgomery parameter:
11 *
12 * p_inv = (-(p mod 2^32))^(-1) mod 2^32
13 *
14 * p mod 2^32 = 0xFFFFFFFF
15 * -(0xFFFFFFFF) mod 2^32 = 0x00000001
16 * 0x00000001^(-1) mod 2^32 = 1
17 *
18 * So p_inv = 1 for the group-14 prime.
19 * In the SOS reduction pass: m_i = t[i] * p_inv mod 2^32 = t[i].
20 *
21 * ─── R² mod p ────────────────────────────────────────────────────────────
22 * R = 2^2048. R² mod p = 2^4096 mod p.
23 * It is computed once at startup by bn_init() via 4096 doublings mod p,
24 * and stored in the static s_g14.r2 constant.
25 * ─────────────────────────────────────────────────────────────────────────
26 */
27
29#include "crypto/crypto_opt.h"
30#include "server/mmgr/secure.h"
31#include <string.h>
33
34// The modexp below borrows its Montgomery temporaries from the secure pool as one working set. It does not
35// know where that memory comes from or that releasing it wipes - it asks for a resource and uses it.
36
37// ---------------------------------------------------------------------------
38// Group-14 prime and generator (RFC 3526, §3)
39// Little-endian 32-bit limbs: d[0] = least significant.
40// ---------------------------------------------------------------------------
41
43 // 2048-bit MODP group-14 prime
44 0xFFFFFFFFu, 0xFFFFFFFFu, 0x8AACaa68u, 0x15728E5Au, 0x98FA0510u, 0x15D22618u, 0xEA956AE5u, 0x3995497Cu,
45 0x95581718u, 0xDE2BCBF6u, 0x6F4C52C9u, 0xB5C55DF0u, 0xEC07A28Fu, 0x9B2783A2u, 0x180E8603u, 0xE39E772Cu,
46 0x2E36CE3Bu, 0x32905E46u, 0xCA18217Cu, 0xF1746C08u, 0x4ABC9804u, 0x670C354Eu, 0x7096966Du, 0x9ED52907u,
47 0x208552BBu, 0x1C62F356u, 0xDCA3AD96u, 0x83655D23u, 0xFD24CF5Fu, 0x69163FA8u, 0x1C55D39Au, 0x98DA4836u,
48 0xA163BF05u, 0xC2007CB8u, 0xECE45B3Du, 0x49286651u, 0x7C4B1FE6u, 0xAE9F2411u, 0x5A899FA5u, 0xEE386BFBu,
49 0xF406B7EDu, 0x0BFF5CB6u, 0xA637ED6Bu, 0xF44C42E9u, 0x625E7EC6u, 0xE485B576u, 0x6D51C245u, 0x4FE1356Du,
50 0xF25F1437u, 0x302B0A6Du, 0xCD3A431Bu, 0xEF9519B3u, 0x8E3404DDu, 0x514A0879u, 0x3B139B22u, 0x020BBEa6u,
51 0x8A67CC74u, 0x29024E08u, 0x80DC1CD1u, 0xC4C6628Bu, 0x2168C234u, 0xC90FDAA2u, 0xFFFFFFFFu, 0xFFFFFFFFu,
52}};
53
55 2u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
56 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
57 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
58}};
59
60// ---------------------------------------------------------------------------
61// Internal helpers
62// ---------------------------------------------------------------------------
63
64int bn_cmp_raw(const uint32_t *a, const uint32_t *b, int n)
65{
66 for (int i = n - 1; i >= 0; i--)
67 {
68 if (a[i] < b[i])
69 {
70 return -1;
71 }
72 if (a[i] > b[i])
73 {
74 return 1;
75 }
76 }
77 return 0;
78}
79
80// ---------------------------------------------------------------------------
81// Public API (bn_from_bytes / bn_to_bytes / bn_cmp / ... shared both platforms)
82// ---------------------------------------------------------------------------
83
84void bn_from_bytes(pc_bignum *out, const uint8_t *bytes, size_t len)
85{
86 memset(out->d, 0, sizeof(pc_bignum));
87 // bytes are big-endian; map to little-endian limbs.
88 size_t blen = len < 256 ? len : 256;
89 for (size_t i = 0; i < blen; i++)
90 {
91 size_t byte_pos = i; // byte i from LSB (bytes[len-1-i] is the i-th byte from the end)
92 out->d[byte_pos / 4] |= (uint32_t)bytes[len - 1 - i] << ((byte_pos % 4) * 8);
93 }
94}
95
96void bn_to_bytes(uint8_t bytes[256], const pc_bignum *in)
97{
98 for (int i = 0; i < PC_BN_LIMBS; i++)
99 {
100 uint32_t v = in->d[PC_BN_LIMBS - 1 - i];
101 bytes[i * 4 + 0] = (uint8_t)(v >> 24);
102 bytes[i * 4 + 1] = (uint8_t)(v >> 16);
103 bytes[i * 4 + 2] = (uint8_t)(v >> 8);
104 bytes[i * 4 + 3] = (uint8_t)(v);
105 }
106}
107
108int bn_cmp(const pc_bignum *a, const pc_bignum *b)
109{
110 return bn_cmp_raw(a->d, b->d, PC_BN_LIMBS);
111}
112
114{
115 for (int i = 0; i < PC_BN_LIMBS; i++)
116 {
117 if (a->d[i])
118 {
119 return 0;
120 }
121 }
122 return 1;
123}
124
126{
127 // Must be > 1
128 int ok = 0;
129 for (int i = 1; i < PC_BN_LIMBS; i++)
130 {
131 if (v->d[i])
132 {
133 ok = 1;
134 break;
135 }
136 }
137 if (!ok && v->d[0] <= 1u)
138 {
139 return -1;
140 }
141 // Must be < p-1
142 // p-1: subtract 1 from p
143 pc_bignum pm1 = group14_p;
144 pm1.d[0]--;
145 if (bn_cmp(v, &pm1) >= 0)
146 {
147 return -1;
148 }
149 return 0;
150}
151
152// ---------------------------------------------------------------------------
153// Modular exponentiation: out = base^exp mod group14_p
154// ---------------------------------------------------------------------------
PC_CRYPTO_HOT const pc_bignum group14_p
The RFC 3526 MODP group-14 prime (2048-bit).
Definition bignum.cpp:42
void bn_to_bytes(uint8_t bytes[256], const pc_bignum *in)
Write a pc_bignum as a 256-byte big-endian array.
Definition bignum.cpp:96
const pc_bignum group14_g
Generator for group-14: g = 2.
Definition bignum.cpp:54
int bn_cmp(const pc_bignum *a, const pc_bignum *b)
Compare two pc_bignum values.
Definition bignum.cpp:108
int bn_is_zero(const pc_bignum *a)
Return non-zero if a is zero (all limbs zero).
Definition bignum.cpp:113
void bn_from_bytes(pc_bignum *out, const uint8_t *bytes, size_t len)
Read a big-endian byte array of len bytes into a pc_bignum.
Definition bignum.cpp:84
int bn_dh_validate(const pc_bignum *v)
Validate a received DH public value.
Definition bignum.cpp:125
int bn_cmp_raw(const uint32_t *a, const uint32_t *b, int n)
Compute out = base^exp mod group14_p.
Definition bignum.cpp:64
2048-bit big-integer arithmetic for DH-group14 and RSA-2048.
#define PC_BN_LIMBS
Number of 32-bit limbs in a 2048-bit integer.
Definition bignum.h:83
Per-translation-unit optimization override for hot, pure-integer crypto.
Secure pool accessor - borrows that hold key material.
A 2048-bit unsigned integer stored as 64 little-endian 32-bit limbs.
Definition bignum.h:92
uint32_t d[PC_BN_LIMBS]
256 bytes of magnitude, little-endian limbs.
Definition bignum.h:93