Commit70557062Recorded4 May 2026Repositorysigil-crypto
Merge pull request 'feat: add ripemd160, hmac-sha512-bytes, and mbedTLS MPI bignum primitives' (#1) from trevarj/sigil-crypto:feat/ripemd160-hmac-sha512-mpi into master
Message
Reviewed-on: https://codeberg.org/sigil/sigil-crypto/pulls/1
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
A merge. Shown against its first parent, so this is the effect of merging rather than the work of the branch.
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 thetest/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