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 derivation
src/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
;; ============================================================