qr_reader-native 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE-mruby.txt +22 -0
- data/LICENSE-spinel.txt +19 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/ext/qr_reader_native/SPINEL_VERSION +1 -0
- data/ext/qr_reader_native/extconf.rb +8 -0
- data/ext/qr_reader_native/mruby/error.h +13 -0
- data/ext/qr_reader_native/mruby/internal.h +145 -0
- data/ext/qr_reader_native/mruby/string.h +22 -0
- data/ext/qr_reader_native/mruby.h +93 -0
- data/ext/qr_reader_native/mruby_shim.h +118 -0
- data/ext/qr_reader_native/qr_reader_native.c +13513 -0
- data/ext/qr_reader_native/qr_reader_native.h +29 -0
- data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
- data/ext/qr_reader_native/re_cased.h +71 -0
- data/ext/qr_reader_native/re_compile.c +5784 -0
- data/ext/qr_reader_native/re_ctype.h +3540 -0
- data/ext/qr_reader_native/re_exec.c +2190 -0
- data/ext/qr_reader_native/re_internal.h +671 -0
- data/ext/qr_reader_native/re_prop.h +4629 -0
- data/ext/qr_reader_native/re_spinel.c +363 -0
- data/ext/qr_reader_native/re_spinel.h +22 -0
- data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
- data/ext/qr_reader_native/re_utf8.c +156 -0
- data/ext/qr_reader_native/sp_alloc.c +1447 -0
- data/ext/qr_reader_native/sp_alloc.h +1196 -0
- data/ext/qr_reader_native/sp_argf.h +33 -0
- data/ext/qr_reader_native/sp_array.c +1052 -0
- data/ext/qr_reader_native/sp_array.h +456 -0
- data/ext/qr_reader_native/sp_bigint.c +5826 -0
- data/ext/qr_reader_native/sp_bigint.h +117 -0
- data/ext/qr_reader_native/sp_cold.c +5259 -0
- data/ext/qr_reader_native/sp_compat.h +402 -0
- data/ext/qr_reader_native/sp_core.c +818 -0
- data/ext/qr_reader_native/sp_core.h +56 -0
- data/ext/qr_reader_native/sp_crypto.c +877 -0
- data/ext/qr_reader_native/sp_crypto.h +169 -0
- data/ext/qr_reader_native/sp_dtoa.c +1533 -0
- data/ext/qr_reader_native/sp_dtoa.h +29 -0
- data/ext/qr_reader_native/sp_encoding_names.h +172 -0
- data/ext/qr_reader_native/sp_enum.h +86 -0
- data/ext/qr_reader_native/sp_exc.c +1185 -0
- data/ext/qr_reader_native/sp_exc.h +186 -0
- data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
- data/ext/qr_reader_native/sp_fiber.c +889 -0
- data/ext/qr_reader_native/sp_fiber.h +162 -0
- data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
- data/ext/qr_reader_native/sp_format.c +695 -0
- data/ext/qr_reader_native/sp_format.h +83 -0
- data/ext/qr_reader_native/sp_gc.c +1793 -0
- data/ext/qr_reader_native/sp_gc.h +762 -0
- data/ext/qr_reader_native/sp_hash.c +131 -0
- data/ext/qr_reader_native/sp_hash.h +131 -0
- data/ext/qr_reader_native/sp_inspect.c +215 -0
- data/ext/qr_reader_native/sp_inspect.h +128 -0
- data/ext/qr_reader_native/sp_io.c +1676 -0
- data/ext/qr_reader_native/sp_io.h +272 -0
- data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
- data/ext/qr_reader_native/sp_iobuffer.h +167 -0
- data/ext/qr_reader_native/sp_marshal.c +487 -0
- data/ext/qr_reader_native/sp_marshal.h +60 -0
- data/ext/qr_reader_native/sp_net.c +1029 -0
- data/ext/qr_reader_native/sp_net.h +205 -0
- data/ext/qr_reader_native/sp_pack.c +1254 -0
- data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
- data/ext/qr_reader_native/sp_proc.c +410 -0
- data/ext/qr_reader_native/sp_proc.h +327 -0
- data/ext/qr_reader_native/sp_process.c +547 -0
- data/ext/qr_reader_native/sp_process_status.c +211 -0
- data/ext/qr_reader_native/sp_process_status.h +66 -0
- data/ext/qr_reader_native/sp_random.c +287 -0
- data/ext/qr_reader_native/sp_random.h +52 -0
- data/ext/qr_reader_native/sp_range.h +144 -0
- data/ext/qr_reader_native/sp_re.c +1379 -0
- data/ext/qr_reader_native/sp_re.h +186 -0
- data/ext/qr_reader_native/sp_re_names.h +59 -0
- data/ext/qr_reader_native/sp_sched.c +4044 -0
- data/ext/qr_reader_native/sp_sched.h +295 -0
- data/ext/qr_reader_native/sp_slab.c +1507 -0
- data/ext/qr_reader_native/sp_str.c +1462 -0
- data/ext/qr_reader_native/sp_str.h +306 -0
- data/ext/qr_reader_native/sp_str_crypt.c +23 -0
- data/ext/qr_reader_native/sp_string.c +92 -0
- data/ext/qr_reader_native/sp_string.h +253 -0
- data/ext/qr_reader_native/sp_system.c +78 -0
- data/ext/qr_reader_native/sp_system.h +18 -0
- data/ext/qr_reader_native/sp_time.c +860 -0
- data/ext/qr_reader_native/sp_time.h +100 -0
- data/ext/qr_reader_native/sp_types.h +292 -0
- data/ext/qr_reader_native/spinel_rt.h +19017 -0
- data/ext/qr_reader_native/unicase.c +399 -0
- data/ext/qr_reader_native/unicase.h +901 -0
- data/lib/qr_reader/native.rb +6 -0
- metadata +153 -0
|
@@ -0,0 +1,877 @@
|
|
|
1
|
+
/* sp_crypto.c -- SHA-256 / HMAC-SHA256 / PBKDF2 / Base64URL / CSPRNG.
|
|
2
|
+
*
|
|
3
|
+
* See sp_crypto.h for the public surface. Pure C, no spinel-runtime
|
|
4
|
+
* dependency -- archived into libspinel_rt.a alongside sp_bigint.o
|
|
5
|
+
* so spinel programs can ffi_func against it (or, longer term, have
|
|
6
|
+
* codegen pick up `Digest::SHA256.hexdigest`-shaped Ruby calls and
|
|
7
|
+
* lower them to these primitives directly).
|
|
8
|
+
*
|
|
9
|
+
* Implementations
|
|
10
|
+
* ---------------
|
|
11
|
+
* - SHA-256: compact public-domain implementation; the canonical
|
|
12
|
+
* FIPS-180-4 reference (Wikipedia pseudocode + Go's crypto/sha256
|
|
13
|
+
* match this byte-for-byte on the standard test vectors).
|
|
14
|
+
* - HMAC-SHA256: RFC 2104 / RFC 4231 on top of the SHA-256 above.
|
|
15
|
+
* - PBKDF2-HMAC-SHA256: RFC 8018, dkLen fixed at 32 (one block).
|
|
16
|
+
* - Base64URL: RFC 4648 §5 (URL/filename-safe alphabet), no padding.
|
|
17
|
+
* - CSPRNG: arc4random_buf on BSD/macOS; getrandom(2) then
|
|
18
|
+
* /dev/urandom on Linux, failing closed (NULL) with no weak fallback.
|
|
19
|
+
*
|
|
20
|
+
* State buffers
|
|
21
|
+
* -------------
|
|
22
|
+
* Each public function owns a per-function SP_TLS return buffer.
|
|
23
|
+
* The next call to the SAME function on the SAME thread clobbers it.
|
|
24
|
+
* Distinct functions use distinct buffers, so chained calls
|
|
25
|
+
* (b64url_encode then hex) don't trample each other, and distinct
|
|
26
|
+
* threads never share one -- the call site copies out of the buffer
|
|
27
|
+
* after the call has returned, and that window is long enough for
|
|
28
|
+
* another thread to enter the same function and overwrite it.
|
|
29
|
+
*/
|
|
30
|
+
#include "sp_crypto.h"
|
|
31
|
+
#include "sp_alloc.h" /* sp_str_byte_len: hash/HMAC/PBKDF2 inputs are spinel
|
|
32
|
+
Strings that can carry embedded NULs (binary keys,
|
|
33
|
+
salts, digests), so use the header byte length rather
|
|
34
|
+
than strlen, which truncates at the first NUL (#1779). */
|
|
35
|
+
|
|
36
|
+
#include <stdio.h>
|
|
37
|
+
#include <stdlib.h>
|
|
38
|
+
#include <string.h>
|
|
39
|
+
#include <stdint.h>
|
|
40
|
+
#include <time.h>
|
|
41
|
+
#if defined(__linux__)
|
|
42
|
+
#include <unistd.h> /* syscall */
|
|
43
|
+
#include <sys/syscall.h> /* SYS_getrandom (the CSPRNG's first choice) */
|
|
44
|
+
#endif
|
|
45
|
+
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
|
46
|
+
# include <stdlib.h> /* arc4random_buf */
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
/* ---------- SHA-256 ----------
|
|
50
|
+
* FIPS-180-4. Compact reference implementation -- public domain.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
static const uint32_t sp_crypto_sha256_k[64] = {
|
|
54
|
+
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
|
|
55
|
+
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
|
|
56
|
+
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
|
|
57
|
+
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
|
|
58
|
+
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
|
|
59
|
+
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
|
|
60
|
+
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
|
|
61
|
+
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
#define SPC_ROTR(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
|
|
65
|
+
#define SPC_S0(x) (SPC_ROTR(x, 2) ^ SPC_ROTR(x,13) ^ SPC_ROTR(x,22))
|
|
66
|
+
#define SPC_S1(x) (SPC_ROTR(x, 6) ^ SPC_ROTR(x,11) ^ SPC_ROTR(x,25))
|
|
67
|
+
#define SPC_s0(x) (SPC_ROTR(x, 7) ^ SPC_ROTR(x,18) ^ ((x) >> 3))
|
|
68
|
+
#define SPC_s1(x) (SPC_ROTR(x,17) ^ SPC_ROTR(x,19) ^ ((x) >> 10))
|
|
69
|
+
#define SPC_CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
|
|
70
|
+
#define SPC_MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
|
71
|
+
|
|
72
|
+
static void sp_crypto_sha256_block(uint32_t H[8], const uint8_t b[64]) {
|
|
73
|
+
uint32_t w[64], sa, sb, sc, sd, se, sf, sg, sh, t1, t2;
|
|
74
|
+
int i;
|
|
75
|
+
for (i = 0; i < 16; i++) {
|
|
76
|
+
w[i] = ((uint32_t)b[i*4] << 24) | ((uint32_t)b[i*4+1] << 16) |
|
|
77
|
+
((uint32_t)b[i*4+2] << 8) | (uint32_t)b[i*4+3];
|
|
78
|
+
}
|
|
79
|
+
for (i = 16; i < 64; i++) {
|
|
80
|
+
w[i] = SPC_s1(w[i-2]) + w[i-7] + SPC_s0(w[i-15]) + w[i-16];
|
|
81
|
+
}
|
|
82
|
+
sa=H[0]; sb=H[1]; sc=H[2]; sd=H[3]; se=H[4]; sf=H[5]; sg=H[6]; sh=H[7];
|
|
83
|
+
for (i = 0; i < 64; i++) {
|
|
84
|
+
t1 = sh + SPC_S1(se) + SPC_CH(se,sf,sg) + sp_crypto_sha256_k[i] + w[i];
|
|
85
|
+
t2 = SPC_S0(sa) + SPC_MAJ(sa,sb,sc);
|
|
86
|
+
sh = sg; sg = sf; sf = se; se = sd + t1;
|
|
87
|
+
sd = sc; sc = sb; sb = sa; sa = t1 + t2;
|
|
88
|
+
}
|
|
89
|
+
H[0]+=sa; H[1]+=sb; H[2]+=sc; H[3]+=sd;
|
|
90
|
+
H[4]+=se; H[5]+=sf; H[6]+=sg; H[7]+=sh;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static void sp_crypto_store_be32(uint8_t *out, const uint32_t *H, int n) {
|
|
94
|
+
for (int i = 0; i < n; i++) {
|
|
95
|
+
out[i*4] = (uint8_t)(H[i] >> 24);
|
|
96
|
+
out[i*4+1] = (uint8_t)(H[i] >> 16);
|
|
97
|
+
out[i*4+2] = (uint8_t)(H[i] >> 8);
|
|
98
|
+
out[i*4+3] = (uint8_t)(H[i]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static void sp_crypto_sha256(const uint8_t *msg, size_t len, uint8_t out[32]) {
|
|
103
|
+
uint32_t H[8] = {
|
|
104
|
+
0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,
|
|
105
|
+
0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19
|
|
106
|
+
};
|
|
107
|
+
uint8_t buf[64];
|
|
108
|
+
size_t i, full = len & ~((size_t)63);
|
|
109
|
+
for (i = 0; i < full; i += 64) sp_crypto_sha256_block(H, msg + i);
|
|
110
|
+
size_t rem = len - full;
|
|
111
|
+
for (i = 0; i < rem; i++) buf[i] = msg[full + i];
|
|
112
|
+
buf[rem] = 0x80;
|
|
113
|
+
if (rem >= 56) {
|
|
114
|
+
for (i = rem + 1; i < 64; i++) buf[i] = 0;
|
|
115
|
+
sp_crypto_sha256_block(H, buf);
|
|
116
|
+
for (i = 0; i < 56; i++) buf[i] = 0;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
for (i = rem + 1; i < 56; i++) buf[i] = 0;
|
|
120
|
+
}
|
|
121
|
+
uint64_t bits = (uint64_t)len * 8;
|
|
122
|
+
for (i = 0; i < 8; i++) buf[56 + i] = (uint8_t)(bits >> (56 - 8*i));
|
|
123
|
+
sp_crypto_sha256_block(H, buf);
|
|
124
|
+
sp_crypto_store_be32(out, H, 8);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ---------- SHA-1 ----------
|
|
128
|
+
* FIPS-180-4 (legacy hash, kept for WebSocket handshake -- RFC 6455
|
|
129
|
+
* requires SHA-1 specifically for Sec-WebSocket-Accept). Do not use
|
|
130
|
+
* for new security designs; SHA-256 is the right primitive for those.
|
|
131
|
+
* Compact reference implementation -- public domain.
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
static void sp_crypto_sha1_block(uint32_t H[5], const uint8_t b[64]) {
|
|
135
|
+
uint32_t w[80], a, sa, sb, sc, sd, se, f, k;
|
|
136
|
+
int i;
|
|
137
|
+
for (i = 0; i < 16; i++) {
|
|
138
|
+
w[i] = ((uint32_t)b[i*4] << 24) | ((uint32_t)b[i*4+1] << 16) |
|
|
139
|
+
((uint32_t)b[i*4+2] << 8) | (uint32_t)b[i*4+3];
|
|
140
|
+
}
|
|
141
|
+
for (i = 16; i < 80; i++) {
|
|
142
|
+
a = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16];
|
|
143
|
+
w[i] = (a << 1) | (a >> 31);
|
|
144
|
+
}
|
|
145
|
+
sa = H[0]; sb = H[1]; sc = H[2]; sd = H[3]; se = H[4];
|
|
146
|
+
for (i = 0; i < 80; i++) {
|
|
147
|
+
if (i < 20) { f = (sb & sc) | (~sb & sd); k = 0x5A827999; }
|
|
148
|
+
else if (i < 40) { f = sb ^ sc ^ sd; k = 0x6ED9EBA1; }
|
|
149
|
+
else if (i < 60) { f = (sb & sc) | (sb & sd) | (sc & sd); k = 0x8F1BBCDC; }
|
|
150
|
+
else { f = sb ^ sc ^ sd; k = 0xCA62C1D6; }
|
|
151
|
+
uint32_t t = ((sa << 5) | (sa >> 27)) + f + se + k + w[i];
|
|
152
|
+
se = sd; sd = sc; sc = (sb << 30) | (sb >> 2); sb = sa; sa = t;
|
|
153
|
+
}
|
|
154
|
+
H[0] += sa; H[1] += sb; H[2] += sc; H[3] += sd; H[4] += se;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static void sp_crypto_sha1(const uint8_t *msg, size_t len, uint8_t out[20]) {
|
|
158
|
+
uint32_t H[5] = {
|
|
159
|
+
0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
|
|
160
|
+
};
|
|
161
|
+
uint8_t buf[64];
|
|
162
|
+
size_t i, full = len & ~((size_t)63);
|
|
163
|
+
for (i = 0; i < full; i += 64) sp_crypto_sha1_block(H, msg + i);
|
|
164
|
+
size_t rem = len - full;
|
|
165
|
+
for (i = 0; i < rem; i++) buf[i] = msg[full + i];
|
|
166
|
+
buf[rem] = 0x80;
|
|
167
|
+
if (rem >= 56) {
|
|
168
|
+
for (i = rem + 1; i < 64; i++) buf[i] = 0;
|
|
169
|
+
sp_crypto_sha1_block(H, buf);
|
|
170
|
+
for (i = 0; i < 56; i++) buf[i] = 0;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
for (i = rem + 1; i < 56; i++) buf[i] = 0;
|
|
174
|
+
}
|
|
175
|
+
uint64_t bits = (uint64_t)len * 8;
|
|
176
|
+
for (i = 0; i < 8; i++) buf[56 + i] = (uint8_t)(bits >> (56 - 8*i));
|
|
177
|
+
sp_crypto_sha1_block(H, buf);
|
|
178
|
+
sp_crypto_store_be32(out, H, 5);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static SP_TLS char sp_crypto_sha256_hex_buf[65];
|
|
182
|
+
|
|
183
|
+
/* SHA-256(msg) -> 64-char lowercase hex. The `digest` spin package binds
|
|
184
|
+
Digest::SHA256.hexdigest to this. Same static-buffer contract as the
|
|
185
|
+
sibling helpers. */
|
|
186
|
+
const char *sp_crypto_sha256_hex(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
187
|
+
uint8_t out[32];
|
|
188
|
+
sp_crypto_sha256((const uint8_t *)msg, sp_str_byte_len(msg), out);
|
|
189
|
+
static const char H[] = "0123456789abcdef";
|
|
190
|
+
int i;
|
|
191
|
+
for (i = 0; i < 32; i++) {
|
|
192
|
+
sp_crypto_sha256_hex_buf[i*2] = H[(out[i] >> 4) & 0xf];
|
|
193
|
+
sp_crypto_sha256_hex_buf[i*2+1] = H[out[i] & 0xf];
|
|
194
|
+
}
|
|
195
|
+
sp_crypto_sha256_hex_buf[64] = '\0';
|
|
196
|
+
return sp_crypto_sha256_hex_buf;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* SHA-256(msg) -> the raw 32 digest bytes. Digest::SHA256.digest binds here
|
|
200
|
+
through the native binding's `:cbinstr` return mode, which reads the exact
|
|
201
|
+
length from sp_ffi_bin_len rather than calling strlen -- a binary digest
|
|
202
|
+
contains NUL bytes roughly one time in eight. Same static-buffer contract as
|
|
203
|
+
the hex siblings. */
|
|
204
|
+
static SP_TLS char sp_crypto_sha256_bin_buf[32];
|
|
205
|
+
|
|
206
|
+
const char *sp_crypto_sha256_bin(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
207
|
+
sp_crypto_sha256((const uint8_t *)msg, sp_str_byte_len(msg),
|
|
208
|
+
(uint8_t *)sp_crypto_sha256_bin_buf);
|
|
209
|
+
sp_ffi_bin_len = 32;
|
|
210
|
+
return sp_crypto_sha256_bin_buf;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
static SP_TLS char sp_crypto_sha1_hex_buf[41];
|
|
214
|
+
|
|
215
|
+
const char *sp_crypto_sha1_hex(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
216
|
+
uint8_t out[20];
|
|
217
|
+
sp_crypto_sha1((const uint8_t *)msg, sp_str_byte_len(msg), out);
|
|
218
|
+
static const char H[] = "0123456789abcdef";
|
|
219
|
+
int i;
|
|
220
|
+
for (i = 0; i < 20; i++) {
|
|
221
|
+
sp_crypto_sha1_hex_buf[i*2] = H[(out[i] >> 4) & 0xf];
|
|
222
|
+
sp_crypto_sha1_hex_buf[i*2+1] = H[out[i] & 0xf];
|
|
223
|
+
}
|
|
224
|
+
sp_crypto_sha1_hex_buf[40] = '\0';
|
|
225
|
+
return sp_crypto_sha1_hex_buf;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* SHA-1(msg) -> the raw 20 digest bytes; see sp_crypto_sha256_bin. */
|
|
229
|
+
static SP_TLS char sp_crypto_sha1_bin_buf[20];
|
|
230
|
+
|
|
231
|
+
const char *sp_crypto_sha1_bin(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
232
|
+
sp_crypto_sha1((const uint8_t *)msg, sp_str_byte_len(msg),
|
|
233
|
+
(uint8_t *)sp_crypto_sha1_bin_buf);
|
|
234
|
+
sp_ffi_bin_len = 20;
|
|
235
|
+
return sp_crypto_sha1_bin_buf;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* ---------- MD5 (RFC 1321) ----------
|
|
239
|
+
* Legacy hash, bound for Digest::MD5: Active Storage's direct-upload
|
|
240
|
+
* protocol checks a blob by the base64 MD5 of its bytes (#4631). Do not use
|
|
241
|
+
* for new security designs. Compact reference implementation, public domain. */
|
|
242
|
+
|
|
243
|
+
static const uint32_t sp_crypto_md5_k[64] = {
|
|
244
|
+
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
|
245
|
+
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
|
246
|
+
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
|
247
|
+
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
|
248
|
+
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
|
249
|
+
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
|
250
|
+
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
|
251
|
+
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
|
252
|
+
};
|
|
253
|
+
static const uint8_t sp_crypto_md5_r[64] = {
|
|
254
|
+
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
|
255
|
+
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
|
256
|
+
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
|
257
|
+
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
static void sp_crypto_md5_block(uint32_t H[4], const uint8_t b[64]) {
|
|
261
|
+
uint32_t w[16], a, bb, c, d, f, t;
|
|
262
|
+
int i, g;
|
|
263
|
+
for (i = 0; i < 16; i++) {
|
|
264
|
+
w[i] = (uint32_t)b[i*4] | ((uint32_t)b[i*4+1] << 8) |
|
|
265
|
+
((uint32_t)b[i*4+2] << 16) | ((uint32_t)b[i*4+3] << 24);
|
|
266
|
+
}
|
|
267
|
+
a = H[0]; bb = H[1]; c = H[2]; d = H[3];
|
|
268
|
+
for (i = 0; i < 64; i++) {
|
|
269
|
+
if (i < 16) { f = (bb & c) | (~bb & d); g = i; }
|
|
270
|
+
else if (i < 32) { f = (d & bb) | (~d & c); g = (5*i + 1) & 15; }
|
|
271
|
+
else if (i < 48) { f = bb ^ c ^ d; g = (3*i + 5) & 15; }
|
|
272
|
+
else { f = c ^ (bb | ~d); g = (7*i) & 15; }
|
|
273
|
+
t = d; d = c; c = bb;
|
|
274
|
+
uint32_t x = a + f + sp_crypto_md5_k[i] + w[g];
|
|
275
|
+
bb = bb + ((x << sp_crypto_md5_r[i]) | (x >> (32 - sp_crypto_md5_r[i])));
|
|
276
|
+
a = t;
|
|
277
|
+
}
|
|
278
|
+
H[0] += a; H[1] += bb; H[2] += c; H[3] += d;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
static void sp_crypto_md5(const uint8_t *msg, size_t len, uint8_t out[16]) {
|
|
282
|
+
uint32_t H[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
|
|
283
|
+
uint8_t buf[64];
|
|
284
|
+
size_t i, full = len & ~((size_t)63);
|
|
285
|
+
for (i = 0; i < full; i += 64) sp_crypto_md5_block(H, msg + i);
|
|
286
|
+
size_t rem = len - full;
|
|
287
|
+
for (i = 0; i < rem; i++) buf[i] = msg[full + i];
|
|
288
|
+
buf[rem] = 0x80;
|
|
289
|
+
if (rem >= 56) {
|
|
290
|
+
for (i = rem + 1; i < 64; i++) buf[i] = 0;
|
|
291
|
+
sp_crypto_md5_block(H, buf);
|
|
292
|
+
for (i = 0; i < 56; i++) buf[i] = 0;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
for (i = rem + 1; i < 56; i++) buf[i] = 0;
|
|
296
|
+
}
|
|
297
|
+
uint64_t bits = (uint64_t)len * 8; /* little-endian length, unlike SHA */
|
|
298
|
+
for (i = 0; i < 8; i++) buf[56 + i] = (uint8_t)(bits >> (8*i));
|
|
299
|
+
sp_crypto_md5_block(H, buf);
|
|
300
|
+
for (i = 0; i < 4; i++) {
|
|
301
|
+
out[i*4] = (uint8_t)(H[i]);
|
|
302
|
+
out[i*4+1] = (uint8_t)(H[i] >> 8);
|
|
303
|
+
out[i*4+2] = (uint8_t)(H[i] >> 16);
|
|
304
|
+
out[i*4+3] = (uint8_t)(H[i] >> 24);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
static SP_TLS char sp_crypto_md5_hex_buf[33];
|
|
309
|
+
|
|
310
|
+
const char *sp_crypto_md5_hex(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
311
|
+
uint8_t out[16];
|
|
312
|
+
sp_crypto_md5((const uint8_t *)msg, sp_str_byte_len(msg), out);
|
|
313
|
+
static const char H[] = "0123456789abcdef";
|
|
314
|
+
int i;
|
|
315
|
+
for (i = 0; i < 16; i++) {
|
|
316
|
+
sp_crypto_md5_hex_buf[i*2] = H[(out[i] >> 4) & 0xf];
|
|
317
|
+
sp_crypto_md5_hex_buf[i*2+1] = H[out[i] & 0xf];
|
|
318
|
+
}
|
|
319
|
+
sp_crypto_md5_hex_buf[32] = '\0';
|
|
320
|
+
return sp_crypto_md5_hex_buf;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* MD5(msg) -> the raw 16 digest bytes; see sp_crypto_sha256_bin. */
|
|
324
|
+
static SP_TLS char sp_crypto_md5_bin_buf[16];
|
|
325
|
+
|
|
326
|
+
const char *sp_crypto_md5_bin(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
327
|
+
sp_crypto_md5((const uint8_t *)msg, sp_str_byte_len(msg),
|
|
328
|
+
(uint8_t *)sp_crypto_md5_bin_buf);
|
|
329
|
+
sp_ffi_bin_len = 16;
|
|
330
|
+
return sp_crypto_md5_bin_buf;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* RFC 6455 §1.3 Sec-WebSocket-Accept:
|
|
334
|
+
* base64(SHA-1(client_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
|
|
335
|
+
* Standard Base64 (RFC 4648 §4, `+/` alphabet, `=` padding) -- NOT
|
|
336
|
+
* the URL-safe variant. 20 bytes of SHA-1 -> 28-char output ("xxx=").
|
|
337
|
+
* The GUID is the WebSocket protocol's fixed magic string.
|
|
338
|
+
*/
|
|
339
|
+
static const char SPC_B64[64] =
|
|
340
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
341
|
+
|
|
342
|
+
static SP_TLS char sp_crypto_websocket_accept_buf[29];
|
|
343
|
+
static void sp_crypto_b64_strict(const uint8_t *d, size_t n, char *out);
|
|
344
|
+
|
|
345
|
+
const char *sp_crypto_websocket_accept(const char *client_key) {
|
|
346
|
+
/* client_key is the 24-char base64 from the request header;
|
|
347
|
+
* GUID is 36 chars. Total <= 60; cap input at 128 for safety. */
|
|
348
|
+
char in[128 + 36 + 1];
|
|
349
|
+
size_t klen = strlen(client_key);
|
|
350
|
+
if (klen > 128) klen = 128;
|
|
351
|
+
size_t i;
|
|
352
|
+
for (i = 0; i < klen; i++) in[i] = client_key[i];
|
|
353
|
+
static const char guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
354
|
+
for (i = 0; i < 36; i++) in[klen + i] = guid[i];
|
|
355
|
+
uint8_t digest[20];
|
|
356
|
+
sp_crypto_sha1((const uint8_t *)in, klen + 36, digest);
|
|
357
|
+
/* base64(20 bytes) = 28 chars: 6 full triplets + 2 leftover bytes
|
|
358
|
+
* -> 3 chars + 1 padding `=`. */
|
|
359
|
+
sp_crypto_b64_strict(digest, 20, sp_crypto_websocket_accept_buf);
|
|
360
|
+
return sp_crypto_websocket_accept_buf;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/* Digest::X.base64digest: strict base64 (RFC 4648 §4, `=` padded) of the
|
|
364
|
+
raw digest, which Ruby's Digest::Class defines for every digest. One
|
|
365
|
+
buffer per digest so the three can be held at once. */
|
|
366
|
+
static void sp_crypto_b64_strict(const uint8_t *d, size_t n, char *out) {
|
|
367
|
+
size_t i; int j = 0;
|
|
368
|
+
for (i = 0; i + 3 <= n; i += 3) {
|
|
369
|
+
uint32_t v = ((uint32_t)d[i] << 16) | ((uint32_t)d[i+1] << 8) | (uint32_t)d[i+2];
|
|
370
|
+
out[j++] = SPC_B64[(v >> 18) & 0x3f];
|
|
371
|
+
out[j++] = SPC_B64[(v >> 12) & 0x3f];
|
|
372
|
+
out[j++] = SPC_B64[(v >> 6) & 0x3f];
|
|
373
|
+
out[j++] = SPC_B64[ v & 0x3f];
|
|
374
|
+
}
|
|
375
|
+
if (n - i == 2) {
|
|
376
|
+
uint32_t v = ((uint32_t)d[i] << 16) | ((uint32_t)d[i+1] << 8);
|
|
377
|
+
out[j++] = SPC_B64[(v >> 18) & 0x3f];
|
|
378
|
+
out[j++] = SPC_B64[(v >> 12) & 0x3f];
|
|
379
|
+
out[j++] = SPC_B64[(v >> 6) & 0x3f];
|
|
380
|
+
out[j++] = '=';
|
|
381
|
+
}
|
|
382
|
+
else if (n - i == 1) {
|
|
383
|
+
uint32_t v = ((uint32_t)d[i] << 16);
|
|
384
|
+
out[j++] = SPC_B64[(v >> 18) & 0x3f];
|
|
385
|
+
out[j++] = SPC_B64[(v >> 12) & 0x3f];
|
|
386
|
+
out[j++] = '=';
|
|
387
|
+
out[j++] = '=';
|
|
388
|
+
}
|
|
389
|
+
out[j] = '\0';
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
static SP_TLS char sp_crypto_md5_b64_buf[25];
|
|
393
|
+
static SP_TLS char sp_crypto_sha1_b64_buf[29];
|
|
394
|
+
static SP_TLS char sp_crypto_sha256_b64_buf[45];
|
|
395
|
+
|
|
396
|
+
const char *sp_crypto_md5_b64(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
397
|
+
uint8_t out[16];
|
|
398
|
+
sp_crypto_md5((const uint8_t *)msg, sp_str_byte_len(msg), out);
|
|
399
|
+
sp_crypto_b64_strict(out, 16, sp_crypto_md5_b64_buf);
|
|
400
|
+
return sp_crypto_md5_b64_buf;
|
|
401
|
+
}
|
|
402
|
+
const char *sp_crypto_sha1_b64(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
403
|
+
uint8_t out[20];
|
|
404
|
+
sp_crypto_sha1((const uint8_t *)msg, sp_str_byte_len(msg), out);
|
|
405
|
+
sp_crypto_b64_strict(out, 20, sp_crypto_sha1_b64_buf);
|
|
406
|
+
return sp_crypto_sha1_b64_buf;
|
|
407
|
+
}
|
|
408
|
+
const char *sp_crypto_sha256_b64(const char *msg) {SP_GC_ROOT_STR(msg);
|
|
409
|
+
uint8_t out[32];
|
|
410
|
+
sp_crypto_sha256((const uint8_t *)msg, sp_str_byte_len(msg), out);
|
|
411
|
+
sp_crypto_b64_strict(out, 32, sp_crypto_sha256_b64_buf);
|
|
412
|
+
return sp_crypto_sha256_b64_buf;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* ---------- HMAC-SHA256 (RFC 2104) ---------- */
|
|
416
|
+
|
|
417
|
+
static void sp_crypto_hmac_sha256(const uint8_t *key, size_t klen,
|
|
418
|
+
const uint8_t *msg, size_t mlen,
|
|
419
|
+
uint8_t out[32]) {
|
|
420
|
+
uint8_t kpad[64], ipad[64], opad[64], inner[32];
|
|
421
|
+
size_t i;
|
|
422
|
+
if (klen > 64) {
|
|
423
|
+
sp_crypto_sha256(key, klen, kpad);
|
|
424
|
+
for (i = 32; i < 64; i++) kpad[i] = 0;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
for (i = 0; i < klen; i++) kpad[i] = key[i];
|
|
428
|
+
for (i = klen; i < 64; i++) kpad[i] = 0;
|
|
429
|
+
}
|
|
430
|
+
for (i = 0; i < 64; i++) {
|
|
431
|
+
ipad[i] = kpad[i] ^ 0x36;
|
|
432
|
+
opad[i] = kpad[i] ^ 0x5c;
|
|
433
|
+
}
|
|
434
|
+
/* inner = SHA256(ipad || msg). Stream both segments through the
|
|
435
|
+
* compression to avoid allocating ipad||msg. */
|
|
436
|
+
{
|
|
437
|
+
uint32_t H[8] = {
|
|
438
|
+
0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,
|
|
439
|
+
0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19
|
|
440
|
+
};
|
|
441
|
+
sp_crypto_sha256_block(H, ipad);
|
|
442
|
+
uint8_t buf[64];
|
|
443
|
+
size_t full = mlen & ~((size_t)63);
|
|
444
|
+
for (i = 0; i < full; i += 64) sp_crypto_sha256_block(H, msg + i);
|
|
445
|
+
size_t rem = mlen - full;
|
|
446
|
+
for (i = 0; i < rem; i++) buf[i] = msg[full + i];
|
|
447
|
+
buf[rem] = 0x80;
|
|
448
|
+
if (rem >= 56) {
|
|
449
|
+
for (i = rem + 1; i < 64; i++) buf[i] = 0;
|
|
450
|
+
sp_crypto_sha256_block(H, buf);
|
|
451
|
+
for (i = 0; i < 56; i++) buf[i] = 0;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
for (i = rem + 1; i < 56; i++) buf[i] = 0;
|
|
455
|
+
}
|
|
456
|
+
uint64_t bits = (uint64_t)(64 + mlen) * 8;
|
|
457
|
+
for (i = 0; i < 8; i++) buf[56 + i] = (uint8_t)(bits >> (56 - 8*i));
|
|
458
|
+
sp_crypto_sha256_block(H, buf);
|
|
459
|
+
for (i = 0; i < 8; i++) {
|
|
460
|
+
inner[i*4] = (uint8_t)(H[i] >> 24);
|
|
461
|
+
inner[i*4+1] = (uint8_t)(H[i] >> 16);
|
|
462
|
+
inner[i*4+2] = (uint8_t)(H[i] >> 8);
|
|
463
|
+
inner[i*4+3] = (uint8_t)(H[i]);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
/* outer = SHA256(opad || inner) */
|
|
467
|
+
{
|
|
468
|
+
uint32_t H[8] = {
|
|
469
|
+
0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,
|
|
470
|
+
0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19
|
|
471
|
+
};
|
|
472
|
+
sp_crypto_sha256_block(H, opad);
|
|
473
|
+
uint8_t buf[64];
|
|
474
|
+
for (i = 0; i < 32; i++) buf[i] = inner[i];
|
|
475
|
+
buf[32] = 0x80;
|
|
476
|
+
for (i = 33; i < 56; i++) buf[i] = 0;
|
|
477
|
+
uint64_t bits = (uint64_t)(64 + 32) * 8;
|
|
478
|
+
for (i = 0; i < 8; i++) buf[56 + i] = (uint8_t)(bits >> (56 - 8*i));
|
|
479
|
+
sp_crypto_sha256_block(H, buf);
|
|
480
|
+
sp_crypto_store_be32(out, H, 8);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
static SP_TLS char sp_crypto_hmac_hex_buf[65];
|
|
485
|
+
|
|
486
|
+
const char *sp_crypto_hmac_sha256_hex(const char *key, const char *msg) {SP_GC_ROOT_STR(key);SP_GC_ROOT_STR(msg);
|
|
487
|
+
uint8_t out[32];
|
|
488
|
+
sp_crypto_hmac_sha256((const uint8_t *)key, sp_str_byte_len(key),
|
|
489
|
+
(const uint8_t *)msg, sp_str_byte_len(msg),
|
|
490
|
+
out);
|
|
491
|
+
static const char H[] = "0123456789abcdef";
|
|
492
|
+
int i;
|
|
493
|
+
for (i = 0; i < 32; i++) {
|
|
494
|
+
sp_crypto_hmac_hex_buf[i*2] = H[(out[i] >> 4) & 0xf];
|
|
495
|
+
sp_crypto_hmac_hex_buf[i*2+1] = H[out[i] & 0xf];
|
|
496
|
+
}
|
|
497
|
+
sp_crypto_hmac_hex_buf[64] = '\0';
|
|
498
|
+
return sp_crypto_hmac_hex_buf;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* The same MAC as its own bytes rather than as hex. Its own buffer, per
|
|
502
|
+
* the file header's rule: a caller that hexes one MAC and takes the raw
|
|
503
|
+
* bytes of another must not have the second clobber the first. */
|
|
504
|
+
static SP_TLS char sp_crypto_hmac_sha256_bin_buf[32];
|
|
505
|
+
|
|
506
|
+
const char *sp_crypto_hmac_sha256_bin(const char *key, const char *msg) {SP_GC_ROOT_STR(key);SP_GC_ROOT_STR(msg);
|
|
507
|
+
sp_crypto_hmac_sha256((const uint8_t *)key, sp_str_byte_len(key),
|
|
508
|
+
(const uint8_t *)msg, sp_str_byte_len(msg),
|
|
509
|
+
(uint8_t *)sp_crypto_hmac_sha256_bin_buf);
|
|
510
|
+
sp_ffi_bin_len = 32;
|
|
511
|
+
return sp_crypto_hmac_sha256_bin_buf;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/* HMAC-SHA1 (RFC 2104). SHA-1 is here for the same reason
|
|
515
|
+
* sp_crypto_websocket_accept is: an existing protocol names it, and
|
|
516
|
+
* reproducing that protocol is not a new security design. Rails signs
|
|
517
|
+
* its cookies with HMAC-SHA1 unless the app overrides
|
|
518
|
+
* config.action_dispatch.cookies_digest, so a spinel program that reads
|
|
519
|
+
* one has no choice of digest. Same streaming structure as the SHA-256
|
|
520
|
+
* sibling above -- ipad/opad are compressed straight in rather than
|
|
521
|
+
* concatenated with the message. */
|
|
522
|
+
static void sp_crypto_hmac_sha1(const uint8_t *key, size_t klen,
|
|
523
|
+
const uint8_t *msg, size_t mlen,
|
|
524
|
+
uint8_t out[20]) {
|
|
525
|
+
uint8_t kpad[64], ipad[64], opad[64], inner[20];
|
|
526
|
+
size_t i;
|
|
527
|
+
if (klen > 64) {
|
|
528
|
+
sp_crypto_sha1(key, klen, kpad);
|
|
529
|
+
for (i = 20; i < 64; i++) kpad[i] = 0;
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
for (i = 0; i < klen; i++) kpad[i] = key[i];
|
|
533
|
+
for (i = klen; i < 64; i++) kpad[i] = 0;
|
|
534
|
+
}
|
|
535
|
+
for (i = 0; i < 64; i++) {
|
|
536
|
+
ipad[i] = kpad[i] ^ 0x36;
|
|
537
|
+
opad[i] = kpad[i] ^ 0x5c;
|
|
538
|
+
}
|
|
539
|
+
/* inner = SHA1(ipad || msg) */
|
|
540
|
+
{
|
|
541
|
+
uint32_t H[5] = {
|
|
542
|
+
0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0
|
|
543
|
+
};
|
|
544
|
+
sp_crypto_sha1_block(H, ipad);
|
|
545
|
+
uint8_t buf[64];
|
|
546
|
+
size_t full = mlen & ~((size_t)63);
|
|
547
|
+
for (i = 0; i < full; i += 64) sp_crypto_sha1_block(H, msg + i);
|
|
548
|
+
size_t rem = mlen - full;
|
|
549
|
+
for (i = 0; i < rem; i++) buf[i] = msg[full + i];
|
|
550
|
+
buf[rem] = 0x80;
|
|
551
|
+
if (rem >= 56) {
|
|
552
|
+
for (i = rem + 1; i < 64; i++) buf[i] = 0;
|
|
553
|
+
sp_crypto_sha1_block(H, buf);
|
|
554
|
+
for (i = 0; i < 56; i++) buf[i] = 0;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
for (i = rem + 1; i < 56; i++) buf[i] = 0;
|
|
558
|
+
}
|
|
559
|
+
uint64_t bits = (uint64_t)(64 + mlen) * 8;
|
|
560
|
+
for (i = 0; i < 8; i++) buf[56 + i] = (uint8_t)(bits >> (56 - 8*i));
|
|
561
|
+
sp_crypto_sha1_block(H, buf);
|
|
562
|
+
for (i = 0; i < 5; i++) {
|
|
563
|
+
inner[i*4] = (uint8_t)(H[i] >> 24);
|
|
564
|
+
inner[i*4+1] = (uint8_t)(H[i] >> 16);
|
|
565
|
+
inner[i*4+2] = (uint8_t)(H[i] >> 8);
|
|
566
|
+
inner[i*4+3] = (uint8_t)(H[i]);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
/* outer = SHA1(opad || inner) */
|
|
570
|
+
{
|
|
571
|
+
uint32_t H[5] = {
|
|
572
|
+
0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0
|
|
573
|
+
};
|
|
574
|
+
sp_crypto_sha1_block(H, opad);
|
|
575
|
+
uint8_t buf[64];
|
|
576
|
+
for (i = 0; i < 20; i++) buf[i] = inner[i];
|
|
577
|
+
buf[20] = 0x80;
|
|
578
|
+
for (i = 21; i < 56; i++) buf[i] = 0;
|
|
579
|
+
uint64_t bits = (uint64_t)(64 + 20) * 8;
|
|
580
|
+
for (i = 0; i < 8; i++) buf[56 + i] = (uint8_t)(bits >> (56 - 8*i));
|
|
581
|
+
sp_crypto_sha1_block(H, buf);
|
|
582
|
+
sp_crypto_store_be32(out, H, 5);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
static SP_TLS char sp_crypto_hmac_sha1_hex_buf[41];
|
|
587
|
+
|
|
588
|
+
const char *sp_crypto_hmac_sha1_hex(const char *key, const char *msg) {SP_GC_ROOT_STR(key);SP_GC_ROOT_STR(msg);
|
|
589
|
+
uint8_t out[20];
|
|
590
|
+
sp_crypto_hmac_sha1((const uint8_t *)key, sp_str_byte_len(key),
|
|
591
|
+
(const uint8_t *)msg, sp_str_byte_len(msg),
|
|
592
|
+
out);
|
|
593
|
+
static const char H[] = "0123456789abcdef";
|
|
594
|
+
int i;
|
|
595
|
+
for (i = 0; i < 20; i++) {
|
|
596
|
+
sp_crypto_hmac_sha1_hex_buf[i*2] = H[(out[i] >> 4) & 0xf];
|
|
597
|
+
sp_crypto_hmac_sha1_hex_buf[i*2+1] = H[out[i] & 0xf];
|
|
598
|
+
}
|
|
599
|
+
sp_crypto_hmac_sha1_hex_buf[40] = '\0';
|
|
600
|
+
return sp_crypto_hmac_sha1_hex_buf;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/* HMAC-SHA1 as its own bytes; see the SHA-256 sibling. */
|
|
604
|
+
static SP_TLS char sp_crypto_hmac_sha1_bin_buf[20];
|
|
605
|
+
|
|
606
|
+
const char *sp_crypto_hmac_sha1_bin(const char *key, const char *msg) {SP_GC_ROOT_STR(key);SP_GC_ROOT_STR(msg);
|
|
607
|
+
sp_crypto_hmac_sha1((const uint8_t *)key, sp_str_byte_len(key),
|
|
608
|
+
(const uint8_t *)msg, sp_str_byte_len(msg),
|
|
609
|
+
(uint8_t *)sp_crypto_hmac_sha1_bin_buf);
|
|
610
|
+
sp_ffi_bin_len = 20;
|
|
611
|
+
return sp_crypto_hmac_sha1_bin_buf;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/* ---------- Base64URL (RFC 4648 §5) ---------- */
|
|
615
|
+
|
|
616
|
+
static const char SPC_B64U[64] =
|
|
617
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
618
|
+
|
|
619
|
+
static SP_TLS char sp_crypto_hmac_b64url_buf[44];
|
|
620
|
+
static void sp_crypto_b64url_bytes(const uint8_t *src, int n, char *out);
|
|
621
|
+
|
|
622
|
+
const char *sp_crypto_hmac_sha256_b64url(const char *key, const char *msg) {SP_GC_ROOT_STR(key);SP_GC_ROOT_STR(msg);
|
|
623
|
+
uint8_t out[32];
|
|
624
|
+
sp_crypto_hmac_sha256((const uint8_t *)key, sp_str_byte_len(key),
|
|
625
|
+
(const uint8_t *)msg, sp_str_byte_len(msg),
|
|
626
|
+
out);
|
|
627
|
+
sp_crypto_b64url_bytes(out, 32, sp_crypto_hmac_b64url_buf);
|
|
628
|
+
return sp_crypto_hmac_b64url_buf;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
#define SPC_B64U_BUFSIZE (16 * 1024)
|
|
632
|
+
static SP_TLS char sp_crypto_b64url_buf[SPC_B64U_BUFSIZE];
|
|
633
|
+
|
|
634
|
+
const char *sp_crypto_b64url_encode(const char *src) {
|
|
635
|
+
size_t n = sp_str_byte_len(src);
|
|
636
|
+
size_t i = 0, j = 0;
|
|
637
|
+
if (4 * ((n + 2) / 3) + 1 > SPC_B64U_BUFSIZE) {
|
|
638
|
+
sp_crypto_b64url_buf[0] = '\0';
|
|
639
|
+
return sp_crypto_b64url_buf;
|
|
640
|
+
}
|
|
641
|
+
while (i + 3 <= n) {
|
|
642
|
+
uint32_t v = ((uint32_t)(uint8_t)src[i] << 16)
|
|
643
|
+
| ((uint32_t)(uint8_t)src[i+1] << 8)
|
|
644
|
+
| (uint32_t)(uint8_t)src[i+2];
|
|
645
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[(v >> 18) & 0x3f];
|
|
646
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[(v >> 12) & 0x3f];
|
|
647
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[(v >> 6) & 0x3f];
|
|
648
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[ v & 0x3f];
|
|
649
|
+
i += 3;
|
|
650
|
+
}
|
|
651
|
+
size_t rem = n - i;
|
|
652
|
+
if (rem == 1) {
|
|
653
|
+
uint32_t v = (uint32_t)(uint8_t)src[i] << 16;
|
|
654
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[(v >> 18) & 0x3f];
|
|
655
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[(v >> 12) & 0x3f];
|
|
656
|
+
}
|
|
657
|
+
else if (rem == 2) {
|
|
658
|
+
uint32_t v = ((uint32_t)(uint8_t)src[i] << 16)
|
|
659
|
+
| ((uint32_t)(uint8_t)src[i+1] << 8);
|
|
660
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[(v >> 18) & 0x3f];
|
|
661
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[(v >> 12) & 0x3f];
|
|
662
|
+
sp_crypto_b64url_buf[j++] = SPC_B64U[(v >> 6) & 0x3f];
|
|
663
|
+
}
|
|
664
|
+
sp_crypto_b64url_buf[j] = '\0';
|
|
665
|
+
return sp_crypto_b64url_buf;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
static SP_TLS char sp_crypto_b64u_dec_buf[SPC_B64U_BUFSIZE];
|
|
669
|
+
|
|
670
|
+
static int sp_crypto_b64u_val(char c) {
|
|
671
|
+
if (c >= 'A' && c <= 'Z') return c - 'A';
|
|
672
|
+
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
|
673
|
+
if (c >= '0' && c <= '9') return c - '0' + 52;
|
|
674
|
+
if (c == '-') return 62;
|
|
675
|
+
if (c == '_') return 63;
|
|
676
|
+
return -1;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const char *sp_crypto_b64url_decode(const char *src) {SP_GC_ROOT_STR(src);
|
|
680
|
+
size_t n = strlen(src);
|
|
681
|
+
while (n > 0 && src[n - 1] == '=') n--; /* tolerate padded input */
|
|
682
|
+
size_t i = 0, j = 0;
|
|
683
|
+
if (n / 4 * 3 + 3 > SPC_B64U_BUFSIZE) {
|
|
684
|
+
sp_crypto_b64u_dec_buf[0] = '\0'; sp_ffi_bin_len = 0;
|
|
685
|
+
return sp_crypto_b64u_dec_buf;
|
|
686
|
+
}
|
|
687
|
+
while (i + 4 <= n) {
|
|
688
|
+
int a = sp_crypto_b64u_val(src[i]);
|
|
689
|
+
int b = sp_crypto_b64u_val(src[i+1]);
|
|
690
|
+
int c = sp_crypto_b64u_val(src[i+2]);
|
|
691
|
+
int d = sp_crypto_b64u_val(src[i+3]);
|
|
692
|
+
if (a < 0 || b < 0 || c < 0 || d < 0) {
|
|
693
|
+
sp_crypto_b64u_dec_buf[0] = '\0'; sp_ffi_bin_len = 0;
|
|
694
|
+
return sp_crypto_b64u_dec_buf;
|
|
695
|
+
}
|
|
696
|
+
uint32_t v = (uint32_t)a << 18 | (uint32_t)b << 12
|
|
697
|
+
| (uint32_t)c << 6 | (uint32_t)d;
|
|
698
|
+
sp_crypto_b64u_dec_buf[j++] = (v >> 16) & 0xff;
|
|
699
|
+
sp_crypto_b64u_dec_buf[j++] = (v >> 8) & 0xff;
|
|
700
|
+
sp_crypto_b64u_dec_buf[j++] = v & 0xff;
|
|
701
|
+
i += 4;
|
|
702
|
+
}
|
|
703
|
+
size_t rem = n - i;
|
|
704
|
+
if (rem == 2) {
|
|
705
|
+
int a = sp_crypto_b64u_val(src[i]);
|
|
706
|
+
int b = sp_crypto_b64u_val(src[i+1]);
|
|
707
|
+
if (a < 0 || b < 0) { sp_crypto_b64u_dec_buf[0] = '\0'; sp_ffi_bin_len = 0; return sp_crypto_b64u_dec_buf; }
|
|
708
|
+
sp_crypto_b64u_dec_buf[j++] = (a << 2) | (b >> 4);
|
|
709
|
+
}
|
|
710
|
+
else if (rem == 3) {
|
|
711
|
+
int a = sp_crypto_b64u_val(src[i]);
|
|
712
|
+
int b = sp_crypto_b64u_val(src[i+1]);
|
|
713
|
+
int c = sp_crypto_b64u_val(src[i+2]);
|
|
714
|
+
if (a < 0 || b < 0 || c < 0) { sp_crypto_b64u_dec_buf[0] = '\0'; sp_ffi_bin_len = 0; return sp_crypto_b64u_dec_buf; }
|
|
715
|
+
sp_crypto_b64u_dec_buf[j++] = (a << 2) | (b >> 4);
|
|
716
|
+
sp_crypto_b64u_dec_buf[j++] = ((b & 0xf) << 4) | (c >> 2);
|
|
717
|
+
}
|
|
718
|
+
sp_crypto_b64u_dec_buf[j] = '\0';
|
|
719
|
+
sp_ffi_bin_len = (int)j; /* the decoded bytes may hold a NUL: bind it `:binstr` */
|
|
720
|
+
return sp_crypto_b64u_dec_buf;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/* ---------- PBKDF2-HMAC-SHA256 (RFC 8018) ----------
|
|
724
|
+
* dkLen up to 64 bytes (two HMAC-SHA256 output blocks).
|
|
725
|
+
*/
|
|
726
|
+
|
|
727
|
+
/* 64 derived bytes -> 86 unpadded b64url chars + NUL. */
|
|
728
|
+
static SP_TLS char sp_crypto_pbkdf2_b64url_buf[88];
|
|
729
|
+
|
|
730
|
+
/* Unpadded base64url of `n` bytes into `out` (NUL-terminated). */
|
|
731
|
+
static void sp_crypto_b64url_bytes(const uint8_t *src, int n, char *out) {
|
|
732
|
+
int i, j = 0;
|
|
733
|
+
for (i = 0; i + 3 <= n; i += 3) {
|
|
734
|
+
uint32_t v = ((uint32_t)src[i] << 16)
|
|
735
|
+
| ((uint32_t)src[i+1] << 8)
|
|
736
|
+
| (uint32_t)src[i+2];
|
|
737
|
+
out[j++] = SPC_B64U[(v >> 18) & 0x3f];
|
|
738
|
+
out[j++] = SPC_B64U[(v >> 12) & 0x3f];
|
|
739
|
+
out[j++] = SPC_B64U[(v >> 6) & 0x3f];
|
|
740
|
+
out[j++] = SPC_B64U[v & 0x3f];
|
|
741
|
+
}
|
|
742
|
+
if (i < n) {
|
|
743
|
+
uint32_t v = ((uint32_t)src[i] << 16)
|
|
744
|
+
| (i + 1 < n ? ((uint32_t)src[i+1] << 8) : 0);
|
|
745
|
+
out[j++] = SPC_B64U[(v >> 18) & 0x3f];
|
|
746
|
+
out[j++] = SPC_B64U[(v >> 12) & 0x3f];
|
|
747
|
+
if (i + 1 < n) {
|
|
748
|
+
out[j++] = SPC_B64U[(v >> 6) & 0x3f];
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
out[j] = '\0';
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const char *sp_crypto_pbkdf2_sha256_b64url_len(const char *password, const char *salt,
|
|
755
|
+
int iters, int dklen) {SP_GC_ROOT_STR(password);
|
|
756
|
+
if (iters < 1) iters = 1;
|
|
757
|
+
if (dklen < 1) dklen = 32;
|
|
758
|
+
if (dklen > 64) dklen = 64;
|
|
759
|
+
size_t plen = sp_str_byte_len(password);
|
|
760
|
+
size_t slen = sp_str_byte_len(salt);
|
|
761
|
+
uint8_t salted[256];
|
|
762
|
+
if (slen + 4 > sizeof(salted)) {
|
|
763
|
+
sp_crypto_pbkdf2_b64url_buf[0] = '\0';
|
|
764
|
+
return sp_crypto_pbkdf2_b64url_buf;
|
|
765
|
+
}
|
|
766
|
+
memcpy(salted, salt, slen);
|
|
767
|
+
/* PBKDF2 (RFC 8018 sec 5.2): DK = T(1) || T(2) || ... , each block
|
|
768
|
+
T(i) = F(password, salt, iters, i) and 32 bytes wide for SHA-256.
|
|
769
|
+
Blocks are independent -- only the INT(i) suffix on the salt
|
|
770
|
+
changes -- so one loop covers any dkLen. */
|
|
771
|
+
uint8_t dk[64];
|
|
772
|
+
int block, done = 0;
|
|
773
|
+
for (block = 1; done < dklen; block++) {
|
|
774
|
+
uint8_t U[32], T[32];
|
|
775
|
+
int b, it, take;
|
|
776
|
+
salted[slen+0] = (uint8_t)((block >> 24) & 0xff);
|
|
777
|
+
salted[slen+1] = (uint8_t)((block >> 16) & 0xff);
|
|
778
|
+
salted[slen+2] = (uint8_t)((block >> 8) & 0xff);
|
|
779
|
+
salted[slen+3] = (uint8_t)(block & 0xff);
|
|
780
|
+
sp_crypto_hmac_sha256((const uint8_t *)password, plen, salted, slen + 4, U);
|
|
781
|
+
memcpy(T, U, 32);
|
|
782
|
+
for (it = 1; it < iters; it++) {
|
|
783
|
+
sp_crypto_hmac_sha256((const uint8_t *)password, plen, U, 32, U);
|
|
784
|
+
for (b = 0; b < 32; b++) T[b] ^= U[b];
|
|
785
|
+
}
|
|
786
|
+
take = (dklen - done < 32) ? (dklen - done) : 32;
|
|
787
|
+
memcpy(dk + done, T, take);
|
|
788
|
+
done += take;
|
|
789
|
+
}
|
|
790
|
+
sp_crypto_b64url_bytes(dk, dklen, sp_crypto_pbkdf2_b64url_buf);
|
|
791
|
+
return sp_crypto_pbkdf2_b64url_buf;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const char *sp_crypto_pbkdf2_sha256_b64url(const char *password, const char *salt, int iters) {
|
|
795
|
+
return sp_crypto_pbkdf2_sha256_b64url_len(password, salt, iters, 32);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/* ---------- CSPRNG ---------- */
|
|
799
|
+
|
|
800
|
+
static SP_TLS char sp_crypto_random_b64url_buf[90];
|
|
801
|
+
|
|
802
|
+
/* Fill `out` with `nbytes` CSPRNG bytes. 1 on success, 0 when no secure
|
|
803
|
+
source is available -- the ONE place the entropy decision is made, so the
|
|
804
|
+
raw-bytes and base64url entry points below cannot drift apart about what
|
|
805
|
+
counts as secure. */
|
|
806
|
+
int sp_crypto_entropy(uint8_t *out, int nbytes) {
|
|
807
|
+
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
|
808
|
+
arc4random_buf(out, nbytes);
|
|
809
|
+
return 1;
|
|
810
|
+
#elif defined(__wasi__) || defined(_WIN32)
|
|
811
|
+
/* the host's entropy, through WASI's random_get or (Windows, lib/win32)
|
|
812
|
+
BCryptGenRandom; there is no device */
|
|
813
|
+
return getentropy(out, (size_t)nbytes) == 0;
|
|
814
|
+
#else
|
|
815
|
+
/* getrandom(2) first: kernel-sourced, works where /dev/urandom is not
|
|
816
|
+
even visible (chroot, minimal containers) -- the very environments
|
|
817
|
+
whose old time-based fallback made "CSPRNG" a lie. Then the device;
|
|
818
|
+
then fail CLOSED, never weak randomness. */
|
|
819
|
+
#if defined(__linux__) && defined(SYS_getrandom)
|
|
820
|
+
{
|
|
821
|
+
long n = syscall(SYS_getrandom, out, (size_t)nbytes, 0);
|
|
822
|
+
if (n == (long)nbytes) return 1;
|
|
823
|
+
}
|
|
824
|
+
#endif
|
|
825
|
+
{
|
|
826
|
+
FILE *f = fopen("/dev/urandom", "rb");
|
|
827
|
+
if (!f) return 0;
|
|
828
|
+
size_t got = fread(out, 1, (size_t)nbytes, f);
|
|
829
|
+
fclose(f);
|
|
830
|
+
if (got != (size_t)nbytes) return 0;
|
|
831
|
+
}
|
|
832
|
+
return 1;
|
|
833
|
+
#endif
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/* nbytes raw CSPRNG bytes. SecureRandom's one primitive: every other method
|
|
837
|
+
in the bundled `securerandom` package is a rendering of these bytes, so
|
|
838
|
+
this is the only place that has to be right about entropy.
|
|
839
|
+
|
|
840
|
+
RAISES rather than returning NULL when no secure source is available.
|
|
841
|
+
The b64url sibling below predates it and keeps its NULL contract, but a
|
|
842
|
+
caller reaching for random BYTES is minting a token or a key -- there is
|
|
843
|
+
no answer to give, and a nil flowing into a token is the failure nothing
|
|
844
|
+
notices. Length rides sp_ffi_bin_len for the `:cbinstr` return mode:
|
|
845
|
+
random bytes contain NULs about one time in 256.
|
|
846
|
+
|
|
847
|
+
SP_TLS, unlike its siblings in this file. The `:cbinstr` call site copies
|
|
848
|
+
the returned pointer AFTER the call, so under -DSP_THREADS a second worker
|
|
849
|
+
drawing in that window would overwrite the first one's bytes -- and here
|
|
850
|
+
that means two sessions handed the same token, which is the one failure
|
|
851
|
+
mode that never shows up as a crash. sp_ffi_bin_len, which carries this
|
|
852
|
+
buffer's length, is already SP_TLS: a value and its length in different
|
|
853
|
+
storage classes cannot both be right. sp_random.c keeps its generator
|
|
854
|
+
state per-worker for the same reason. Costs SPC_RANDOM_MAX bytes per
|
|
855
|
+
thread. */
|
|
856
|
+
static SP_TLS char sp_crypto_random_bin_buf[SPC_RANDOM_MAX];
|
|
857
|
+
|
|
858
|
+
const char *sp_crypto_random_bin(int nbytes) {
|
|
859
|
+
if (nbytes < 0) sp_raise_cls("ArgumentError", "negative string size");
|
|
860
|
+
if (nbytes > SPC_RANDOM_MAX)
|
|
861
|
+
sp_raise_cls("ArgumentError",
|
|
862
|
+
"securerandom: request exceeds the bundled maximum draw");
|
|
863
|
+
if (nbytes == 0) { sp_ffi_bin_len = 0; sp_crypto_random_bin_buf[0] = '\0'; return sp_crypto_random_bin_buf; }
|
|
864
|
+
if (!sp_crypto_entropy((uint8_t *)sp_crypto_random_bin_buf, nbytes))
|
|
865
|
+
sp_raise_cls("RuntimeError", "securerandom: no secure random source available");
|
|
866
|
+
sp_ffi_bin_len = nbytes;
|
|
867
|
+
return sp_crypto_random_bin_buf;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const char *sp_crypto_random_b64url(int nbytes) {
|
|
871
|
+
if (nbytes < 1) nbytes = 16;
|
|
872
|
+
if (nbytes > 64) nbytes = 64;
|
|
873
|
+
uint8_t r[64];
|
|
874
|
+
if (!sp_crypto_entropy(r, nbytes)) return NULL;
|
|
875
|
+
sp_crypto_b64url_bytes(r, nbytes, sp_crypto_random_b64url_buf);
|
|
876
|
+
return sp_crypto_random_b64url_buf;
|
|
877
|
+
}
|