AtlatestRepositorysigil-crypto
1/*
2 * Sigil Crypto Library Implementation
3 *
4 * This file implements cryptographic operations using mbedTLS:
5 * - SHA-256 hashing
6 * - HMAC-SHA256 message authentication
7 * - Base64 encoding/decoding
8 * - Cryptographically secure random number generation (CTR-DRBG)
9 *
10 * Note: Crypto is not available on Emscripten/WebAssembly builds.
11 * Web applications should use the Web Crypto API via JavaScript interop.
12 */
14#include <sigil/sigil.h>
16#ifdef __EMSCRIPTEN__
17/*
18 * Stub implementation for Emscripten builds.
19 * Crypto operations should use Web Crypto API in the browser.
20 */
21void sigil__init_sigil_crypto_module(SigilVM *vm)
23 (void)vm;
24 /* Crypto module not available on web platform */
27#else /* Native build */
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
32#include "mbedtls/sha1.h"
33#include "mbedtls/sha256.h"
34#include "mbedtls/ripemd160.h"
35#include "mbedtls/md.h"
36#include "mbedtls/base64.h"
37#include "mbedtls/entropy.h"
38#include "mbedtls/ctr_drbg.h"
39#include "mbedtls/pkcs5.h"
40#include "mbedtls/ecp.h"
41#include "mbedtls/ecdsa.h"
42#include "mbedtls/ecdh.h"
43#include "mbedtls/bignum.h"
44#include "mbedtls/gcm.h"
46/* Global RNG context (initialized on first use) */
47static mbedtls_entropy_context entropy_ctx;
48static mbedtls_ctr_drbg_context ctr_drbg_ctx;
49static int rng_initialized = 0;
51/*
52 * Initialize the random number generator on first use.
53 * Returns 0 on success, non-zero on failure.
54 */
55static int ensure_rng_initialized(void)
57 if (rng_initialized) return 0;
59 mbedtls_entropy_init(&entropy_ctx);
60 mbedtls_ctr_drbg_init(&ctr_drbg_ctx);
62 /* Seed the DRBG with entropy from the OS */
63 int ret = mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func,
64 &entropy_ctx, NULL, 0);
65 if (ret != 0) {
66 mbedtls_ctr_drbg_free(&ctr_drbg_ctx);
67 mbedtls_entropy_free(&entropy_ctx);
68 return ret;
69 }
71 rng_initialized = 1;
72 return 0;
75/*
76 * Helper: Convert bytes to hex string
77 */
78static void bytes_to_hex(const unsigned char *bytes, size_t len, char *hex)
80 static const char hex_chars[] = "0123456789abcdef";
81 for (size_t i = 0; i < len; i++) {
82 hex[i * 2] = hex_chars[(bytes[i] >> 4) & 0x0f];
83 hex[i * 2 + 1] = hex_chars[bytes[i] & 0x0f];
84 }
85 hex[len * 2] = '\0';
88/*
89 * sha256 data -> hex-string
90 * Compute SHA-256 hash of data (string or bytevector).
91 * Returns 64-character hex-encoded hash.
92 */
93static Value native_sha256(SigilVM *vm, int argc, Value *args)
95 (void)argc;
97 const unsigned char *data;
98 size_t len;
100 if (sigil_is_string(args[0])) {
101 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
102 data = (const unsigned char *)s->data;
103 len = s->byte_length;
104 } else if (sigil_is_bytevector(args[0])) {
105 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
106 data = bv->data;
107 len = bv->length;
108 } else {
109 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sha256: expected string or bytevector");
110 return SIGIL_UNDEFINED;
111 }
113 unsigned char hash[32]; /* SHA-256 produces 32 bytes */
115 /* Use mbedTLS SHA-256 */
116 mbedtls_sha256_context ctx;
117 mbedtls_sha256_init(&ctx);
118 mbedtls_sha256_starts(&ctx, 0); /* 0 = SHA-256, 1 = SHA-224 */
119 mbedtls_sha256_update(&ctx, data, len);
120 mbedtls_sha256_finish(&ctx, hash);
121 mbedtls_sha256_free(&ctx);
123 /* Convert to hex string */
124 char hex[65];
125 bytes_to_hex(hash, 32, hex);
127 return sigil_make_string(vm, hex, 64);
130/*
131 * sha256-bytes data -> bytevector
132 * Compute SHA-256 hash of data (string or bytevector).
133 * Returns 32-byte hash as bytevector.
134 */
135static Value native_sha256_bytes(SigilVM *vm, int argc, Value *args)
137 (void)argc;
139 const unsigned char *data;
140 size_t len;
142 if (sigil_is_string(args[0])) {
143 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
144 data = (const unsigned char *)s->data;
145 len = s->byte_length;
146 } else if (sigil_is_bytevector(args[0])) {
147 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
148 data = bv->data;
149 len = bv->length;
150 } else {
151 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sha256-bytes: expected string or bytevector");
152 return SIGIL_UNDEFINED;
153 }
155 unsigned char hash[32]; /* SHA-256 produces 32 bytes */
157 /* Use mbedTLS SHA-256 */
158 mbedtls_sha256_context ctx;
159 mbedtls_sha256_init(&ctx);
160 mbedtls_sha256_starts(&ctx, 0); /* 0 = SHA-256, 1 = SHA-224 */
161 mbedtls_sha256_update(&ctx, data, len);
162 mbedtls_sha256_finish(&ctx, hash);
163 mbedtls_sha256_free(&ctx);
165 /* Return as bytevector */
166 Value result = sigil_make_bytevector(vm, 32);
167 if (sigil_is_bytevector(result)) {
168 memcpy(sigil_bytevector_data(result), hash, 32);
169 }
170 return result;
173/*
174 * sha1 data -> bytevector
175 * Compute SHA-1 hash of data (string or bytevector).
176 * Returns 20-byte hash as bytevector (for WebSocket handshake compatibility).
177 * Note: SHA-1 is cryptographically weak; use SHA-256 for new applications.
178 */
179static Value native_sha1(SigilVM *vm, int argc, Value *args)
181 (void)argc;
183 const unsigned char *data;
184 size_t len;
186 if (sigil_is_string(args[0])) {
187 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
188 data = (const unsigned char *)s->data;
189 len = s->byte_length;
190 } else if (sigil_is_bytevector(args[0])) {
191 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
192 data = bv->data;
193 len = bv->length;
194 } else {
195 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sha1: expected string or bytevector");
196 return SIGIL_UNDEFINED;
197 }
199 unsigned char hash[20]; /* SHA-1 produces 20 bytes */
201 /* Use mbedTLS SHA-1 */
202 mbedtls_sha1_context ctx;
203 mbedtls_sha1_init(&ctx);
204 mbedtls_sha1_starts(&ctx);
205 mbedtls_sha1_update(&ctx, data, len);
206 mbedtls_sha1_finish(&ctx, hash);
207 mbedtls_sha1_free(&ctx);
209 /* Return as bytevector for use with base64-encode */
210 Value result = sigil_make_bytevector(vm, 20);
211 if (sigil_is_bytevector(result)) {
212 memcpy(sigil_bytevector_data(result), hash, 20);
213 }
214 return result;
217/*
218 * ripemd160 data -> bytevector
219 * Compute RIPEMD-160 hash of data (string or bytevector).
220 * Returns 20-byte hash as bytevector.
221 */
222static Value native_ripemd160(SigilVM *vm, int argc, Value *args)
224 (void)argc;
226 const unsigned char *data;
227 size_t len;
229 if (sigil_is_string(args[0])) {
230 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
231 data = (const unsigned char *)s->data;
232 len = s->byte_length;
233 } else if (sigil_is_bytevector(args[0])) {
234 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
235 data = bv->data;
236 len = bv->length;
237 } else {
238 sigil__vm_error(vm, SIGIL_ERR_TYPE, "ripemd160: expected string or bytevector");
239 return SIGIL_UNDEFINED;
240 }
242 unsigned char hash[20];
244 mbedtls_ripemd160_context ctx;
245 mbedtls_ripemd160_init(&ctx);
246 mbedtls_ripemd160_starts(&ctx);
247 mbedtls_ripemd160_update(&ctx, data, len);
248 mbedtls_ripemd160_finish(&ctx, hash);
249 mbedtls_ripemd160_free(&ctx);
251 Value result = sigil_make_bytevector(vm, 20);
252 if (sigil_is_bytevector(result)) {
253 memcpy(sigil_bytevector_data(result), hash, 20);
254 }
255 return result;
258/*
259 * hmac-sha256 key data -> hex-string
260 * Compute HMAC-SHA256 of data using the given key.
261 * Both key and data can be strings or bytevectors.
262 * Returns 64-character hex-encoded MAC.
263 */
264static Value native_hmac_sha256(SigilVM *vm, int argc, Value *args)
266 (void)argc;
268 const unsigned char *key_data;
269 size_t key_len;
270 const unsigned char *msg_data;
271 size_t msg_len;
273 /* Get key */
274 if (sigil_is_string(args[0])) {
275 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
276 key_data = (const unsigned char *)s->data;
277 key_len = s->byte_length;
278 } else if (sigil_is_bytevector(args[0])) {
279 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
280 key_data = bv->data;
281 key_len = bv->length;
282 } else {
283 sigil__vm_error(vm, SIGIL_ERR_TYPE, "hmac-sha256: expected string or bytevector for key");
284 return SIGIL_UNDEFINED;
285 }
287 /* Get message */
288 if (sigil_is_string(args[1])) {
289 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
290 msg_data = (const unsigned char *)s->data;
291 msg_len = s->byte_length;
292 } else if (sigil_is_bytevector(args[1])) {
293 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[1]);
294 msg_data = bv->data;
295 msg_len = bv->length;
296 } else {
297 sigil__vm_error(vm, SIGIL_ERR_TYPE, "hmac-sha256: expected string or bytevector for data");
298 return SIGIL_UNDEFINED;
299 }
301 unsigned char hmac[32]; /* HMAC-SHA256 produces 32 bytes */
303 /* Use mbedTLS HMAC */
304 const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
305 int ret = mbedtls_md_hmac(md_info, key_data, key_len, msg_data, msg_len, hmac);
306 if (ret != 0) {
307 return SIGIL_FALSE;
308 }
310 /* Convert to hex string */
311 char hex[65];
312 bytes_to_hex(hmac, 32, hex);
314 return sigil_make_string(vm, hex, 64);
317/*
318 * hmac-sha256-bytes key data -> bytevector
319 * Like hmac-sha256 but returns the 32-byte MAC as a bytevector instead
320 * of a hex string. Required for SCRAM-SHA-256 where intermediate values
321 * are bytewise XORed and concatenated.
322 */
323static Value native_hmac_sha256_bytes(SigilVM *vm, int argc, Value *args)
325 (void)argc;
327 const unsigned char *key_data;
328 size_t key_len;
329 const unsigned char *msg_data;
330 size_t msg_len;
332 if (sigil_is_string(args[0])) {
333 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
334 key_data = (const unsigned char *)s->data;
335 key_len = s->byte_length;
336 } else if (sigil_is_bytevector(args[0])) {
337 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
338 key_data = bv->data;
339 key_len = bv->length;
340 } else {
341 sigil__vm_error(vm, SIGIL_ERR_TYPE, "hmac-sha256-bytes: expected string or bytevector for key");
342 return SIGIL_UNDEFINED;
343 }
345 if (sigil_is_string(args[1])) {
346 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
347 msg_data = (const unsigned char *)s->data;
348 msg_len = s->byte_length;
349 } else if (sigil_is_bytevector(args[1])) {
350 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[1]);
351 msg_data = bv->data;
352 msg_len = bv->length;
353 } else {
354 sigil__vm_error(vm, SIGIL_ERR_TYPE, "hmac-sha256-bytes: expected string or bytevector for data");
355 return SIGIL_UNDEFINED;
356 }
358 unsigned char hmac[32];
360 const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
361 int ret = mbedtls_md_hmac(md_info, key_data, key_len, msg_data, msg_len, hmac);
362 if (ret != 0) {
363 return SIGIL_FALSE;
364 }
366 Value result = sigil_make_bytevector(vm, 32);
367 if (sigil_is_bytevector(result)) {
368 memcpy(sigil_bytevector_data(result), hmac, 32);
369 }
370 return result;
373/*
374 * hmac-sha512-bytes key data -> bytevector
375 * Like hmac-sha256-bytes but using SHA-512.
376 */
377static Value native_hmac_sha512_bytes(SigilVM *vm, int argc, Value *args)
379 (void)argc;
381 const unsigned char *key_data;
382 size_t key_len;
383 const unsigned char *msg_data;
384 size_t msg_len;
386 if (sigil_is_string(args[0])) {
387 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
388 key_data = (const unsigned char *)s->data;
389 key_len = s->byte_length;
390 } else if (sigil_is_bytevector(args[0])) {
391 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
392 key_data = bv->data;
393 key_len = bv->length;
394 } else {
395 sigil__vm_error(vm, SIGIL_ERR_TYPE, "hmac-sha512-bytes: expected string or bytevector for key");
396 return SIGIL_UNDEFINED;
397 }
399 if (sigil_is_string(args[1])) {
400 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
401 msg_data = (const unsigned char *)s->data;
402 msg_len = s->byte_length;
403 } else if (sigil_is_bytevector(args[1])) {
404 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[1]);
405 msg_data = bv->data;
406 msg_len = bv->length;
407 } else {
408 sigil__vm_error(vm, SIGIL_ERR_TYPE, "hmac-sha512-bytes: expected string or bytevector for data");
409 return SIGIL_UNDEFINED;
410 }
412 unsigned char hmac[64];
414 const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
415 int ret = mbedtls_md_hmac(md_info, key_data, key_len, msg_data, msg_len, hmac);
416 if (ret != 0) {
417 return SIGIL_FALSE;
418 }
420 Value result = sigil_make_bytevector(vm, 64);
421 if (sigil_is_bytevector(result)) {
422 memcpy(sigil_bytevector_data(result), hmac, 64);
423 }
424 return result;
427/*
428 * hmac-sha1 key data -> bytevector
429 * Compute HMAC-SHA1 of data using the given key.
430 * Both key and data can be strings or bytevectors.
431 * Returns 20-byte MAC as bytevector.
432 */
433static Value native_hmac_sha1(SigilVM *vm, int argc, Value *args)
435 (void)argc;
437 const unsigned char *key_data;
438 size_t key_len;
439 const unsigned char *msg_data;
440 size_t msg_len;
442 /* Get key */
443 if (sigil_is_string(args[0])) {
444 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
445 key_data = (const unsigned char *)s->data;
446 key_len = s->byte_length;
447 } else if (sigil_is_bytevector(args[0])) {
448 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
449 key_data = bv->data;
450 key_len = bv->length;
451 } else {
452 sigil__vm_error(vm, SIGIL_ERR_TYPE, "hmac-sha1: expected string or bytevector for key");
453 return SIGIL_UNDEFINED;
454 }
456 /* Get message */
457 if (sigil_is_string(args[1])) {
458 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
459 msg_data = (const unsigned char *)s->data;
460 msg_len = s->byte_length;
461 } else if (sigil_is_bytevector(args[1])) {
462 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[1]);
463 msg_data = bv->data;
464 msg_len = bv->length;
465 } else {
466 sigil__vm_error(vm, SIGIL_ERR_TYPE, "hmac-sha1: expected string or bytevector for data");
467 return SIGIL_UNDEFINED;
468 }
470 unsigned char hmac[20]; /* HMAC-SHA1 produces 20 bytes */
472 const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
473 int ret = mbedtls_md_hmac(md_info, key_data, key_len, msg_data, msg_len, hmac);
474 if (ret != 0) {
475 return SIGIL_FALSE;
476 }
478 Value result = sigil_make_bytevector(vm, 20);
479 if (sigil_is_bytevector(result)) {
480 memcpy(sigil_bytevector_data(result), hmac, 20);
481 }
482 return result;
485/*
486 * pbkdf2-sha1 password salt iterations key-length -> bytevector
487 * Derive a key using PBKDF2 with HMAC-SHA1.
488 * Password and salt can be strings or bytevectors.
489 * Returns derived key as bytevector.
490 */
491static Value native_pbkdf2_sha1(SigilVM *vm, int argc, Value *args)
493 (void)argc;
495 const unsigned char *password;
496 size_t password_len;
497 const unsigned char *salt;
498 size_t salt_len;
500 /* Get password */
501 if (sigil_is_string(args[0])) {
502 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
503 password = (const unsigned char *)s->data;
504 password_len = s->byte_length;
505 } else if (sigil_is_bytevector(args[0])) {
506 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
507 password = bv->data;
508 password_len = bv->length;
509 } else {
510 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha1: expected string or bytevector for password");
511 return SIGIL_UNDEFINED;
512 }
514 /* Get salt */
515 if (sigil_is_string(args[1])) {
516 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
517 salt = (const unsigned char *)s->data;
518 salt_len = s->byte_length;
519 } else if (sigil_is_bytevector(args[1])) {
520 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[1]);
521 salt = bv->data;
522 salt_len = bv->length;
523 } else {
524 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha1: expected string or bytevector for salt");
525 return SIGIL_UNDEFINED;
526 }
528 /* Get iterations */
529 if (!sigil_is_fixnum(args[2])) {
530 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha1: expected integer for iterations");
531 return SIGIL_UNDEFINED;
532 }
533 int iterations = (int)sigil_as_fixnum(args[2]);
534 if (iterations < 1) {
535 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "pbkdf2-sha1: iterations must be positive");
536 return SIGIL_UNDEFINED;
537 }
539 /* Get key length */
540 if (!sigil_is_fixnum(args[3])) {
541 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha1: expected integer for key-length");
542 return SIGIL_UNDEFINED;
543 }
544 int key_length = (int)sigil_as_fixnum(args[3]);
545 if (key_length < 1 || key_length > 65536) {
546 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "pbkdf2-sha1: key-length must be 1-65536");
547 return SIGIL_UNDEFINED;
548 }
550 unsigned char *output = malloc(key_length);
551 if (!output) return SIGIL_FALSE;
553 int ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1,
554 password, password_len,
555 salt, salt_len,
556 iterations, key_length, output);
557 if (ret != 0) {
558 free(output);
559 return SIGIL_FALSE;
560 }
562 Value result = sigil_make_bytevector(vm, key_length);
563 if (sigil_is_bytevector(result)) {
564 memcpy(sigil_bytevector_data(result), output, key_length);
565 }
566 free(output);
567 return result;
570/*
571 * pbkdf2-sha256 password salt iterations key-length -> bytevector
572 * Derive a key using PBKDF2 with HMAC-SHA256.
573 * Password and salt can be strings or bytevectors.
574 * Returns derived key as bytevector.
575 *
576 * Required for SCRAM-SHA-256 (RFC 5802 / RFC 7677): the salted password
577 * `Hi(password, salt, iterations)` is PBKDF2-SHA-256 of the user's
578 * password against the per-user salt, with iterations chosen by the
579 * server (typically 4096+).
580 */
581static Value native_pbkdf2_sha256(SigilVM *vm, int argc, Value *args)
583 (void)argc;
585 const unsigned char *password;
586 size_t password_len;
587 const unsigned char *salt;
588 size_t salt_len;
590 if (sigil_is_string(args[0])) {
591 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
592 password = (const unsigned char *)s->data;
593 password_len = s->byte_length;
594 } else if (sigil_is_bytevector(args[0])) {
595 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
596 password = bv->data;
597 password_len = bv->length;
598 } else {
599 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha256: expected string or bytevector for password");
600 return SIGIL_UNDEFINED;
601 }
603 if (sigil_is_string(args[1])) {
604 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
605 salt = (const unsigned char *)s->data;
606 salt_len = s->byte_length;
607 } else if (sigil_is_bytevector(args[1])) {
608 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[1]);
609 salt = bv->data;
610 salt_len = bv->length;
611 } else {
612 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha256: expected string or bytevector for salt");
613 return SIGIL_UNDEFINED;
614 }
616 if (!sigil_is_fixnum(args[2])) {
617 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha256: expected integer for iterations");
618 return SIGIL_UNDEFINED;
619 }
620 int iterations = (int)sigil_as_fixnum(args[2]);
621 if (iterations < 1) {
622 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "pbkdf2-sha256: iterations must be positive");
623 return SIGIL_UNDEFINED;
624 }
626 if (!sigil_is_fixnum(args[3])) {
627 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha256: expected integer for key-length");
628 return SIGIL_UNDEFINED;
629 }
630 int key_length = (int)sigil_as_fixnum(args[3]);
631 if (key_length < 1 || key_length > 65536) {
632 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "pbkdf2-sha256: key-length must be 1-65536");
633 return SIGIL_UNDEFINED;
634 }
636 unsigned char *output = malloc(key_length);
637 if (!output) return SIGIL_FALSE;
639 int ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA256,
640 password, password_len,
641 salt, salt_len,
642 iterations, key_length, output);
643 if (ret != 0) {
644 free(output);
645 return SIGIL_FALSE;
646 }
648 Value result = sigil_make_bytevector(vm, key_length);
649 if (sigil_is_bytevector(result)) {
650 memcpy(sigil_bytevector_data(result), output, key_length);
651 }
652 free(output);
653 return result;
656/*
657 * pbkdf2-sha512 password salt iterations key-length -> bytevector
658 * Derive a key using PBKDF2 with HMAC-SHA512.
659 * Password and salt can be strings or bytevectors.
660 * Returns derived key as bytevector.
661 */
662static Value native_pbkdf2_sha512(SigilVM *vm, int argc, Value *args)
664 (void)argc;
666 const unsigned char *password;
667 size_t password_len;
668 const unsigned char *salt;
669 size_t salt_len;
671 if (sigil_is_string(args[0])) {
672 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
673 password = (const unsigned char *)s->data;
674 password_len = s->byte_length;
675 } else if (sigil_is_bytevector(args[0])) {
676 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
677 password = bv->data;
678 password_len = bv->length;
679 } else {
680 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha512: expected string or bytevector for password");
681 return SIGIL_UNDEFINED;
682 }
684 if (sigil_is_string(args[1])) {
685 SigilString *s = (SigilString *)sigil_as_ptr(args[1]);
686 salt = (const unsigned char *)s->data;
687 salt_len = s->byte_length;
688 } else if (sigil_is_bytevector(args[1])) {
689 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[1]);
690 salt = bv->data;
691 salt_len = bv->length;
692 } else {
693 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha512: expected string or bytevector for salt");
694 return SIGIL_UNDEFINED;
695 }
697 if (!sigil_is_fixnum(args[2])) {
698 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha512: expected integer for iterations");
699 return SIGIL_UNDEFINED;
700 }
701 int iterations = (int)sigil_as_fixnum(args[2]);
702 if (iterations < 1) {
703 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "pbkdf2-sha512: iterations must be positive");
704 return SIGIL_UNDEFINED;
705 }
707 if (!sigil_is_fixnum(args[3])) {
708 sigil__vm_error(vm, SIGIL_ERR_TYPE, "pbkdf2-sha512: expected integer for key-length");
709 return SIGIL_UNDEFINED;
710 }
711 int key_length = (int)sigil_as_fixnum(args[3]);
712 if (key_length < 1 || key_length > 65536) {
713 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "pbkdf2-sha512: key-length must be 1-65536");
714 return SIGIL_UNDEFINED;
715 }
717 unsigned char *output = malloc(key_length);
718 if (!output) return SIGIL_FALSE;
720 int ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA512,
721 password, password_len,
722 salt, salt_len,
723 iterations, key_length, output);
724 if (ret != 0) {
725 free(output);
726 return SIGIL_FALSE;
727 }
729 Value result = sigil_make_bytevector(vm, key_length);
730 if (sigil_is_bytevector(result)) {
731 memcpy(sigil_bytevector_data(result), output, key_length);
732 }
733 free(output);
734 return result;
737/*
738 * base64-encode data -> string
739 * Encode data (string or bytevector) as base64.
740 */
741static Value native_base64_encode(SigilVM *vm, int argc, Value *args)
743 (void)argc;
745 const unsigned char *data;
746 size_t len;
748 if (sigil_is_string(args[0])) {
749 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
750 data = (const unsigned char *)s->data;
751 len = s->byte_length;
752 } else if (sigil_is_bytevector(args[0])) {
753 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(args[0]);
754 data = bv->data;
755 len = bv->length;
756 } else {
757 sigil__vm_error(vm, SIGIL_ERR_TYPE, "base64-encode: expected string or bytevector");
758 return SIGIL_UNDEFINED;
759 }
761 /* Calculate output size: 4 * ceil(len/3) + 1 for null terminator */
762 size_t out_len = ((len + 2) / 3) * 4 + 1;
763 char *output = malloc(out_len);
764 if (!output) return SIGIL_FALSE;
766 /* TODO: Implement when mbedTLS is added
767 size_t olen;
768 int ret = mbedtls_base64_encode((unsigned char *)output, out_len, &olen, data, len);
769 if (ret != 0) {
770 free(output);
771 return SIGIL_FALSE;
772 }
773 Value result = sigil_make_string(vm, output, olen);
774 */
776 /* Temporary stub: use simple base64 implementation */
777 static const char base64_chars[] =
778 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
780 size_t i = 0, j = 0;
781 unsigned char array3[3], array4[4];
783 while (len--) {
784 array3[i++] = *(data++);
785 if (i == 3) {
786 array4[0] = (array3[0] & 0xfc) >> 2;
787 array4[1] = ((array3[0] & 0x03) << 4) + ((array3[1] & 0xf0) >> 4);
788 array4[2] = ((array3[1] & 0x0f) << 2) + ((array3[2] & 0xc0) >> 6);
789 array4[3] = array3[2] & 0x3f;
791 for (i = 0; i < 4; i++)
792 output[j++] = base64_chars[array4[i]];
793 i = 0;
794 }
795 }
797 if (i) {
798 for (size_t k = i; k < 3; k++)
799 array3[k] = '\0';
801 array4[0] = (array3[0] & 0xfc) >> 2;
802 array4[1] = ((array3[0] & 0x03) << 4) + ((array3[1] & 0xf0) >> 4);
803 array4[2] = ((array3[1] & 0x0f) << 2) + ((array3[2] & 0xc0) >> 6);
805 for (size_t k = 0; k < i + 1; k++)
806 output[j++] = base64_chars[array4[k]];
808 while (i++ < 3)
809 output[j++] = '=';
810 }
812 output[j] = '\0';
813 Value result = sigil_make_string(vm, output, j);
814 free(output);
815 return result;
818/*
819 * base64-decode string -> bytevector | #f
820 * Decode base64 string to bytevector.
821 * Returns #f if input is not valid base64.
822 */
823static Value native_base64_decode(SigilVM *vm, int argc, Value *args)
825 (void)argc;
827 if (!sigil_is_string(args[0])) {
828 sigil__vm_error(vm, SIGIL_ERR_TYPE, "base64-decode: expected string");
829 return SIGIL_UNDEFINED;
830 }
832 SigilString *s = (SigilString *)sigil_as_ptr(args[0]);
833 const char *data = s->data;
834 size_t len = s->byte_length;
836 /* Skip trailing whitespace */
837 while (len > 0 && (data[len-1] == ' ' || data[len-1] == '\n' ||
838 data[len-1] == '\r' || data[len-1] == '\t')) {
839 len--;
840 }
842 if (len == 0) {
843 return sigil_make_bytevector(vm, 0);
844 }
846 /* Calculate output size: 3 * (len/4) */
847 size_t out_len = (len / 4) * 3;
848 if (len > 0 && data[len-1] == '=') out_len--;
849 if (len > 1 && data[len-2] == '=') out_len--;
851 unsigned char *output = malloc(out_len + 1);
852 if (!output) return SIGIL_FALSE;
854 /* TODO: Implement when mbedTLS is added
855 size_t olen;
856 int ret = mbedtls_base64_decode(output, out_len + 1, &olen,
857 (const unsigned char *)data, len);
858 if (ret != 0) {
859 free(output);
860 return SIGIL_FALSE;
861 }
862 Value result = sigil_make_bytevector(vm, output, olen);
863 */
865 /* Simple base64 decode implementation */
866 static const unsigned char base64_table[256] = {
867 ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5,
868 ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11,
869 ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17,
870 ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
871 ['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29,
872 ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35,
873 ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41,
874 ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47,
875 ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53,
876 ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
877 ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
878 };
880 size_t i = 0, j = 0;
881 unsigned char array4[4], array3[3];
882 int k = 0;
884 while (i < len) {
885 if (data[i] == '=' || data[i] == '\n' || data[i] == '\r' ||
886 data[i] == ' ' || data[i] == '\t') {
887 i++;
888 continue;
889 }
891 array4[k++] = base64_table[(unsigned char)data[i++]];
893 if (k == 4) {
894 array3[0] = (array4[0] << 2) + ((array4[1] & 0x30) >> 4);
895 array3[1] = ((array4[1] & 0x0f) << 4) + ((array4[2] & 0x3c) >> 2);
896 array3[2] = ((array4[2] & 0x03) << 6) + array4[3];
898 for (k = 0; k < 3 && j < out_len; k++)
899 output[j++] = array3[k];
900 k = 0;
901 }
902 }
904 if (k) {
905 for (int m = k; m < 4; m++)
906 array4[m] = 0;
908 array3[0] = (array4[0] << 2) + ((array4[1] & 0x30) >> 4);
909 array3[1] = ((array4[1] & 0x0f) << 4) + ((array4[2] & 0x3c) >> 2);
911 for (int m = 0; m < k - 1 && j < out_len; m++)
912 output[j++] = array3[m];
913 }
915 Value result = sigil_make_bytevector(vm, j);
916 if (sigil_is_bytevector(result)) {
917 memcpy(sigil_bytevector_data(result), output, j);
918 }
919 free(output);
920 return result;
923/*
924 * random-bytes count -> bytevector
925 * Generate cryptographically secure random bytes.
926 * Uses mbedTLS CTR-DRBG with OS entropy.
927 */
928static Value native_random_bytes(SigilVM *vm, int argc, Value *args)
930 (void)argc;
932 if (!sigil_is_fixnum(args[0])) {
933 sigil__vm_error(vm, SIGIL_ERR_TYPE, "random-bytes: expected integer");
934 return SIGIL_UNDEFINED;
935 }
937 int64_t count = sigil_as_fixnum(args[0]);
938 if (count < 0) {
939 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "random-bytes: count must be non-negative");
940 return SIGIL_UNDEFINED;
941 }
942 if (count > 65536) {
943 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "random-bytes: count exceeds maximum (65536)");
944 return SIGIL_UNDEFINED;
945 }
947 if (ensure_rng_initialized() != 0) {
948 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "random-bytes: failed to initialize RNG");
949 return SIGIL_UNDEFINED;
950 }
952 Value bv = sigil_make_bytevector(vm, (size_t)count);
953 if (bv == SIGIL_UNDEFINED) return bv;
955 unsigned char *data = sigil_bytevector_data(bv);
956 int ret = mbedtls_ctr_drbg_random(&ctr_drbg_ctx, data, (size_t)count);
957 if (ret != 0) {
958 sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "random-bytes: RNG failed");
959 return SIGIL_UNDEFINED;
960 }
962 return bv;
965/* ===========================================================
966 * mbedTLS MPI — Big Integer Arithmetic
967 *
968 * Exposes mbedtls_mpi for arbitrary-precision integer operations
969 * beyond the 63-bit fixnum range. Bytevectors in big-endian.
970 * Functions allocate mpi contexts internally; no GC-visible handles.
971 * =========================================================== */
973static int mpi_read_bv(mbedtls_mpi *X, Value bv_val)
975 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(bv_val);
976 return mbedtls_mpi_read_binary(X, bv->data, bv->length);
979static Value mpi_write_bv(SigilVM *vm, mbedtls_mpi *X, size_t size)
981 Value result = sigil_make_bytevector(vm, size);
982 if (!sigil_is_bytevector(result)) return SIGIL_FALSE;
983 int ret = mbedtls_mpi_write_binary(X, sigil_bytevector_data(result), size);
984 if (ret != 0) return SIGIL_FALSE;
985 return result;
988/* MPI_BINOP generates add, sub, mul — the only difference is the op. */
989#define MPI_BINOP(method_name, op_fn) \
990static Value native_mpi_##method_name(SigilVM *vm, int argc, Value *args) \
991{ \
992 (void)argc; \
993 if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) { \
994 sigil__vm_error(vm, SIGIL_ERR_TYPE, \
995 "mpi-" #method_name ": expected bytevectors"); \
996 return SIGIL_UNDEFINED; \
997 } \
998 if (!sigil_is_fixnum(args[2])) { \
999 sigil__vm_error(vm, SIGIL_ERR_TYPE, \
1000 "mpi-" #method_name ": expected integer size"); \
1001 return SIGIL_UNDEFINED; \
1002 } \
1003 size_t size = (size_t)sigil_as_fixnum(args[2]); \
1004 mbedtls_mpi A, B, R; \
1005 mbedtls_mpi_init(&A); mbedtls_mpi_init(&B); mbedtls_mpi_init(&R); \
1006 Value result = SIGIL_FALSE; \
1007 if (mpi_read_bv(&A, args[0]) != 0) goto cleanup; \
1008 if (mpi_read_bv(&B, args[1]) != 0) goto cleanup; \
1009 if (op_fn(&R, &A, &B) != 0) goto cleanup; \
1010 result = mpi_write_bv(vm, &R, size); \
1011cleanup: \
1012 mbedtls_mpi_free(&R); mbedtls_mpi_free(&B); mbedtls_mpi_free(&A); \
1013 return result; \
1016MPI_BINOP(add, mbedtls_mpi_add_mpi)
1017MPI_BINOP(sub, mbedtls_mpi_sub_mpi)
1018MPI_BINOP(mul, mbedtls_mpi_mul_mpi)
1020#undef MPI_BINOP
1023 * mpi-div a-bv b-bv size -> (cons quotient-bv remainder-bv)
1024 */
1025static Value native_mpi_div(SigilVM *vm, int argc, Value *args)
1027 (void)argc;
1029 if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) {
1030 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-div: expected bytevectors");
1031 return SIGIL_UNDEFINED;
1033 if (!sigil_is_fixnum(args[2])) {
1034 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-div: expected integer size");
1035 return SIGIL_UNDEFINED;
1038 size_t size = (size_t)sigil_as_fixnum(args[2]);
1040 mbedtls_mpi A, B, Q, R;
1041 mbedtls_mpi_init(&A); mbedtls_mpi_init(&B);
1042 mbedtls_mpi_init(&Q); mbedtls_mpi_init(&R);
1044 Value result = SIGIL_FALSE;
1046 if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
1047 if (mpi_read_bv(&B, args[1]) != 0) goto cleanup;
1048 if (mbedtls_mpi_div_mpi(&Q, &R, &A, &B) != 0) goto cleanup;
1050 Value q_bv = mpi_write_bv(vm, &Q, size);
1051 Value r_bv = mpi_write_bv(vm, &R, size);
1052 if (!sigil_is_bytevector(q_bv) || !sigil_is_bytevector(r_bv)) goto cleanup;
1054 result = sigil_cons(vm, q_bv, r_bv);
1056cleanup:
1057 mbedtls_mpi_free(&R); mbedtls_mpi_free(&Q);
1058 mbedtls_mpi_free(&B); mbedtls_mpi_free(&A);
1059 return result;
1063 * mpi-mod a-bv n-bv size -> bytevector
1064 */
1065static Value native_mpi_mod(SigilVM *vm, int argc, Value *args)
1067 (void)argc;
1069 if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) {
1070 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-mod: expected bytevectors");
1071 return SIGIL_UNDEFINED;
1073 if (!sigil_is_fixnum(args[2])) {
1074 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-mod: expected integer size");
1075 return SIGIL_UNDEFINED;
1078 size_t size = (size_t)sigil_as_fixnum(args[2]);
1080 mbedtls_mpi A, N, R;
1081 mbedtls_mpi_init(&A); mbedtls_mpi_init(&N); mbedtls_mpi_init(&R);
1083 Value result = SIGIL_FALSE;
1085 if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
1086 if (mpi_read_bv(&N, args[1]) != 0) goto cleanup;
1087 if (mbedtls_mpi_mod_mpi(&R, &A, &N) != 0) goto cleanup;
1089 result = mpi_write_bv(vm, &R, size);
1091cleanup:
1092 mbedtls_mpi_free(&R); mbedtls_mpi_free(&N); mbedtls_mpi_free(&A);
1093 return result;
1097 * mpi-mod-add a-bv b-bv n-bv size -> bytevector
1098 * (a + b) mod n
1099 */
1100static Value native_mpi_mod_add(SigilVM *vm, int argc, Value *args)
1102 (void)argc;
1104 if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])
1105 || !sigil_is_bytevector(args[2])) {
1106 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-mod-add: expected bytevectors");
1107 return SIGIL_UNDEFINED;
1109 if (!sigil_is_fixnum(args[3])) {
1110 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-mod-add: expected integer size");
1111 return SIGIL_UNDEFINED;
1114 size_t size = (size_t)sigil_as_fixnum(args[3]);
1116 mbedtls_mpi A, B, N, R;
1117 mbedtls_mpi_init(&A); mbedtls_mpi_init(&B);
1118 mbedtls_mpi_init(&N); mbedtls_mpi_init(&R);
1120 Value result = SIGIL_FALSE;
1122 if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
1123 if (mpi_read_bv(&B, args[1]) != 0) goto cleanup;
1124 if (mpi_read_bv(&N, args[2]) != 0) goto cleanup;
1125 if (mbedtls_mpi_add_mpi(&R, &A, &B) != 0) goto cleanup;
1126 if (mbedtls_mpi_mod_mpi(&R, &R, &N) != 0) goto cleanup;
1128 result = mpi_write_bv(vm, &R, size);
1130cleanup:
1131 mbedtls_mpi_free(&R); mbedtls_mpi_free(&N);
1132 mbedtls_mpi_free(&B); mbedtls_mpi_free(&A);
1133 return result;
1137 * mpi-inv-mod a-bv n-bv size -> bytevector | #f
1138 */
1139static Value native_mpi_inv_mod(SigilVM *vm, int argc, Value *args)
1141 (void)argc;
1143 if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) {
1144 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-inv-mod: expected bytevectors");
1145 return SIGIL_UNDEFINED;
1147 if (!sigil_is_fixnum(args[2])) {
1148 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-inv-mod: expected integer size");
1149 return SIGIL_UNDEFINED;
1152 size_t size = (size_t)sigil_as_fixnum(args[2]);
1154 mbedtls_mpi A, N, R;
1155 mbedtls_mpi_init(&A); mbedtls_mpi_init(&N); mbedtls_mpi_init(&R);
1157 Value result = SIGIL_FALSE;
1159 if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
1160 if (mpi_read_bv(&N, args[1]) != 0) goto cleanup;
1161 if (mbedtls_mpi_inv_mod(&R, &A, &N) != 0) goto cleanup;
1163 result = mpi_write_bv(vm, &R, size);
1165cleanup:
1166 mbedtls_mpi_free(&R); mbedtls_mpi_free(&N); mbedtls_mpi_free(&A);
1167 return result;
1171 * mpi-cmp a-bv b-bv -> fixnum (-1, 0, or 1)
1172 */
1173static Value native_mpi_cmp(SigilVM *vm, int argc, Value *args)
1175 (void)argc;
1177 if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) {
1178 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-cmp: expected bytevectors");
1179 return SIGIL_UNDEFINED;
1182 mbedtls_mpi A, B;
1183 mbedtls_mpi_init(&A); mbedtls_mpi_init(&B);
1185 if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
1186 if (mpi_read_bv(&B, args[1]) != 0) goto cleanup;
1188 int cmp = mbedtls_mpi_cmp_mpi(&A, &B);
1189 mbedtls_mpi_free(&B); mbedtls_mpi_free(&A);
1190 return sigil_fixnum(cmp);
1192cleanup:
1193 mbedtls_mpi_free(&B); mbedtls_mpi_free(&A);
1194 return SIGIL_FALSE;
1198 * mpi-is-zero? a-bv -> boolean
1199 */
1200static Value native_mpi_is_zero(SigilVM *vm, int argc, Value *args)
1202 (void)argc;
1204 if (!sigil_is_bytevector(args[0])) {
1205 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-is-zero?: expected bytevector");
1206 return SIGIL_UNDEFINED;
1209 mbedtls_mpi A;
1210 mbedtls_mpi_init(&A);
1212 if (mpi_read_bv(&A, args[0]) != 0) {
1213 mbedtls_mpi_free(&A);
1214 return SIGIL_FALSE;
1217 int result = (mbedtls_mpi_cmp_int(&A, 0) == 0);
1218 mbedtls_mpi_free(&A);
1219 return result ? SIGIL_TRUE : SIGIL_FALSE;
1223 * mpi-shift-l a-bv bits size -> bytevector
1224 */
1225static Value native_mpi_shift_l(SigilVM *vm, int argc, Value *args)
1227 (void)argc;
1229 if (!sigil_is_bytevector(args[0])) {
1230 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-shift-l: expected bytevector");
1231 return SIGIL_UNDEFINED;
1233 if (!sigil_is_fixnum(args[1]) || !sigil_is_fixnum(args[2])) {
1234 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-shift-l: expected integer arguments");
1235 return SIGIL_UNDEFINED;
1238 int64_t bits_in = sigil_as_fixnum(args[1]);
1239 int64_t size_in = sigil_as_fixnum(args[2]);
1240 if (bits_in < 0 || size_in < 0) {
1241 sigil__vm_error(vm, SIGIL_ERR_TYPE,
1242 "mpi-shift-l: bits and size must be non-negative");
1243 return SIGIL_UNDEFINED;
1246 size_t bits = (size_t)bits_in;
1247 size_t size = (size_t)size_in;
1249 mbedtls_mpi A;
1250 mbedtls_mpi_init(&A);
1252 if (mpi_read_bv(&A, args[0]) != 0) {
1253 mbedtls_mpi_free(&A);
1254 return SIGIL_FALSE;
1257 if (mbedtls_mpi_shift_l(&A, bits) != 0) {
1258 mbedtls_mpi_free(&A);
1259 return SIGIL_FALSE;
1262 Value result = mpi_write_bv(vm, &A, size);
1263 mbedtls_mpi_free(&A);
1264 return result;
1268 * mpi-shift-r a-bv bits -> bytevector
1269 */
1270static Value native_mpi_shift_r(SigilVM *vm, int argc, Value *args)
1272 (void)argc;
1274 if (!sigil_is_bytevector(args[0])) {
1275 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-shift-r: expected bytevector");
1276 return SIGIL_UNDEFINED;
1278 if (!sigil_is_fixnum(args[1])) {
1279 sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-shift-r: expected integer");
1280 return SIGIL_UNDEFINED;
1283 int64_t bits_in = sigil_as_fixnum(args[1]);
1284 if (bits_in < 0) {
1285 sigil__vm_error(vm, SIGIL_ERR_TYPE,
1286 "mpi-shift-r: bits must be non-negative");
1287 return SIGIL_UNDEFINED;
1290 size_t bits = (size_t)bits_in;
1292 mbedtls_mpi A;
1293 mbedtls_mpi_init(&A);
1295 if (mpi_read_bv(&A, args[0]) != 0) {
1296 mbedtls_mpi_free(&A);
1297 return SIGIL_FALSE;
1300 if (mbedtls_mpi_shift_r(&A, bits) != 0) {
1301 mbedtls_mpi_free(&A);
1302 return SIGIL_FALSE;
1305 SigilBytevector *in_bv = (SigilBytevector *)sigil_as_ptr(args[0]);
1306 Value result = mpi_write_bv(vm, &A, in_bv->length);
1307 mbedtls_mpi_free(&A);
1308 return result;
1311/* ===========================================================
1312 * ECDSA P-256, ECDH P-256, AES-128-GCM
1314 * Used by Web Push (RFC 8291 / RFC 8292): VAPID JWT signs with
1315 * ES256 (ECDSA P-256 + SHA-256), payload encryption derives
1316 * shared secret via ECDH P-256 and seals with AES-128-GCM.
1317 * =========================================================== */
1319#define ECDSA_P256_PRIV_LEN 32
1320#define ECDSA_P256_PUB_LEN 65 /* Uncompressed: 0x04 || X(32) || Y(32) */
1321#define ECDSA_P256_SIG_LEN 64 /* JOSE format: r(32) || s(32) */
1322#define ECDH_P256_SECRET_LEN 32
1325 * Extract bytes from a string-or-bytevector argument.
1326 * On type mismatch raises a VM error and returns 0.
1327 */
1328static int crypto_read_bytes(SigilVM *vm, Value v, const char *fn,
1329 const unsigned char **out_data, size_t *out_len)
1331 if (sigil_is_string(v)) {
1332 SigilString *s = (SigilString *)sigil_as_ptr(v);
1333 *out_data = (const unsigned char *)s->data;
1334 *out_len = s->byte_length;
1335 return 1;
1337 if (sigil_is_bytevector(v)) {
1338 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(v);
1339 *out_data = bv->data;
1340 *out_len = bv->length;
1341 return 1;
1343 sigil__vm_error(vm, SIGIL_ERR_TYPE,
1344 "expected string or bytevector argument");
1345 (void)fn;
1346 return 0;
1350 * Extract bytes from a bytevector-only argument with a required length.
1351 * Returns 0 on type mismatch or wrong length (raises VM error).
1352 */
1353static int crypto_read_bv_exact(SigilVM *vm, Value v, size_t want,
1354 const char *what,
1355 const unsigned char **out_data)
1357 if (!sigil_is_bytevector(v)) {
1358 sigil__vm_error(vm, SIGIL_ERR_TYPE,
1359 "expected bytevector argument");
1360 (void)what;
1361 return 0;
1363 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(v);
1364 if (bv->length != want) {
1365 sigil__vm_error(vm, SIGIL_ERR_RUNTIME,
1366 "wrong bytevector length");
1367 return 0;
1369 *out_data = bv->data;
1370 return 1;
1374 * ecdsa-p256-generate-keypair -> (cons priv-bv-32 pub-bv-65)
1376 * Generates a fresh P-256 keypair. priv is the 32-byte big-endian
1377 * scalar; pub is the 65-byte uncompressed-point encoding suitable
1378 * for VAPID's `applicationServerKey` and for ECDH peer-key input.
1379 */
1380static Value native_ecdsa_p256_generate_keypair(SigilVM *vm, int argc, Value *args)
1382 (void)argc; (void)args;
1384 if (ensure_rng_initialized() != 0) {
1385 sigil__vm_error(vm, SIGIL_ERR_RUNTIME,
1386 "ecdsa-p256-generate-keypair: failed to init RNG");
1387 return SIGIL_UNDEFINED;
1390 mbedtls_ecp_group grp;
1391 mbedtls_mpi d;
1392 mbedtls_ecp_point Q;
1393 mbedtls_ecp_group_init(&grp);
1394 mbedtls_mpi_init(&d);
1395 mbedtls_ecp_point_init(&Q);
1397 Value result = SIGIL_FALSE;
1398 int ret;
1399 unsigned char priv_buf[ECDSA_P256_PRIV_LEN];
1400 unsigned char pub_buf[ECDSA_P256_PUB_LEN];
1401 size_t pub_olen = 0;
1403 ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
1404 if (ret != 0) goto cleanup;
1406 ret = mbedtls_ecp_gen_keypair(&grp, &d, &Q,
1407 mbedtls_ctr_drbg_random, &ctr_drbg_ctx);
1408 if (ret != 0) goto cleanup;
1410 ret = mbedtls_mpi_write_binary(&d, priv_buf, ECDSA_P256_PRIV_LEN);
1411 if (ret != 0) goto cleanup;
1413 ret = mbedtls_ecp_point_write_binary(&grp, &Q,
1414 MBEDTLS_ECP_PF_UNCOMPRESSED,
1415 &pub_olen, pub_buf,
1416 ECDSA_P256_PUB_LEN);
1417 if (ret != 0 || pub_olen != ECDSA_P256_PUB_LEN) goto cleanup;
1419 Value priv_bv = sigil_make_bytevector(vm, ECDSA_P256_PRIV_LEN);
1420 Value pub_bv = sigil_make_bytevector(vm, ECDSA_P256_PUB_LEN);
1421 if (!sigil_is_bytevector(priv_bv) || !sigil_is_bytevector(pub_bv)) {
1422 goto cleanup;
1424 memcpy(sigil_bytevector_data(priv_bv), priv_buf, ECDSA_P256_PRIV_LEN);
1425 memcpy(sigil_bytevector_data(pub_bv), pub_buf, ECDSA_P256_PUB_LEN);
1427 result = sigil_cons(vm, priv_bv, pub_bv);
1429cleanup:
1430 /* Wipe stack copies of private material before unwinding. */
1431 memset(priv_buf, 0, sizeof(priv_buf));
1432 mbedtls_ecp_point_free(&Q);
1433 mbedtls_mpi_free(&d);
1434 mbedtls_ecp_group_free(&grp);
1435 return result;
1439 * ecdsa-p256-sign priv-bv message -> sig-bv-64 | #f
1441 * Hashes `message` with SHA-256, signs with ECDSA P-256 using the
1442 * provided 32-byte private scalar, and returns the JOSE-format
1443 * 64-byte signature (r || s, each 32 bytes big-endian). This is
1444 * the format VAPID JWT (ES256) wants — NOT DER. Returns #f if
1445 * the private key is invalid.
1446 */
1447static Value native_ecdsa_p256_sign(SigilVM *vm, int argc, Value *args)
1449 (void)argc;
1451 const unsigned char *priv_data;
1452 if (!crypto_read_bv_exact(vm, args[0], ECDSA_P256_PRIV_LEN,
1453 "private key", &priv_data)) {
1454 return SIGIL_UNDEFINED;
1457 const unsigned char *msg_data;
1458 size_t msg_len;
1459 if (!crypto_read_bytes(vm, args[1], "ecdsa-p256-sign",
1460 &msg_data, &msg_len)) {
1461 return SIGIL_UNDEFINED;
1464 if (ensure_rng_initialized() != 0) {
1465 sigil__vm_error(vm, SIGIL_ERR_RUNTIME,
1466 "ecdsa-p256-sign: failed to init RNG");
1467 return SIGIL_UNDEFINED;
1470 /* SHA-256 the message into a 32-byte digest. */
1471 unsigned char digest[32];
1472 mbedtls_sha256_context sha;
1473 mbedtls_sha256_init(&sha);
1474 mbedtls_sha256_starts(&sha, 0);
1475 mbedtls_sha256_update(&sha, msg_data, msg_len);
1476 mbedtls_sha256_finish(&sha, digest);
1477 mbedtls_sha256_free(&sha);
1479 mbedtls_ecp_group grp;
1480 mbedtls_mpi d, r, s;
1481 mbedtls_ecp_group_init(&grp);
1482 mbedtls_mpi_init(&d);
1483 mbedtls_mpi_init(&r);
1484 mbedtls_mpi_init(&s);
1486 Value result = SIGIL_FALSE;
1487 int ret;
1488 unsigned char sig_buf[ECDSA_P256_SIG_LEN];
1490 ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
1491 if (ret != 0) goto cleanup;
1493 ret = mbedtls_mpi_read_binary(&d, priv_data, ECDSA_P256_PRIV_LEN);
1494 if (ret != 0) goto cleanup;
1496 /* Reject scalars outside [1, n-1] — mbedTLS doesn't validate
1497 * for sign(); a zero d would silently produce an invalid sig. */
1498 if (mbedtls_ecp_check_privkey(&grp, &d) != 0) goto cleanup;
1500 ret = mbedtls_ecdsa_sign(&grp, &r, &s, &d,
1501 digest, sizeof(digest),
1502 mbedtls_ctr_drbg_random, &ctr_drbg_ctx);
1503 if (ret != 0) goto cleanup;
1505 ret = mbedtls_mpi_write_binary(&r, sig_buf, 32);
1506 if (ret != 0) goto cleanup;
1507 ret = mbedtls_mpi_write_binary(&s, sig_buf + 32, 32);
1508 if (ret != 0) goto cleanup;
1510 result = sigil_make_bytevector(vm, ECDSA_P256_SIG_LEN);
1511 if (sigil_is_bytevector(result)) {
1512 memcpy(sigil_bytevector_data(result), sig_buf, ECDSA_P256_SIG_LEN);
1515cleanup:
1516 mbedtls_mpi_free(&s);
1517 mbedtls_mpi_free(&r);
1518 mbedtls_mpi_free(&d);
1519 mbedtls_ecp_group_free(&grp);
1520 return result;
1524 * ecdsa-p256-verify pub-bv message sig-bv -> boolean
1526 * Returns #t when the JOSE-format 64-byte sig validates against
1527 * the message under the given 65-byte uncompressed-point public key,
1528 * otherwise #f. Hashes the message with SHA-256 internally so the
1529 * caller passes the raw message body (matches sign's input shape).
1530 */
1531static Value native_ecdsa_p256_verify(SigilVM *vm, int argc, Value *args)
1533 (void)argc;
1535 const unsigned char *pub_data;
1536 if (!crypto_read_bv_exact(vm, args[0], ECDSA_P256_PUB_LEN,
1537 "public key", &pub_data)) {
1538 return SIGIL_UNDEFINED;
1541 const unsigned char *msg_data;
1542 size_t msg_len;
1543 if (!crypto_read_bytes(vm, args[1], "ecdsa-p256-verify",
1544 &msg_data, &msg_len)) {
1545 return SIGIL_UNDEFINED;
1548 const unsigned char *sig_data;
1549 if (!crypto_read_bv_exact(vm, args[2], ECDSA_P256_SIG_LEN,
1550 "signature", &sig_data)) {
1551 return SIGIL_UNDEFINED;
1554 unsigned char digest[32];
1555 mbedtls_sha256_context sha;
1556 mbedtls_sha256_init(&sha);
1557 mbedtls_sha256_starts(&sha, 0);
1558 mbedtls_sha256_update(&sha, msg_data, msg_len);
1559 mbedtls_sha256_finish(&sha, digest);
1560 mbedtls_sha256_free(&sha);
1562 mbedtls_ecp_group grp;
1563 mbedtls_ecp_point Q;
1564 mbedtls_mpi r, s;
1565 mbedtls_ecp_group_init(&grp);
1566 mbedtls_ecp_point_init(&Q);
1567 mbedtls_mpi_init(&r);
1568 mbedtls_mpi_init(&s);
1570 Value result = SIGIL_FALSE;
1571 int ret;
1573 ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
1574 if (ret != 0) goto cleanup;
1576 ret = mbedtls_ecp_point_read_binary(&grp, &Q, pub_data, ECDSA_P256_PUB_LEN);
1577 if (ret != 0) goto cleanup;
1579 /* Reject points off the curve / at infinity to avoid invalid-curve
1580 * attacks — point_read_binary parses but does not validate. */
1581 if (mbedtls_ecp_check_pubkey(&grp, &Q) != 0) goto cleanup;
1583 ret = mbedtls_mpi_read_binary(&r, sig_data, 32);
1584 if (ret != 0) goto cleanup;
1585 ret = mbedtls_mpi_read_binary(&s, sig_data + 32, 32);
1586 if (ret != 0) goto cleanup;
1588 ret = mbedtls_ecdsa_verify(&grp, digest, sizeof(digest), &Q, &r, &s);
1589 result = (ret == 0) ? SIGIL_TRUE : SIGIL_FALSE;
1591cleanup:
1592 mbedtls_mpi_free(&s);
1593 mbedtls_mpi_free(&r);
1594 mbedtls_ecp_point_free(&Q);
1595 mbedtls_ecp_group_free(&grp);
1596 return result;
1600 * ecdh-p256-shared-secret priv-bv peer-pub-bv -> bytevector(32) | #f
1602 * ECDH on P-256: derives the 32-byte big-endian X coordinate of
1603 * (priv * peer_pub). The shared secret is the raw X coordinate per
1604 * RFC 8291 (Web Push uses this directly as the IKM input to HKDF).
1605 * Validates that peer-pub-bv is a valid point on the curve before
1606 * computing — invalid-curve attack defence.
1607 */
1608static Value native_ecdh_p256_shared_secret(SigilVM *vm, int argc, Value *args)
1610 (void)argc;
1612 const unsigned char *priv_data;
1613 if (!crypto_read_bv_exact(vm, args[0], ECDSA_P256_PRIV_LEN,
1614 "private key", &priv_data)) {
1615 return SIGIL_UNDEFINED;
1618 const unsigned char *peer_data;
1619 if (!crypto_read_bv_exact(vm, args[1], ECDSA_P256_PUB_LEN,
1620 "peer public key", &peer_data)) {
1621 return SIGIL_UNDEFINED;
1624 if (ensure_rng_initialized() != 0) {
1625 sigil__vm_error(vm, SIGIL_ERR_RUNTIME,
1626 "ecdh-p256-shared-secret: failed to init RNG");
1627 return SIGIL_UNDEFINED;
1630 mbedtls_ecp_group grp;
1631 mbedtls_mpi d, z;
1632 mbedtls_ecp_point peer_Q;
1633 mbedtls_ecp_group_init(&grp);
1634 mbedtls_mpi_init(&d);
1635 mbedtls_mpi_init(&z);
1636 mbedtls_ecp_point_init(&peer_Q);
1638 Value result = SIGIL_FALSE;
1639 int ret;
1640 unsigned char secret_buf[ECDH_P256_SECRET_LEN];
1642 ret = mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
1643 if (ret != 0) goto cleanup;
1645 ret = mbedtls_mpi_read_binary(&d, priv_data, ECDSA_P256_PRIV_LEN);
1646 if (ret != 0) goto cleanup;
1647 if (mbedtls_ecp_check_privkey(&grp, &d) != 0) goto cleanup;
1649 ret = mbedtls_ecp_point_read_binary(&grp, &peer_Q, peer_data,
1650 ECDSA_P256_PUB_LEN);
1651 if (ret != 0) goto cleanup;
1652 if (mbedtls_ecp_check_pubkey(&grp, &peer_Q) != 0) goto cleanup;
1654 ret = mbedtls_ecdh_compute_shared(&grp, &z, &peer_Q, &d,
1655 mbedtls_ctr_drbg_random, &ctr_drbg_ctx);
1656 if (ret != 0) goto cleanup;
1658 ret = mbedtls_mpi_write_binary(&z, secret_buf, ECDH_P256_SECRET_LEN);
1659 if (ret != 0) goto cleanup;
1661 result = sigil_make_bytevector(vm, ECDH_P256_SECRET_LEN);
1662 if (sigil_is_bytevector(result)) {
1663 memcpy(sigil_bytevector_data(result), secret_buf,
1664 ECDH_P256_SECRET_LEN);
1667cleanup:
1668 memset(secret_buf, 0, sizeof(secret_buf));
1669 mbedtls_ecp_point_free(&peer_Q);
1670 mbedtls_mpi_free(&z);
1671 mbedtls_mpi_free(&d);
1672 mbedtls_ecp_group_free(&grp);
1673 return result;
1677 * aes-128-gcm-encrypt key-bv-16 iv-bv-12 aad plaintext
1678 * -> (cons ciphertext-bv tag-bv-16) | #f
1680 * AAD and plaintext accept string or bytevector. Ciphertext length
1681 * matches plaintext length; tag is always 16 bytes (full GCM tag).
1682 * IV must be 12 bytes (the AEAD-recommended length, and what
1683 * RFC 8291 § 3 prescribes).
1684 */
1685static Value native_aes_128_gcm_encrypt(SigilVM *vm, int argc, Value *args)
1687 (void)argc;
1689 const unsigned char *key_data;
1690 if (!crypto_read_bv_exact(vm, args[0], 16, "key", &key_data)) {
1691 return SIGIL_UNDEFINED;
1694 const unsigned char *iv_data;
1695 if (!crypto_read_bv_exact(vm, args[1], 12, "iv", &iv_data)) {
1696 return SIGIL_UNDEFINED;
1699 const unsigned char *aad_data;
1700 size_t aad_len;
1701 if (!crypto_read_bytes(vm, args[2], "aes-128-gcm-encrypt aad",
1702 &aad_data, &aad_len)) {
1703 return SIGIL_UNDEFINED;
1706 const unsigned char *pt_data;
1707 size_t pt_len;
1708 if (!crypto_read_bytes(vm, args[3], "aes-128-gcm-encrypt plaintext",
1709 &pt_data, &pt_len)) {
1710 return SIGIL_UNDEFINED;
1713 mbedtls_gcm_context ctx;
1714 mbedtls_gcm_init(&ctx);
1716 Value result = SIGIL_FALSE;
1717 unsigned char tag_buf[16];
1718 unsigned char *ct_buf = NULL;
1720 int ret = mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key_data, 128);
1721 if (ret != 0) goto cleanup;
1723 ct_buf = (pt_len == 0) ? NULL : malloc(pt_len);
1724 if (pt_len != 0 && !ct_buf) goto cleanup;
1726 ret = mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_ENCRYPT,
1727 pt_len,
1728 iv_data, 12,
1729 aad_data, aad_len,
1730 pt_data, ct_buf,
1731 sizeof(tag_buf), tag_buf);
1732 if (ret != 0) goto cleanup;
1734 Value ct_bv = sigil_make_bytevector(vm, pt_len);
1735 Value tag_bv = sigil_make_bytevector(vm, sizeof(tag_buf));
1736 if (!sigil_is_bytevector(ct_bv) || !sigil_is_bytevector(tag_bv)) {
1737 goto cleanup;
1739 if (pt_len > 0) memcpy(sigil_bytevector_data(ct_bv), ct_buf, pt_len);
1740 memcpy(sigil_bytevector_data(tag_bv), tag_buf, sizeof(tag_buf));
1742 result = sigil_cons(vm, ct_bv, tag_bv);
1744cleanup:
1745 if (ct_buf) free(ct_buf);
1746 mbedtls_gcm_free(&ctx);
1747 return result;
1751 * aes-128-gcm-decrypt key-bv-16 iv-bv-12 aad ciphertext-bv tag-bv-16
1752 * -> plaintext-bv | #f
1754 * Returns #f on auth-tag mismatch (the classic AEAD failure). Used
1755 * by the test path; production WEBPUSH only encrypts.
1756 */
1757static Value native_aes_128_gcm_decrypt(SigilVM *vm, int argc, Value *args)
1759 (void)argc;
1761 const unsigned char *key_data;
1762 if (!crypto_read_bv_exact(vm, args[0], 16, "key", &key_data)) {
1763 return SIGIL_UNDEFINED;
1766 const unsigned char *iv_data;
1767 if (!crypto_read_bv_exact(vm, args[1], 12, "iv", &iv_data)) {
1768 return SIGIL_UNDEFINED;
1771 const unsigned char *aad_data;
1772 size_t aad_len;
1773 if (!crypto_read_bytes(vm, args[2], "aes-128-gcm-decrypt aad",
1774 &aad_data, &aad_len)) {
1775 return SIGIL_UNDEFINED;
1778 if (!sigil_is_bytevector(args[3])) {
1779 sigil__vm_error(vm, SIGIL_ERR_TYPE,
1780 "aes-128-gcm-decrypt: ciphertext must be bytevector");
1781 return SIGIL_UNDEFINED;
1783 SigilBytevector *ct_bv_in = (SigilBytevector *)sigil_as_ptr(args[3]);
1784 const unsigned char *ct_data = ct_bv_in->data;
1785 size_t ct_len = ct_bv_in->length;
1787 const unsigned char *tag_data;
1788 if (!crypto_read_bv_exact(vm, args[4], 16, "tag", &tag_data)) {
1789 return SIGIL_UNDEFINED;
1792 mbedtls_gcm_context ctx;
1793 mbedtls_gcm_init(&ctx);
1795 Value result = SIGIL_FALSE;
1796 unsigned char *pt_buf = NULL;
1798 int ret = mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key_data, 128);
1799 if (ret != 0) goto cleanup;
1801 pt_buf = (ct_len == 0) ? NULL : malloc(ct_len);
1802 if (ct_len != 0 && !pt_buf) goto cleanup;
1804 ret = mbedtls_gcm_auth_decrypt(&ctx, ct_len,
1805 iv_data, 12,
1806 aad_data, aad_len,
1807 tag_data, 16,
1808 ct_data, pt_buf);
1809 if (ret != 0) goto cleanup;
1811 result = sigil_make_bytevector(vm, ct_len);
1812 if (sigil_is_bytevector(result) && ct_len > 0) {
1813 memcpy(sigil_bytevector_data(result), pt_buf, ct_len);
1816cleanup:
1817 if (pt_buf) {
1818 memset(pt_buf, 0, ct_len);
1819 free(pt_buf);
1821 mbedtls_gcm_free(&ctx);
1822 return result;
1827 * Helper macro for module-scoped registration with export
1828 */
1829#define REGISTER_AND_EXPORT(name, func, arity, doc) \
1830 sigil_module_register_native(vm, name, func, arity, doc); \
1831 sigil_module_export(vm, name)
1834 * Initialize the (sigil crypto) module.
1835 * This is called at VM startup.
1836 */
1837void sigil__init_sigil_crypto_module(SigilVM *vm)
1839 SigilModule *module = sigil_begin_module(vm, "(sigil crypto)");
1840 if (!module) return;
1842 /* Hashing */
1843 REGISTER_AND_EXPORT("sha1", native_sha1,
1844 SIGIL_ARITY_EXACT(1), "Compute SHA-1 hash (returns bytevector)");
1845 REGISTER_AND_EXPORT("sha256", native_sha256,
1846 SIGIL_ARITY_EXACT(1), "Compute SHA-256 hash (hex string)");
1847 REGISTER_AND_EXPORT("sha256-bytes", native_sha256_bytes,
1848 SIGIL_ARITY_EXACT(1), "Compute SHA-256 hash (bytevector)");
1849 REGISTER_AND_EXPORT("ripemd160", native_ripemd160,
1850 SIGIL_ARITY_EXACT(1), "Compute RIPEMD-160 hash (returns bytevector)");
1852 /* HMAC */
1853 REGISTER_AND_EXPORT("hmac-sha256", native_hmac_sha256,
1854 SIGIL_ARITY_EXACT(2), "Compute HMAC-SHA256 (hex string)");
1855 REGISTER_AND_EXPORT("hmac-sha256-bytes", native_hmac_sha256_bytes,
1856 SIGIL_ARITY_EXACT(2), "Compute HMAC-SHA256 (bytevector)");
1857 REGISTER_AND_EXPORT("hmac-sha512-bytes", native_hmac_sha512_bytes,
1858 SIGIL_ARITY_EXACT(2), "Compute HMAC-SHA512 (bytevector)");
1859 REGISTER_AND_EXPORT("hmac-sha1", native_hmac_sha1,
1860 SIGIL_ARITY_EXACT(2), "Compute HMAC-SHA1 (returns bytevector)");
1862 /* Key Derivation */
1863 REGISTER_AND_EXPORT("pbkdf2-sha1", native_pbkdf2_sha1,
1864 SIGIL_ARITY_EXACT(4), "Derive key using PBKDF2-HMAC-SHA1");
1865 REGISTER_AND_EXPORT("pbkdf2-sha256", native_pbkdf2_sha256,
1866 SIGIL_ARITY_EXACT(4), "Derive key using PBKDF2-HMAC-SHA256");
1867 REGISTER_AND_EXPORT("pbkdf2-sha512", native_pbkdf2_sha512,
1868 SIGIL_ARITY_EXACT(4), "Derive key using PBKDF2-HMAC-SHA512");
1870 /* Base64 */
1871 REGISTER_AND_EXPORT("base64-encode", native_base64_encode,
1872 SIGIL_ARITY_EXACT(1), "Encode data as base64");
1873 REGISTER_AND_EXPORT("base64-decode", native_base64_decode,
1874 SIGIL_ARITY_EXACT(1), "Decode base64 string");
1876 /* Random */
1877 REGISTER_AND_EXPORT("random-bytes", native_random_bytes,
1878 SIGIL_ARITY_EXACT(1), "Generate secure random bytes");
1880 /* mbedTLS MPI — Big Integer Arithmetic */
1881 REGISTER_AND_EXPORT("mpi-add", native_mpi_add,
1882 SIGIL_ARITY_EXACT(3), "Add two bytevectors as big integers");
1883 REGISTER_AND_EXPORT("mpi-sub", native_mpi_sub,
1884 SIGIL_ARITY_EXACT(3), "Subtract two bytevectors as big integers");
1885 REGISTER_AND_EXPORT("mpi-mul", native_mpi_mul,
1886 SIGIL_ARITY_EXACT(3), "Multiply two bytevectors as big integers");
1887 REGISTER_AND_EXPORT("mpi-div", native_mpi_div,
1888 SIGIL_ARITY_EXACT(3), "Divide two bytevectors: returns (cons quotient remainder)");
1889 REGISTER_AND_EXPORT("mpi-mod", native_mpi_mod,
1890 SIGIL_ARITY_EXACT(3), "Modulo of two bytevectors as big integers");
1891 REGISTER_AND_EXPORT("mpi-mod-add", native_mpi_mod_add,
1892 SIGIL_ARITY_EXACT(4), "(a + b) mod n");
1893 REGISTER_AND_EXPORT("mpi-inv-mod", native_mpi_inv_mod,
1894 SIGIL_ARITY_EXACT(3), "Modular inverse of a modulo n");
1895 REGISTER_AND_EXPORT("mpi-cmp", native_mpi_cmp,
1896 SIGIL_ARITY_EXACT(2), "Compare two bytevectors: returns -1, 0, or 1");
1897 REGISTER_AND_EXPORT("mpi-is-zero?", native_mpi_is_zero,
1898 SIGIL_ARITY_EXACT(1), "Test if bytevectored big integer is zero");
1899 REGISTER_AND_EXPORT("mpi-shift-l", native_mpi_shift_l,
1900 SIGIL_ARITY_EXACT(3), "Left-shift bytevectored big integer");
1901 REGISTER_AND_EXPORT("mpi-shift-r", native_mpi_shift_r,
1902 SIGIL_ARITY_EXACT(2), "Right-shift bytevectored big integer");
1904 /* ECDSA P-256 (VAPID JWT signing) */
1905 REGISTER_AND_EXPORT("ecdsa-p256-generate-keypair",
1906 native_ecdsa_p256_generate_keypair,
1907 SIGIL_ARITY_EXACT(0),
1908 "Generate ECDSA P-256 keypair: returns (cons priv-bv-32 pub-bv-65)");
1909 REGISTER_AND_EXPORT("ecdsa-p256-sign", native_ecdsa_p256_sign,
1910 SIGIL_ARITY_EXACT(2),
1911 "Sign message with ECDSA P-256+SHA-256, JOSE format (r||s, 64 bytes)");
1912 REGISTER_AND_EXPORT("ecdsa-p256-verify", native_ecdsa_p256_verify,
1913 SIGIL_ARITY_EXACT(3),
1914 "Verify ECDSA P-256+SHA-256 signature (JOSE format)");
1916 /* ECDH P-256 (Web Push shared-secret derivation) */
1917 REGISTER_AND_EXPORT("ecdh-p256-shared-secret",
1918 native_ecdh_p256_shared_secret,
1919 SIGIL_ARITY_EXACT(2),
1920 "ECDH P-256: 32-byte X coordinate of priv*peer-pub");
1922 /* AES-128-GCM (Web Push payload envelope) */
1923 REGISTER_AND_EXPORT("aes-128-gcm-encrypt", native_aes_128_gcm_encrypt,
1924 SIGIL_ARITY_EXACT(4),
1925 "AES-128-GCM encrypt: returns (cons ciphertext tag)");
1926 REGISTER_AND_EXPORT("aes-128-gcm-decrypt", native_aes_128_gcm_decrypt,
1927 SIGIL_ARITY_EXACT(5),
1928 "AES-128-GCM decrypt: returns plaintext or #f on auth fail");
1930 sigil_end_module(vm);
1933#undef REGISTER_AND_EXPORT
1935#endif /* !__EMSCRIPTEN__ */