Commit85fabc2aRecorded4 May 2026Repositorysigil-crypto

feat: add ripemd160, hmac-sha512-bytes, and mbedTLS MPI bignum primitives

Message

- Enable MBEDTLSRIPEMD160C in sigilmbedtlsconfig.h - Add ripemd160 hash (string/bytevector -> 20-byte bytevector) - Add hmac-sha512-bytes (key msg -> 64-byte MAC bytevector) - Add mpi-add, mpi-sub, mpi-mul, mpi-div, mpi-mod, mpi-mod-add, mpi-inv-mod, mpi-cmp, mpi-is-zero?, mpi-shift-l, mpi-shift-r - Deduplicate add/sub/mul with MPI_BINOP macro - Include RFC 4231 hmac-sha512 test vectors and RIPEMD-160 KATs

Changed
 native/crypto.c                       | 450 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/sigil/crypto.sgl                  | 126 +++++++++++++++++++++++++++++++++++++++++++
 test/test-crypto.sgl                  | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vendor/mbedtls/sigil_mbedtls_config.h |   1 +
 4 files changed, 750 insertions(+)
Diff
native/crypto.cmodified
@@ -31,6 +31,7 @@ void sigil__init_sigil_crypto_module(SigilVM *vm)
31
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"
@@ -213,6 +214,47 @@ static Value native_sha1(SigilVM *vm, int argc, Value *args)
214
return result;
215
}
216
+217
/*
+218
* ripemd160 data -> bytevector
+219
* Compute RIPEMD-160 hash of data (string or bytevector).
+220
* Returns 20-byte hash as bytevector.
+221
*/
+222
static Value native_ripemd160(SigilVM *vm, int argc, Value *args)
+223
{
+224
(void)argc;
+225
+226
const unsigned char *data;
+227
size_t len;
+228
+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
}
+241
+242
unsigned char hash[20];
+243
+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);
+250
+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;
+256
}
+257
258
/*
259
* hmac-sha256 key data -> hex-string
260
* Compute HMAC-SHA256 of data using the given key.
@@ -328,6 +370,60 @@ static Value native_hmac_sha256_bytes(SigilVM *vm, int argc, Value *args)
370
return result;
371
}
372
+373
/*
+374
* hmac-sha512-bytes key data -> bytevector
+375
* Like hmac-sha256-bytes but using SHA-512.
+376
*/
+377
static Value native_hmac_sha512_bytes(SigilVM *vm, int argc, Value *args)
+378
{
+379
(void)argc;
+380
+381
const unsigned char *key_data;
+382
size_t key_len;
+383
const unsigned char *msg_data;
+384
size_t msg_len;
+385
+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
}
+398
+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
}
+411
+412
unsigned char hmac[64];
+413
+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
}
+419
+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;
+425
}
+426
427
/*
428
* hmac-sha1 key data -> bytevector
429
* Compute HMAC-SHA1 of data using the given key.
@@ -785,6 +881,332 @@ static Value native_random_bytes(SigilVM *vm, int argc, Value *args)
881
return bv;
882
}
883
+884
/* ===========================================================
+885
* mbedTLS MPI — Big Integer Arithmetic
+886
*
+887
* Exposes mbedtls_mpi for arbitrary-precision integer operations
+888
* beyond the 63-bit fixnum range. Bytevectors in big-endian.
+889
* Functions allocate mpi contexts internally; no GC-visible handles.
+890
* =========================================================== */
+891
+892
static int mpi_read_bv(mbedtls_mpi *X, Value bv_val)
+893
{
+894
SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(bv_val);
+895
return mbedtls_mpi_read_binary(X, bv->data, bv->length);
+896
}
+897
+898
static Value mpi_write_bv(SigilVM *vm, mbedtls_mpi *X, size_t size)
+899
{
+900
Value result = sigil_make_bytevector(vm, size);
+901
if (!sigil_is_bytevector(result)) return SIGIL_FALSE;
+902
int ret = mbedtls_mpi_write_binary(X, sigil_bytevector_data(result), size);
+903
if (ret != 0) return SIGIL_FALSE;
+904
return result;
+905
}
+906
+907
/* MPI_BINOP generates add, sub, mul — the only difference is the op. */
+908
#define MPI_BINOP(method_name, op_fn) \
+909
static Value native_mpi_##method_name(SigilVM *vm, int argc, Value *args) \
+910
{ \
+911
(void)argc; \
+912
if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) { \
+913
sigil__vm_error(vm, SIGIL_ERR_TYPE, \
+914
"mpi-" #method_name ": expected bytevectors"); \
+915
return SIGIL_UNDEFINED; \
+916
} \
+917
if (!sigil_is_fixnum(args[2])) { \
+918
sigil__vm_error(vm, SIGIL_ERR_TYPE, \
+919
"mpi-" #method_name ": expected integer size"); \
+920
return SIGIL_UNDEFINED; \
+921
} \
+922
size_t size = (size_t)sigil_as_fixnum(args[2]); \
+923
mbedtls_mpi A, B, R; \
+924
mbedtls_mpi_init(&A); mbedtls_mpi_init(&B); mbedtls_mpi_init(&R); \
+925
if (mpi_read_bv(&A, args[0]) != 0) goto cleanup; \
+926
if (mpi_read_bv(&B, args[1]) != 0) goto cleanup; \
+927
if (op_fn(&R, &A, &B) != 0) goto cleanup; \
+928
Value result = mpi_write_bv(vm, &R, size); \
+929
cleanup: \
+930
mbedtls_mpi_free(&R); mbedtls_mpi_free(&B); mbedtls_mpi_free(&A); \
+931
return ((result != SIGIL_UNDEFINED) ? result : SIGIL_FALSE); \
+932
}
+933
+934
MPI_BINOP(add, mbedtls_mpi_add_mpi)
+935
MPI_BINOP(sub, mbedtls_mpi_sub_mpi)
+936
MPI_BINOP(mul, mbedtls_mpi_mul_mpi)
+937
+938
#undef MPI_BINOP
+939
+940
/*
+941
* mpi-div a-bv b-bv size -> (cons quotient-bv remainder-bv)
+942
*/
+943
static Value native_mpi_div(SigilVM *vm, int argc, Value *args)
+944
{
+945
(void)argc;
+946
+947
if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) {
+948
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-div: expected bytevectors");
+949
return SIGIL_UNDEFINED;
+950
}
+951
if (!sigil_is_fixnum(args[2])) {
+952
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-div: expected integer size");
+953
return SIGIL_UNDEFINED;
+954
}
+955
+956
size_t size = (size_t)sigil_as_fixnum(args[2]);
+957
+958
mbedtls_mpi A, B, Q, R;
+959
mbedtls_mpi_init(&A); mbedtls_mpi_init(&B);
+960
mbedtls_mpi_init(&Q); mbedtls_mpi_init(&R);
+961
+962
Value result = SIGIL_FALSE;
+963
+964
if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
+965
if (mpi_read_bv(&B, args[1]) != 0) goto cleanup;
+966
if (mbedtls_mpi_div_mpi(&Q, &R, &A, &B) != 0) goto cleanup;
+967
+968
Value q_bv = mpi_write_bv(vm, &Q, size);
+969
Value r_bv = mpi_write_bv(vm, &R, size);
+970
if (!sigil_is_bytevector(q_bv) || !sigil_is_bytevector(r_bv)) goto cleanup;
+971
+972
result = sigil_cons(vm, q_bv, r_bv);
+973
+974
cleanup:
+975
mbedtls_mpi_free(&R); mbedtls_mpi_free(&Q);
+976
mbedtls_mpi_free(&B); mbedtls_mpi_free(&A);
+977
return result;
+978
}
+979
+980
/*
+981
* mpi-mod a-bv n-bv size -> bytevector
+982
*/
+983
static Value native_mpi_mod(SigilVM *vm, int argc, Value *args)
+984
{
+985
(void)argc;
+986
+987
if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) {
+988
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-mod: expected bytevectors");
+989
return SIGIL_UNDEFINED;
+990
}
+991
if (!sigil_is_fixnum(args[2])) {
+992
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-mod: expected integer size");
+993
return SIGIL_UNDEFINED;
+994
}
+995
+996
size_t size = (size_t)sigil_as_fixnum(args[2]);
+997
+998
mbedtls_mpi A, N, R;
+999
mbedtls_mpi_init(&A); mbedtls_mpi_init(&N); mbedtls_mpi_init(&R);
+1000
+1001
if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
+1002
if (mpi_read_bv(&N, args[1]) != 0) goto cleanup;
+1003
if (mbedtls_mpi_mod_mpi(&R, &A, &N) != 0) goto cleanup;
+1004
+1005
Value result = mpi_write_bv(vm, &R, size);
+1006
+1007
cleanup:
+1008
mbedtls_mpi_free(&R); mbedtls_mpi_free(&N); mbedtls_mpi_free(&A);
+1009
return (result != SIGIL_UNDEFINED) ? result : SIGIL_FALSE;
+1010
}
+1011
+1012
/*
+1013
* mpi-mod-add a-bv b-bv n-bv size -> bytevector
+1014
* (a + b) mod n
+1015
*/
+1016
static Value native_mpi_mod_add(SigilVM *vm, int argc, Value *args)
+1017
{
+1018
(void)argc;
+1019
+1020
if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])
+1021
|| !sigil_is_bytevector(args[2])) {
+1022
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-mod-add: expected bytevectors");
+1023
return SIGIL_UNDEFINED;
+1024
}
+1025
if (!sigil_is_fixnum(args[3])) {
+1026
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-mod-add: expected integer size");
+1027
return SIGIL_UNDEFINED;
+1028
}
+1029
+1030
size_t size = (size_t)sigil_as_fixnum(args[3]);
+1031
+1032
mbedtls_mpi A, B, N, R;
+1033
mbedtls_mpi_init(&A); mbedtls_mpi_init(&B);
+1034
mbedtls_mpi_init(&N); mbedtls_mpi_init(&R);
+1035
+1036
if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
+1037
if (mpi_read_bv(&B, args[1]) != 0) goto cleanup;
+1038
if (mpi_read_bv(&N, args[2]) != 0) goto cleanup;
+1039
if (mbedtls_mpi_add_mpi(&R, &A, &B) != 0) goto cleanup;
+1040
if (mbedtls_mpi_mod_mpi(&R, &R, &N) != 0) goto cleanup;
+1041
+1042
Value result = mpi_write_bv(vm, &R, size);
+1043
+1044
cleanup:
+1045
mbedtls_mpi_free(&R); mbedtls_mpi_free(&N);
+1046
mbedtls_mpi_free(&B); mbedtls_mpi_free(&A);
+1047
return (result != SIGIL_UNDEFINED) ? result : SIGIL_FALSE;
+1048
}
+1049
+1050
/*
+1051
* mpi-inv-mod a-bv n-bv size -> bytevector | #f
+1052
*/
+1053
static Value native_mpi_inv_mod(SigilVM *vm, int argc, Value *args)
+1054
{
+1055
(void)argc;
+1056
+1057
if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) {
+1058
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-inv-mod: expected bytevectors");
+1059
return SIGIL_UNDEFINED;
+1060
}
+1061
if (!sigil_is_fixnum(args[2])) {
+1062
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-inv-mod: expected integer size");
+1063
return SIGIL_UNDEFINED;
+1064
}
+1065
+1066
size_t size = (size_t)sigil_as_fixnum(args[2]);
+1067
+1068
mbedtls_mpi A, N, R;
+1069
mbedtls_mpi_init(&A); mbedtls_mpi_init(&N); mbedtls_mpi_init(&R);
+1070
+1071
Value result = SIGIL_FALSE;
+1072
+1073
if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
+1074
if (mpi_read_bv(&N, args[1]) != 0) goto cleanup;
+1075
if (mbedtls_mpi_inv_mod(&R, &A, &N) != 0) goto cleanup;
+1076
+1077
result = mpi_write_bv(vm, &R, size);
+1078
+1079
cleanup:
+1080
mbedtls_mpi_free(&R); mbedtls_mpi_free(&N); mbedtls_mpi_free(&A);
+1081
return result;
+1082
}
+1083
+1084
/*
+1085
* mpi-cmp a-bv b-bv -> fixnum (-1, 0, or 1)
+1086
*/
+1087
static Value native_mpi_cmp(SigilVM *vm, int argc, Value *args)
+1088
{
+1089
(void)argc;
+1090
+1091
if (!sigil_is_bytevector(args[0]) || !sigil_is_bytevector(args[1])) {
+1092
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-cmp: expected bytevectors");
+1093
return SIGIL_UNDEFINED;
+1094
}
+1095
+1096
mbedtls_mpi A, B;
+1097
mbedtls_mpi_init(&A); mbedtls_mpi_init(&B);
+1098
+1099
if (mpi_read_bv(&A, args[0]) != 0) goto cleanup;
+1100
if (mpi_read_bv(&B, args[1]) != 0) goto cleanup;
+1101
+1102
int cmp = mbedtls_mpi_cmp_mpi(&A, &B);
+1103
mbedtls_mpi_free(&B); mbedtls_mpi_free(&A);
+1104
return sigil_fixnum(cmp);
+1105
+1106
cleanup:
+1107
mbedtls_mpi_free(&B); mbedtls_mpi_free(&A);
+1108
return SIGIL_FALSE;
+1109
}
+1110
+1111
/*
+1112
* mpi-is-zero? a-bv -> boolean
+1113
*/
+1114
static Value native_mpi_is_zero(SigilVM *vm, int argc, Value *args)
+1115
{
+1116
(void)argc;
+1117
+1118
if (!sigil_is_bytevector(args[0])) {
+1119
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-is-zero?: expected bytevector");
+1120
return SIGIL_UNDEFINED;
+1121
}
+1122
+1123
mbedtls_mpi A;
+1124
mbedtls_mpi_init(&A);
+1125
+1126
if (mpi_read_bv(&A, args[0]) != 0) {
+1127
mbedtls_mpi_free(&A);
+1128
return SIGIL_FALSE;
+1129
}
+1130
+1131
int result = (mbedtls_mpi_cmp_int(&A, 0) == 0);
+1132
mbedtls_mpi_free(&A);
+1133
return result ? SIGIL_TRUE : SIGIL_FALSE;
+1134
}
+1135
+1136
/*
+1137
* mpi-shift-l a-bv bits size -> bytevector
+1138
*/
+1139
static Value native_mpi_shift_l(SigilVM *vm, int argc, Value *args)
+1140
{
+1141
(void)argc;
+1142
+1143
if (!sigil_is_bytevector(args[0])) {
+1144
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-shift-l: expected bytevector");
+1145
return SIGIL_UNDEFINED;
+1146
}
+1147
if (!sigil_is_fixnum(args[1]) || !sigil_is_fixnum(args[2])) {
+1148
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-shift-l: expected integer arguments");
+1149
return SIGIL_UNDEFINED;
+1150
}
+1151
+1152
size_t bits = (size_t)sigil_as_fixnum(args[1]);
+1153
size_t size = (size_t)sigil_as_fixnum(args[2]);
+1154
+1155
mbedtls_mpi A;
+1156
mbedtls_mpi_init(&A);
+1157
+1158
if (mpi_read_bv(&A, args[0]) != 0) {
+1159
mbedtls_mpi_free(&A);
+1160
return SIGIL_FALSE;
+1161
}
+1162
+1163
if (mbedtls_mpi_shift_l(&A, bits) != 0) {
+1164
mbedtls_mpi_free(&A);
+1165
return SIGIL_FALSE;
+1166
}
+1167
+1168
Value result = mpi_write_bv(vm, &A, size);
+1169
mbedtls_mpi_free(&A);
+1170
return result;
+1171
}
+1172
+1173
/*
+1174
* mpi-shift-r a-bv bits -> bytevector
+1175
*/
+1176
static Value native_mpi_shift_r(SigilVM *vm, int argc, Value *args)
+1177
{
+1178
(void)argc;
+1179
+1180
if (!sigil_is_bytevector(args[0])) {
+1181
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-shift-r: expected bytevector");
+1182
return SIGIL_UNDEFINED;
+1183
}
+1184
if (!sigil_is_fixnum(args[1])) {
+1185
sigil__vm_error(vm, SIGIL_ERR_TYPE, "mpi-shift-r: expected integer");
+1186
return SIGIL_UNDEFINED;
+1187
}
+1188
+1189
size_t bits = (size_t)sigil_as_fixnum(args[1]);
+1190
+1191
mbedtls_mpi A;
+1192
mbedtls_mpi_init(&A);
+1193
+1194
if (mpi_read_bv(&A, args[0]) != 0) {
+1195
mbedtls_mpi_free(&A);
+1196
return SIGIL_FALSE;
+1197
}
+1198
+1199
if (mbedtls_mpi_shift_r(&A, bits) != 0) {
+1200
mbedtls_mpi_free(&A);
+1201
return SIGIL_FALSE;
+1202
}
+1203
+1204
SigilBytevector *in_bv = (SigilBytevector *)sigil_as_ptr(args[0]);
+1205
Value result = mpi_write_bv(vm, &A, in_bv->length);
+1206
mbedtls_mpi_free(&A);
+1207
return result;
+1208
}
+1209
1210
/* ===========================================================
1211
* ECDSA P-256, ECDH P-256, AES-128-GCM
1212
*
@@ -1323,12 +1745,16 @@ void sigil__init_sigil_crypto_module(SigilVM *vm)
1745
SIGIL_ARITY_EXACT(1), "Compute SHA-256 hash (hex string)");
1746
REGISTER_AND_EXPORT("sha256-bytes", native_sha256_bytes,
1747
SIGIL_ARITY_EXACT(1), "Compute SHA-256 hash (bytevector)");
+1748
REGISTER_AND_EXPORT("ripemd160", native_ripemd160,
+1749
SIGIL_ARITY_EXACT(1), "Compute RIPEMD-160 hash (returns bytevector)");
1750
1751
/* HMAC */
1752
REGISTER_AND_EXPORT("hmac-sha256", native_hmac_sha256,
1753
SIGIL_ARITY_EXACT(2), "Compute HMAC-SHA256 (hex string)");
1754
REGISTER_AND_EXPORT("hmac-sha256-bytes", native_hmac_sha256_bytes,
1755
SIGIL_ARITY_EXACT(2), "Compute HMAC-SHA256 (bytevector)");
+1756
REGISTER_AND_EXPORT("hmac-sha512-bytes", native_hmac_sha512_bytes,
+1757
SIGIL_ARITY_EXACT(2), "Compute HMAC-SHA512 (bytevector)");
1758
REGISTER_AND_EXPORT("hmac-sha1", native_hmac_sha1,
1759
SIGIL_ARITY_EXACT(2), "Compute HMAC-SHA1 (returns bytevector)");
1760
@@ -1348,6 +1774,30 @@ void sigil__init_sigil_crypto_module(SigilVM *vm)
1774
REGISTER_AND_EXPORT("random-bytes", native_random_bytes,
1775
SIGIL_ARITY_EXACT(1), "Generate secure random bytes");
1776
+1777
/* mbedTLS MPI — Big Integer Arithmetic */
+1778
REGISTER_AND_EXPORT("mpi-add", native_mpi_add,
+1779
SIGIL_ARITY_EXACT(3), "Add two bytevectors as big integers");
+1780
REGISTER_AND_EXPORT("mpi-sub", native_mpi_sub,
+1781
SIGIL_ARITY_EXACT(3), "Subtract two bytevectors as big integers");
+1782
REGISTER_AND_EXPORT("mpi-mul", native_mpi_mul,
+1783
SIGIL_ARITY_EXACT(3), "Multiply two bytevectors as big integers");
+1784
REGISTER_AND_EXPORT("mpi-div", native_mpi_div,
+1785
SIGIL_ARITY_EXACT(3), "Divide two bytevectors: returns (cons quotient remainder)");
+1786
REGISTER_AND_EXPORT("mpi-mod", native_mpi_mod,
+1787
SIGIL_ARITY_EXACT(3), "Modulo of two bytevectors as big integers");
+1788
REGISTER_AND_EXPORT("mpi-mod-add", native_mpi_mod_add,
+1789
SIGIL_ARITY_EXACT(4), "(a + b) mod n");
+1790
REGISTER_AND_EXPORT("mpi-inv-mod", native_mpi_inv_mod,
+1791
SIGIL_ARITY_EXACT(3), "Modular inverse of a modulo n");
+1792
REGISTER_AND_EXPORT("mpi-cmp", native_mpi_cmp,
+1793
SIGIL_ARITY_EXACT(2), "Compare two bytevectors: returns -1, 0, or 1");
+1794
REGISTER_AND_EXPORT("mpi-is-zero?", native_mpi_is_zero,
+1795
SIGIL_ARITY_EXACT(1), "Test if bytevectored big integer is zero");
+1796
REGISTER_AND_EXPORT("mpi-shift-l", native_mpi_shift_l,
+1797
SIGIL_ARITY_EXACT(3), "Left-shift bytevectored big integer");
+1798
REGISTER_AND_EXPORT("mpi-shift-r", native_mpi_shift_r,
+1799
SIGIL_ARITY_EXACT(2), "Right-shift bytevectored big integer");
+1800
1801
/* ECDSA P-256 (VAPID JWT signing) */
1802
REGISTER_AND_EXPORT("ecdsa-p256-generate-keypair",
1803
native_ecdsa_p256_generate_keypair,
src/sigil/crypto.sglmodified
@@ -19,8 +19,10 @@
19
sha1
20
sha256
21
sha256-bytes
+22
ripemd160
23
hmac-sha256
24
hmac-sha256-bytes
+25
hmac-sha512-bytes
26
hmac-sha1
27
pbkdf2-sha1
28
pbkdf2-sha256
@@ -30,6 +32,17 @@
32
base64url-decode
33
random-bytes
34
timing-safe-equal?
+35
mpi-add
+36
mpi-sub
+37
mpi-mul
+38
mpi-div
+39
mpi-mod
+40
mpi-mod-add
+41
mpi-inv-mod
+42
mpi-cmp
+43
mpi-is-zero?
+44
mpi-shift-l
+45
mpi-shift-r
46
ecdsa-p256-generate-keypair
47
ecdsa-p256-sign
48
ecdsa-p256-verify
@@ -73,6 +86,16 @@
86
(define-native (sha256-bytes data)
87
(: (any-of string? bytevector?) -> bytevector?))
88
+89
;;; Compute the RIPEMD-160 hash of data as a bytevector.
+90
;;;
+91
;;; Accepts a string or bytevector. Returns a 20-byte bytevector.
+92
;;;
+93
;;; ```scheme
+94
;;; (bytevector-length (ripemd160 "hello")) ; => 20
+95
;;; ```
+96
(define-native (ripemd160 data)
+97
(: (any-of string? bytevector?) -> bytevector?))
+98
99
;;; Compute HMAC-SHA256 message authentication code.
100
;;;
101
;;; Both key and message accept strings or bytevectors.
@@ -96,6 +119,14 @@
119
(define-native (hmac-sha256-bytes key message)
120
(: (any-of string? bytevector?) (any-of string? bytevector?) -> bytevector?))
121
+122
;;; Compute HMAC-SHA512 returning the raw 64-byte MAC as a bytevector.
+123
;;;
+124
;;; ```scheme
+125
;;; (hmac-sha512-bytes "secret" "msg") ; => 64-byte bytevector
+126
;;; ```
+127
(define-native (hmac-sha512-bytes key message)
+128
(: (any-of string? bytevector?) (any-of string? bytevector?) -> bytevector?))
+129
130
;;; Compute HMAC-SHA1 message authentication code.
131
;;;
132
;;; Both key and message accept strings or bytevectors.
@@ -168,6 +199,101 @@
199
(define-native (random-bytes count)
200
(: integer? -> bytevector?))
201
+202
;;; Add two bytevectors as big integers.
+203
;;;
+204
;;; Bytevectors are interpreted as unsigned big-endian integers.
+205
;;; The result is written right-padded to `size` bytes; if the
+206
;;; sum exceeds the width it wraps modulo 2^(8*size).
+207
;;;
+208
;;; ```scheme
+209
;;; (mpi-add #u8(0 0 0 1) #u8(0 0 0 2) 4) ; => #u8(0 0 0 3)
+210
;;; ```
+211
(define-native (mpi-add a-bv b-bv size)
+212
(: bytevector? bytevector? integer? -> bytevector?))
+213
+214
;;; Subtract two bytevectors as big integers.
+215
;;;
+216
;;; Result wraps modulo 2^(8*size) when negative.
+217
(define-native (mpi-sub a-bv b-bv size)
+218
(: bytevector? bytevector? integer? -> bytevector?))
+219
+220
;;; Multiply two bytevectors as big integers.
+221
;;;
+222
;;; Result wraps modulo 2^(8*size). Returns #f if the output
+223
;;; bytevector can't hold the full-width product.
+224
(define-native (mpi-mul a-bv b-bv size)
+225
(: bytevector? bytevector? integer? -> bytevector?))
+226
+227
;;; Divide two bytevectors as big integers.
+228
;;;
+229
;;; Returns `(cons quotient remainder)` as bytevectors of length `size`.
+230
;;;
+231
;;; ```scheme
+232
;;; (mpi-div #u8(0 0 0 10) #u8(0 0 0 3) 4)
+233
;;; ; => (#u8(0 0 0 3) . #u8(0 0 0 1))
+234
;;; ```
+235
(define-native (mpi-div a-bv b-bv size)
+236
(: bytevector? bytevector? integer? -> pair?))
+237
+238
;;; Compute a modulo n.
+239
(define-native (mpi-mod a-bv n-bv size)
+240
(: bytevector? bytevector? integer? -> bytevector?))
+241
+242
;;; Compute (a + b) mod n.
+243
(define-native (mpi-mod-add a-bv b-bv n-bv size)
+244
(: bytevector? bytevector? bytevector? integer? -> bytevector?))
+245
+246
;;; Compute the modular inverse of a modulo n.
+247
;;;
+248
;;; Returns #f if no inverse exists (a and n are not coprime).
+249
;;;
+250
;;; ```scheme
+251
;;; (mpi-inv-mod #u8(0 0 0 3) #u8(0 0 0 7) 4)
+252
;;; ; => #u8(0 0 0 5) ;; 3 * 5 = 15 = 1 mod 7
+253
;;; ```
+254
(define-native (mpi-inv-mod a-bv n-bv size)
+255
(: bytevector? bytevector? integer? -> (any-of bytevector? boolean?)))
+256
+257
;;; Compare two bytevectors as big integers.
+258
;;;
+259
;;; Returns -1 if a < b, 0 if equal, 1 if a > b.
+260
;;;
+261
;;; ```scheme
+262
;;; (mpi-cmp #u8(0 0 0 1) #u8(0 0 0 2)) ; => -1
+263
;;; ```
+264
(define-native (mpi-cmp a-bv b-bv)
+265
(: bytevector? bytevector? -> integer?))
+266
+267
;;; Test if a bytevector represents the big integer zero.
+268
;;;
+269
;;; ```scheme
+270
;;; (mpi-is-zero? #u8(0 0 0 0)) ; => #t
+271
;;; (mpi-is-zero? #u8(0 0 0 1)) ; => #f
+272
;;; ```
+273
(define-native (mpi-is-zero? a-bv)
+274
(: bytevector? -> boolean?))
+275
+276
;;; Left-shift a bytevectored big integer by `bits` positions.
+277
;;;
+278
;;; The result is written right-padded to `size` bytes; excess
+279
;;; high bits are truncated.
+280
;;;
+281
;;; ```scheme
+282
;;; (mpi-shift-l #u8(0 0 0 1) 3 4) ; => #u8(0 0 0 8)
+283
;;; ```
+284
(define-native (mpi-shift-l a-bv bits size)
+285
(: bytevector? integer? integer? -> bytevector?))
+286
+287
;;; Right-shift a bytevectored big integer by `bits` positions.
+288
;;;
+289
;;; Result preserves the input bytevector length.
+290
;;;
+291
;;; ```scheme
+292
;;; (mpi-shift-r #u8(0 0 0 8) 3) ; => #u8(0 0 0 1)
+293
;;; ```
+294
(define-native (mpi-shift-r a-bv bits)
+295
(: bytevector? integer? -> bytevector?))
+296
297
;;; Timing-safe string comparison to prevent timing attacks.
298
;;;
299
;;; Compares every character regardless of mismatches so that the
test/test-crypto.sglmodified
@@ -922,4 +922,177 @@
922
(assert-equal 12 (bytevector-length nonce)))))
923
924
+925
;; ============================================================
+926
;; ripemd160
+927
;; ============================================================
+928
+929
(test-group "ripemd160"
+930
(test "empty string — known vector"
+931
(let ((expected (bytevector
+932
#x9c #x11 #x85 #xa5 #xc5 #xe9 #xfc #x54
+933
#x61 #x28 #x08 #x97 #x7e #xe8 #xf5 #x48
+934
#xb2 #x25 #x8d #x31)))
+935
(assert-equal expected (ripemd160 ""))))
+936
+937
(test "abc — known vector"
+938
(let ((expected (bytevector
+939
#x8e #xb2 #x08 #xf7 #xe0 #x5d #x98 #x7a
+940
#x9b #x04 #x4a #x8e #x98 #xc6 #xb0 #x87
+941
#xf1 #x5a #x0b #xfc)))
+942
(assert-equal expected (ripemd160 "abc"))))
+943
+944
(test "binary input works"
+945
(let ((bv (make-bytevector 32 0)))
+946
(assert-equal 20 (bytevector-length (ripemd160 bv))))))
+947
+948
+949
;; ============================================================
+950
;; hmac-sha512-bytes
+951
;; ============================================================
+952
;;
+953
;; RFC 4231 test case 1 (HMAC-SHA-512):
+954
;; key = 20 bytes of 0x0b
+955
;; data = "Hi There"
+956
;; expected =
+957
;; 87aa7cdea5ef619d4ff0b4241a1d6cb0
+958
;; 2379f4e2ce4ec2787ad0b30545e17cde
+959
;; daa833b7d6b8a702038b274eaea3f4e4
+960
;; be9d914eeb61f1702e696c203a126854
+961
+962
(define %rfc4231-sha512-vec1-bv
+963
(bytevector
+964
#x87 #xaa #x7c #xde #xa5 #xef #x61 #x9d
+965
#x4f #xf0 #xb4 #x24 #x1a #x1d #x6c #xb0
+966
#x23 #x79 #xf4 #xe2 #xce #x4e #xc2 #x78
+967
#x7a #xd0 #xb3 #x05 #x45 #xe1 #x7c #xde
+968
#xda #xa8 #x33 #xb7 #xd6 #xb8 #xa7 #x02
+969
#x03 #x8b #x27 #x4e #xae #xa3 #xf4 #xe4
+970
#xbe #x9d #x91 #x4e #xeb #x61 #xf1 #x70
+971
#x2e #x69 #x6c #x20 #x3a #x12 #x68 #x54))
+972
+973
(test-group "hmac-sha512-bytes"
+974
(test "RFC 4231 test case 1"
+975
(let* ((key (make-bytevector 20 #x0b))
+976
(result (hmac-sha512-bytes key "Hi There")))
+977
(assert-equal 64 (bytevector-length result))
+978
(assert-equal %rfc4231-sha512-vec1-bv result)))
+979
+980
(test "returns 64-byte bytevector with string args"
+981
(let ((result (hmac-sha512-bytes "key" "msg")))
+982
(assert-equal 64 (bytevector-length result)))))
+983
+984
+985
;; ============================================================
+986
;; mpi-add, mpi-sub, mpi-mul
+987
;; ============================================================
+988
+989
(test-group "mpi-add"
+990
(test "1 + 2 = 3 (big-endian, 4 bytes)"
+991
(let ((a #u8(0 0 0 1))
+992
(b #u8(0 0 0 2))
+993
(result (mpi-add a b 4)))
+994
(assert-equal 3 (bytevector-u8-ref result 3)))))
+995
+996
(test-group "mpi-sub"
+997
(test "5 - 3 = 2 (big-endian, 4 bytes)"
+998
(let ((a #u8(0 0 0 5))
+999
(b #u8(0 0 0 3))
+1000
(result (mpi-sub a b 4)))
+1001
(assert-equal 2 (bytevector-u8-ref result 3)))))
+1002
+1003
(test-group "mpi-mul"
+1004
(test "3 * 4 = 12 (big-endian, 4 bytes)"
+1005
(let ((a #u8(0 0 0 3))
+1006
(b #u8(0 0 0 4))
+1007
(result (mpi-mul a b 4)))
+1008
(assert-equal 12 (bytevector-u8-ref result 3)))))
+1009
+1010
+1011
;; ============================================================
+1012
;; mpi-div
+1013
;; ============================================================
+1014
+1015
(test-group "mpi-div"
+1016
(test "10 / 3 = 3 rem 1 (big-endian, 4 bytes)"
+1017
(let ((result (mpi-div #u8(0 0 0 10) #u8(0 0 0 3) 4)))
+1018
(assert-equal 3 (bytevector-u8-ref (car result) 3))
+1019
(assert-equal 1 (bytevector-u8-ref (cdr result) 3)))))
+1020
+1021
+1022
;; ============================================================
+1023
;; mpi-mod
+1024
;; ============================================================
+1025
+1026
(test-group "mpi-mod"
+1027
(test "10 mod 3 = 1 (big-endian, 4 bytes)"
+1028
(assert-equal 1
+1029
(bytevector-u8-ref (mpi-mod #u8(0 0 0 10) #u8(0 0 0 3) 4) 3))))
+1030
+1031
+1032
;; ============================================================
+1033
;; mpi-mod-add
+1034
;; ============================================================
+1035
+1036
(test-group "mpi-mod-add"
+1037
(test "(6 + 5) mod 7 = 4 (big-endian, 4 bytes)"
+1038
(let ((result (mpi-mod-add #u8(0 0 0 6) #u8(0 0 0 5)
+1039
#u8(0 0 0 7) 4)))
+1040
(assert-equal 4 (bytevector-u8-ref result 3)))))
+1041
+1042
+1043
;; ============================================================
+1044
;; mpi-inv-mod
+1045
;; ============================================================
+1046
+1047
(test-group "mpi-inv-mod"
+1048
(test "inv(3, 7) = 5 (3 * 5 = 15 = 1 mod 7)"
+1049
(let ((result (mpi-inv-mod #u8(0 0 0 3) #u8(0 0 0 7) 4)))
+1050
(assert-equal 5 (bytevector-u8-ref result 3))))
+1051
+1052
(test "no inverse returns #f (3 and 9 are not coprime)"
+1053
(assert-false (mpi-inv-mod #u8(0 0 0 3) #u8(0 0 0 9) 4))))
+1054
+1055
+1056
;; ============================================================
+1057
;; mpi-cmp
+1058
;; ============================================================
+1059
+1060
(test-group "mpi-cmp"
+1061
(test "equal: 5 == 5"
+1062
(assert-equal 0 (mpi-cmp #u8(0 0 0 5) #u8(0 0 0 5))))
+1063
+1064
(test "less: 3 < 7"
+1065
(assert-equal -1 (mpi-cmp #u8(0 0 0 3) #u8(0 0 0 7))))
+1066
+1067
(test "greater: 9 > 2"
+1068
(assert-equal 1 (mpi-cmp #u8(0 0 0 9) #u8(0 0 0 2)))))
+1069
+1070
+1071
;; ============================================================
+1072
;; mpi-is-zero?
+1073
;; ============================================================
+1074
+1075
(test-group "mpi-is-zero?"
+1076
(test "zero"
+1077
(assert-true (mpi-is-zero? #u8(0 0 0 0))))
+1078
+1079
(test "non-zero"
+1080
(assert-false (mpi-is-zero? #u8(0 0 0 1)))))
+1081
+1082
+1083
;; ============================================================
+1084
;; mpi-shift-l, mpi-shift-r
+1085
;; ============================================================
+1086
+1087
(test-group "mpi-shift-l"
+1088
(test "1 << 3 = 8 (big-endian, 4 bytes)"
+1089
(let ((result (mpi-shift-l #u8(0 0 0 1) 3 4)))
+1090
(assert-equal 8 (bytevector-u8-ref result 3)))))
+1091
+1092
(test-group "mpi-shift-r"
+1093
(test "8 >> 3 = 1 (big-endian, 4 bytes)"
+1094
(let ((result (mpi-shift-r #u8(0 0 0 8) 3)))
+1095
(assert-equal 1 (bytevector-u8-ref result 3)))))
+1096
+1097
1098
(run-tests)
vendor/mbedtls/sigil_mbedtls_config.hmodified
@@ -77,6 +77,7 @@
77
#define MBEDTLS_SHA256_C
78
#define MBEDTLS_SHA384_C
79
#define MBEDTLS_SHA512_C
+80
#define MBEDTLS_RIPEMD160_C
81
82
/* PKCS5 Support (PBKDF2 key derivation) */
83
#define MBEDTLS_PKCS5_C