Commit7f2c115fRecorded7 May 2026Repositorysigil-crypto
feat: add pbkdf2 sha512
Changed
native/crypto.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
package.sgl | 4 ++--
src/sigil/crypto.sgl | 11 +++++++++++
test/test-crypto.sgl | 26 ++++++++++++++++++++++++++
4 files changed, 122 insertions(+), 2 deletions(-)Diff
native/crypto.cmodified
@@ -653,6 +653,87 @@ static Value native_pbkdf2_sha256(SigilVM *vm, int argc, Value *args)
653
return result; 654
} 655
+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
*/+662
static Value native_pbkdf2_sha512(SigilVM *vm, int argc, Value *args)+663
{+664
(void)argc;+665
+666
const unsigned char *password;+667
size_t password_len;+668
const unsigned char *salt;+669
size_t salt_len;+670
+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
}+683
+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
}+696
+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
}+706
+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
}+716
+717
unsigned char *output = malloc(key_length);+718
if (!output) return SIGIL_FALSE;+719
+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
}+728
+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;+735
}+736
737
/* 738
* base64-encode data -> string 739
* Encode data (string or bytevector) as base64.@@ -1783,6 +1864,8 @@ void sigil__init_sigil_crypto_module(SigilVM *vm)
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"); 1869
1870
/* Base64 */ 1871
REGISTER_AND_EXPORT("base64-encode", native_base64_encode,package.sglmodified
@@ -2,8 +2,8 @@
2
;;; 3
;;; Provides cryptographic primitives using mbedTLS: 4
;;; - SHA-1 and SHA-256 hashing−5
;;; - HMAC-SHA1 / HMAC-SHA256 (hex + bytevector outputs)−6
;;; - PBKDF2-SHA1 / PBKDF2-SHA256 key derivation+5
;;; - HMAC-SHA1 / HMAC-SHA256 / HMAC-SHA512+6
;;; - PBKDF2-SHA1 / PBKDF2-SHA256 / PBKDF2-SHA512 key derivation 7
;;; - HKDF-SHA256 (RFC 5869) key derivation 8
;;; - ECDSA P-256 sign + verify + keygen (JOSE/ES256 format) 9
;;; - ECDH P-256 shared-secret derivationsrc/sigil/crypto.sglmodified
@@ -26,6 +26,7 @@
26
hmac-sha1 27
pbkdf2-sha1 28
pbkdf2-sha256+29
pbkdf2-sha512 30
base64-encode 31
base64-decode 32
base64url-encode@@ -169,6 +170,16 @@
170
(define-native (pbkdf2-sha256 password salt iterations key-length) 171
(: (any-of string? bytevector?) (any-of string? bytevector?) integer? integer? -> bytevector?)) 172
+173
;;; Derive a key using PBKDF2-SHA512.+174
;;;+175
;;; Password and salt accept strings or bytevectors. Iterations+176
;;; controls the work factor. Key-length specifies the output size+177
;;; in bytes. Returns the derived key as a bytevector.+178
;;;+179
;;; Useful for protocols that require PBKDF2-HMAC-SHA512.+180
(define-native (pbkdf2-sha512 password salt iterations key-length)+181
(: (any-of string? bytevector?) (any-of string? bytevector?) integer? integer? -> bytevector?))+182
183
;;; Encode data as a base64 string. 184
;;; 185
;;; Accepts a string or bytevector.test/test-crypto.sglmodified
@@ -209,6 +209,32 @@
209
(b (pbkdf2-sha256 "password" "salt" 100 32))) 210
(assert-equal a b)))) 211
+212
;; ============================================================+213
;; pbkdf2-sha512+214
;; ============================================================+215
+216
(define %pbkdf2-sha512-vec1+217
(bytevector+218
#x86 #x7f #x70 #xcf #x1a #xde #x02 #xcf+219
#xf3 #x75 #x25 #x99 #xa3 #xa5 #x3d #xc4+220
#xaf #x34 #xc7 #xa6 #x69 #x81 #x5a #xe5+221
#xd5 #x13 #x55 #x4e #x1c #x8c #xf2 #x52+222
#xc0 #x2d #x47 #x0a #x28 #x5a #x05 #x01+223
#xba #xd9 #x99 #xbf #xe9 #x43 #xc0 #x8f+224
#x05 #x02 #x35 #xd7 #xd6 #x8b #x1d #xa5+225
#x5e #x63 #xf7 #x3b #x60 #xa5 #x7f #xce))+226
+227
(test-group "pbkdf2-sha512"+228
(test "known-answer vector: password/salt/c=1/dkLen=64"+229
(let ((result (pbkdf2-sha512 "password" "salt" 1 64)))+230
(assert-true (bytevector? result))+231
(assert-equal 64 (bytevector-length result))+232
(assert-equal %pbkdf2-sha512-vec1 result)))+233
+234
(test "deterministic"+235
(assert-equal (pbkdf2-sha512 "p" "s" 100 32)+236
(pbkdf2-sha512 "p" "s" 100 32))))+237
238
;; ============================================================ 239
;; base64 240
;; ============================================================