Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4467488
crypto.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
crypto.c
View Options
#include
<compat.h>
// IWYU pragma: keep
#include
<string.h>
#include
<time.h>
#include
<limits.h>
#include
<openssl/evp.h>
#include
<openssl/rand.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-int-conversion"
#include
<uuidv7.h>
#pragma GCC diagnostic pop
#include
"ant.h"
#include
"base64.h"
#include
"errors.h"
#include
"runtime.h"
#include
"modules/crypto.h"
#include
"modules/buffer.h"
#include
"modules/symbol.h"
typedef
enum
{
CRYPTO_TEXT_UTF8
=
0
,
CRYPTO_TEXT_HEX
,
CRYPTO_TEXT_BASE64
,
CRYPTO_TEXT_LATIN1
}
crypto_text_encoding_t
;
typedef
struct
{
EVP_MD_CTX
*
ctx
;
const
EVP_MD
*
md
;
unsigned
char
digest
[
EVP_MAX_MD_SIZE
];
unsigned
int
digest_len
;
bool
finalized
;
}
ant_hash_state_t
;
int
crypto_fill_random
(
void
*
buf
,
size_t
len
)
{
if
(
len
==
0
)
return
0
;
if
(
len
>
(
size_t
)
INT_MAX
)
return
-1
;
return
RAND_bytes
((
uint8_t
*
)
buf
,
(
int
)
len
)
==
1
?
0
:
-1
;
}
static
inline
ant_value_t
crypto_random_error
(
ant_t
*
js
)
{
return
js_mkerr
(
js
,
"secure random generation failed"
);
}
static
ant_value_t
crypto_format_uuid_v4
(
ant_t
*
js
,
const
uint8_t
uuid
[
16
])
{
static
char
lut
[
256
][
2
];
static
bool
lut_init
=
false
;
char
uuid_str
[
36
];
if
(
!
lut_init
)
{
static
const
char
hex
[]
=
"0123456789abcdef"
;
for
(
int
i
=
0
;
i
<
256
;
i
++
)
{
lut
[
i
][
0
]
=
hex
[(
unsigned
)
i
>>
4
];
lut
[
i
][
1
]
=
hex
[(
unsigned
)
i
&
0x0f
];
}
lut_init
=
true
;
}
memcpy
(
uuid_str
+
0
,
lut
[
uuid
[
0
]],
2
);
memcpy
(
uuid_str
+
2
,
lut
[
uuid
[
1
]],
2
);
memcpy
(
uuid_str
+
4
,
lut
[
uuid
[
2
]],
2
);
memcpy
(
uuid_str
+
6
,
lut
[
uuid
[
3
]],
2
);
uuid_str
[
8
]
=
'-'
;
memcpy
(
uuid_str
+
9
,
lut
[
uuid
[
4
]],
2
);
memcpy
(
uuid_str
+
11
,
lut
[
uuid
[
5
]],
2
);
uuid_str
[
13
]
=
'-'
;
memcpy
(
uuid_str
+
14
,
lut
[
uuid
[
6
]],
2
);
memcpy
(
uuid_str
+
16
,
lut
[
uuid
[
7
]],
2
);
uuid_str
[
18
]
=
'-'
;
memcpy
(
uuid_str
+
19
,
lut
[
uuid
[
8
]],
2
);
memcpy
(
uuid_str
+
21
,
lut
[
uuid
[
9
]],
2
);
uuid_str
[
23
]
=
'-'
;
memcpy
(
uuid_str
+
24
,
lut
[
uuid
[
10
]],
2
);
memcpy
(
uuid_str
+
26
,
lut
[
uuid
[
11
]],
2
);
memcpy
(
uuid_str
+
28
,
lut
[
uuid
[
12
]],
2
);
memcpy
(
uuid_str
+
30
,
lut
[
uuid
[
13
]],
2
);
memcpy
(
uuid_str
+
32
,
lut
[
uuid
[
14
]],
2
);
memcpy
(
uuid_str
+
34
,
lut
[
uuid
[
15
]],
2
);
return
js_mkstr
(
js
,
uuid_str
,
sizeof
(
uuid_str
));
}
static
ant_value_t
crypto_make_buffer
(
ant_t
*
js
,
const
uint8_t
*
data
,
size_t
len
)
{
ArrayBufferData
*
buffer
=
create_array_buffer_data
(
len
);
if
(
!
buffer
)
return
js_mkerr
(
js
,
"Failed to allocate Buffer"
);
if
(
len
>
0
&&
data
)
memcpy
(
buffer
->
data
,
data
,
len
);
return
create_typed_array
(
js
,
TYPED_ARRAY_UINT8
,
buffer
,
0
,
len
,
"Buffer"
);
}
static
bool
crypto_get_mutable_bytes
(
ant_value_t
value
,
uint8_t
**
out
,
size_t
*
len
)
{
TypedArrayData
*
ta
=
buffer_get_typedarray_data
(
value
);
if
(
ta
)
{
if
(
!
ta
->
buffer
||
ta
->
buffer
->
is_detached
)
return
false
;
*
out
=
ta
->
buffer
->
data
+
ta
->
byte_offset
;
*
len
=
ta
->
byte_length
;
return
true
;
}
ArrayBufferData
*
ab
=
buffer_get_arraybuffer_data
(
value
);
if
(
ab
)
{
if
(
ab
->
is_detached
)
return
false
;
*
out
=
ab
->
data
;
*
len
=
ab
->
length
;
return
true
;
}
return
false
;
}
static
crypto_text_encoding_t
crypto_parse_encoding
(
const
char
*
str
,
size_t
len
)
{
if
(
len
==
3
&&
strncasecmp
(
str
,
"hex"
,
3
)
==
0
)
return
CRYPTO_TEXT_HEX
;
if
(
len
==
6
&&
strncasecmp
(
str
,
"base64"
,
6
)
==
0
)
return
CRYPTO_TEXT_BASE64
;
if
(
len
==
9
&&
strncasecmp
(
str
,
"base64url"
,
9
)
==
0
)
return
CRYPTO_TEXT_BASE64
;
if
(
(
len
==
5
&&
strncasecmp
(
str
,
"ascii"
,
5
)
==
0
)
||
(
len
==
6
&&
strncasecmp
(
str
,
"latin1"
,
6
)
==
0
)
||
(
len
==
6
&&
strncasecmp
(
str
,
"binary"
,
6
)
==
0
)
)
return
CRYPTO_TEXT_LATIN1
;
return
CRYPTO_TEXT_UTF8
;
}
static
uint8_t
crypto_hex_nibble
(
char
ch
)
{
if
(
ch
>=
'0'
&&
ch
<=
'9'
)
return
(
uint8_t
)(
ch
-
'0'
);
if
(
ch
>=
'a'
&&
ch
<=
'f'
)
return
(
uint8_t
)(
10
+
ch
-
'a'
);
if
(
ch
>=
'A'
&&
ch
<=
'F'
)
return
(
uint8_t
)(
10
+
ch
-
'A'
);
return
0xFFu
;
}
static
uint8_t
*
crypto_decode_hex
(
const
char
*
str
,
size_t
len
,
size_t
*
out_len
)
{
if
((
len
&
1u
)
!=
0
)
return
NULL
;
uint8_t
*
buf
=
malloc
(
len
/
2u
);
if
(
!
buf
)
return
NULL
;
for
(
size_t
i
=
0
;
i
<
len
;
i
+=
2u
)
{
uint8_t
hi
=
crypto_hex_nibble
(
str
[
i
]);
uint8_t
lo
=
crypto_hex_nibble
(
str
[
i
+
1u
]);
if
(
hi
==
0xFFu
||
lo
==
0xFFu
)
{
free
(
buf
);
return
NULL
;
}
buf
[
i
/
2u
]
=
(
uint8_t
)((
hi
<<
4u
)
|
lo
);
}
*
out_len
=
len
/
2u
;
return
buf
;
}
static
ant_value_t
crypto_get_input_bytes
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
encoding_val
,
const
uint8_t
**
out_bytes
,
size_t
*
out_len
,
uint8_t
**
owned
)
{
const
uint8_t
*
bytes
=
NULL
;
size_t
len
=
0
;
uint8_t
*
buf
=
NULL
;
ant_value_t
string_val
;
size_t
str_len
=
0
;
const
char
*
str
=
NULL
;
crypto_text_encoding_t
enc
=
CRYPTO_TEXT_UTF8
;
if
(
buffer_source_get_bytes
(
js
,
value
,
&
bytes
,
&
len
))
{
*
out_bytes
=
bytes
;
*
out_len
=
len
;
*
owned
=
NULL
;
return
js_mkundef
();
}
string_val
=
js_tostring_val
(
js
,
value
);
if
(
is_err
(
string_val
))
return
string_val
;
str
=
js_getstr
(
js
,
string_val
,
&
str_len
);
if
(
!
str
)
return
js_mkerr
(
js
,
"Failed to convert hash input to string"
);
if
(
vtype
(
encoding_val
)
==
T_STR
)
{
size_t
enc_len
=
0
;
const
char
*
enc_str
=
js_getstr
(
js
,
encoding_val
,
&
enc_len
);
if
(
enc_str
)
enc
=
crypto_parse_encoding
(
enc_str
,
enc_len
);
}
switch
(
enc
)
{
case
CRYPTO_TEXT_HEX
:
buf
=
crypto_decode_hex
(
str
,
str_len
,
&
len
);
if
(
!
buf
)
return
js_mkerr
(
js
,
"Invalid hex string"
);
bytes
=
buf
;
break
;
case
CRYPTO_TEXT_BASE64
:
buf
=
ant_base64_decode
(
str
,
str_len
,
&
len
);
if
(
!
buf
)
return
js_mkerr
(
js
,
"Invalid base64 string"
);
bytes
=
buf
;
break
;
case
CRYPTO_TEXT_LATIN1
:
buf
=
malloc
(
str_len
);
if
(
!
buf
)
return
js_mkerr
(
js
,
"Out of memory"
);
for
(
size_t
i
=
0
;
i
<
str_len
;
i
++
)
buf
[
i
]
=
(
uint8_t
)
str
[
i
];
bytes
=
buf
;
len
=
str_len
;
break
;
case
CRYPTO_TEXT_UTF8
:
default
:
bytes
=
(
const
uint8_t
*
)
str
;
len
=
str_len
;
break
;
}
*
out_bytes
=
bytes
;
*
out_len
=
len
;
*
owned
=
buf
;
return
js_mkundef
();
}
static
ant_hash_state_t
*
crypto_get_hash_state
(
ant_value_t
value
)
{
ant_value_t
slot
=
js_get_slot
(
value
,
SLOT_DATA
);
if
(
vtype
(
slot
)
!=
T_NUM
)
return
NULL
;
ant_hash_state_t
*
state
=
(
ant_hash_state_t
*
)(
uintptr_t
)
js_getnum
(
slot
);
return
(
state
&&
state
->
ctx
)
?
state
:
NULL
;
}
static
ant_value_t
crypto_require_hash_state
(
ant_t
*
js
,
ant_value_t
value
,
ant_hash_state_t
**
out_state
)
{
ant_hash_state_t
*
state
=
crypto_get_hash_state
(
value
);
if
(
!
state
)
return
js_mkerr
(
js
,
"Invalid Hash state"
);
*
out_state
=
state
;
return
js_mkundef
();
}
static
ant_value_t
crypto_digest_result
(
ant_t
*
js
,
const
uint8_t
*
digest
,
size_t
digest_len
,
ant_value_t
encoding_val
)
{
if
(
vtype
(
encoding_val
)
!=
T_STR
)
return
crypto_make_buffer
(
js
,
digest
,
digest_len
);
size_t
enc_len
=
0
;
const
char
*
enc
=
js_getstr
(
js
,
encoding_val
,
&
enc_len
);
if
(
!
enc
)
return
crypto_make_buffer
(
js
,
digest
,
digest_len
);
crypto_text_encoding_t
encoding
=
crypto_parse_encoding
(
enc
,
enc_len
);
if
(
encoding
==
CRYPTO_TEXT_HEX
)
{
char
*
hex
=
malloc
(
digest_len
*
2u
+
1u
);
if
(
!
hex
)
return
js_mkerr
(
js
,
"Out of memory"
);
for
(
size_t
i
=
0
;
i
<
digest_len
;
i
++
)
{
snprintf
(
hex
+
(
i
*
2u
),
3
,
"%02x"
,
digest
[
i
]);
}
ant_value_t
result
=
js_mkstr
(
js
,
hex
,
digest_len
*
2u
);
free
(
hex
);
return
result
;
}
if
(
encoding
==
CRYPTO_TEXT_BASE64
)
{
size_t
out_len
=
0
;
char
*
encoded
=
ant_base64_encode
(
digest
,
digest_len
,
&
out_len
);
if
(
!
encoded
)
return
js_mkerr
(
js
,
"Failed to encode base64"
);
if
(
enc_len
==
9
&&
strncasecmp
(
enc
,
"base64url"
,
9
)
==
0
)
{
for
(
size_t
i
=
0
;
i
<
out_len
;
i
++
)
{
if
(
encoded
[
i
]
==
'+'
)
encoded
[
i
]
=
'-'
;
else
if
(
encoded
[
i
]
==
'/'
)
encoded
[
i
]
=
'_'
;
}
while
(
out_len
>
0
&&
encoded
[
out_len
-
1u
]
==
'='
)
out_len
--
;
}
ant_value_t
result
=
js_mkstr
(
js
,
encoded
,
out_len
);
free
(
encoded
);
return
result
;
}
return
crypto_make_buffer
(
js
,
digest
,
digest_len
);
}
int
uuidv7_new
(
uint8_t
*
uuid_out
)
{
static
uint8_t
uuid_prev
[
16
]
=
{
0
};
static
uint8_t
rand_bytes
[
256
]
=
{
0
};
static
size_t
n_rand_consumed
=
sizeof
(
rand_bytes
);
struct
timespec
tp
;
clock_gettime
(
CLOCK_REALTIME
,
&
tp
);
uint64_t
unix_ts_ms
=
(
uint64_t
)
tp
.
tv_sec
*
1000
+
tp
.
tv_nsec
/
1000000
;
if
(
n_rand_consumed
>
sizeof
(
rand_bytes
)
-
10
)
{
if
(
crypto_fill_random
(
rand_bytes
,
sizeof
(
rand_bytes
))
<
0
)
return
-1
;
n_rand_consumed
=
0
;
}
int8_t
status
=
uuidv7_generate
(
uuid_prev
,
unix_ts_ms
,
&
rand_bytes
[
n_rand_consumed
],
uuid_prev
);
n_rand_consumed
+=
uuidv7_status_n_rand_consumed
(
status
);
memcpy
(
uuid_out
,
uuid_prev
,
16
);
return
status
;
}
// crypto.random()
static
ant_value_t
js_crypto_random
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
unsigned
int
value
=
0
;
if
(
crypto_fill_random
(
&
value
,
sizeof
(
value
))
<
0
)
return
crypto_random_error
(
js
);
return
js_mknum
((
double
)
value
);
}
// crypto.randomBytes(length)
static
ant_value_t
js_crypto_random_bytes
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
{
return
js_mkerr
(
js
,
"randomBytes requires a length argument"
);
}
int
length
=
(
int
)
js_getnum
(
args
[
0
]);
if
(
length
<=
0
||
length
>
65536
)
{
return
js_mkerr
(
js
,
"invalid length"
);
}
unsigned
char
*
random_bytes
=
malloc
(
length
);
if
(
random_bytes
==
NULL
)
{
return
js_mkerr
(
js
,
"memory allocation failed"
);
}
if
(
crypto_fill_random
(
random_bytes
,
(
size_t
)
length
)
<
0
)
{
free
(
random_bytes
);
return
crypto_random_error
(
js
);
}
ant_value_t
array
=
crypto_make_buffer
(
js
,
random_bytes
,
(
size_t
)
length
);
free
(
random_bytes
);
return
array
;
}
// crypto.randomUUID()
static
ant_value_t
js_crypto_random_uuid
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
uint8_t
uuid
[
16
];
if
(
crypto_fill_random
(
uuid
,
sizeof
(
uuid
))
<
0
)
return
crypto_random_error
(
js
);
uuid
[
6
]
=
(
uint8_t
)((
uuid
[
6
]
&
0x0f
)
|
0x40
);
uuid
[
8
]
=
(
uint8_t
)((
uuid
[
8
]
&
0x3f
)
|
0x80
);
return
crypto_format_uuid_v4
(
js
,
uuid
);
}
// crypto.randomUUIDv7()
static
ant_value_t
js_crypto_random_uuidv7
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
uint8_t
uuid
[
16
];
char
uuid_str
[
37
];
int
result
=
uuidv7_new
(
uuid
);
if
(
result
<
0
)
return
js_mkerr
(
js
,
"UUIDv7 generation failed"
);
uuidv7_to_string
(
uuid
,
uuid_str
);
return
js_mkstr
(
js
,
uuid_str
,
strlen
(
uuid_str
));
}
// crypto.getRandomValues(typedArray)
static
ant_value_t
js_crypto_get_random_values
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
{
return
js_mkerr
(
js
,
"getRandomValues requires a TypedArray argument"
);
}
TypedArrayData
*
ta_data
=
buffer_get_typedarray_data
(
args
[
0
]);
if
(
!
ta_data
||
!
ta_data
->
buffer
)
{
return
js_mkerr
(
js
,
"argument must be a TypedArray"
);
}
if
(
ta_data
->
byte_length
>
65536
)
{
return
js_mkerr
(
js
,
"TypedArray byte length exceeds 65536"
);
}
uint8_t
*
ptr
=
ta_data
->
buffer
->
data
+
ta_data
->
byte_offset
;
if
(
crypto_fill_random
(
ptr
,
ta_data
->
byte_length
)
<
0
)
return
crypto_random_error
(
js
);
return
args
[
0
];
}
static
ant_value_t
js_crypto_random_fill_sync
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"randomFillSync requires a target"
);
uint8_t
*
bytes
=
NULL
;
size_t
len
=
0
;
if
(
!
crypto_get_mutable_bytes
(
args
[
0
],
&
bytes
,
&
len
))
{
return
js_mkerr
(
js
,
"randomFillSync target must be a Buffer, TypedArray, or ArrayBuffer"
);
}
size_t
offset
=
0
;
if
(
nargs
>=
2
&&
vtype
(
args
[
1
])
!=
T_UNDEF
)
{
double
num
=
js_to_number
(
js
,
args
[
1
]);
if
(
num
<
0
)
return
js_mkerr
(
js
,
"randomFillSync offset must be non-negative"
);
offset
=
(
size_t
)
num
;
}
size_t
size
=
len
-
offset
;
if
(
nargs
>=
3
&&
vtype
(
args
[
2
])
!=
T_UNDEF
)
{
double
num
=
js_to_number
(
js
,
args
[
2
]);
if
(
num
<
0
)
return
js_mkerr
(
js
,
"randomFillSync size must be non-negative"
);
size
=
(
size_t
)
num
;
}
if
(
offset
>
len
||
size
>
len
-
offset
)
{
return
js_mkerr
(
js
,
"randomFillSync range is out of bounds"
);
}
if
(
crypto_fill_random
(
bytes
+
offset
,
size
)
<
0
)
return
crypto_random_error
(
js
);
return
args
[
0
];
}
// TODO: extend subtle
static
ant_value_t
crypto_subtle_get_algorithm_name
(
ant_t
*
js
,
ant_value_t
algorithm
)
{
if
(
vtype
(
algorithm
)
==
T_STR
)
return
js_tostring_val
(
js
,
algorithm
);
if
(
is_object_type
(
algorithm
))
{
ant_value_t
name
=
js_get
(
js
,
algorithm
,
"name"
);
if
(
is_err
(
name
))
return
name
;
return
js_tostring_val
(
js
,
name
);
}
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Algorithm must be a string or object with a name"
);
}
static
ant_value_t
crypto_subtle_digest_impl
(
ant_t
*
js
,
ant_value_t
algorithm
,
ant_value_t
data
)
{
ant_value_t
algo_val
=
crypto_subtle_get_algorithm_name
(
js
,
algorithm
);
if
(
is_err
(
algo_val
))
return
algo_val
;
size_t
algo_len
=
0
;
const
char
*
algo
=
js_getstr
(
js
,
algo_val
,
&
algo_len
);
if
(
!
algo
||
algo_len
==
0
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid digest algorithm"
);
}
char
algo_name
[
16
];
if
(
algo_len
>=
sizeof
(
algo_name
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Unsupported digest algorithm"
);
}
for
(
size_t
i
=
0
;
i
<
algo_len
;
i
++
)
{
char
ch
=
algo
[
i
];
if
(
ch
>=
'a'
&&
ch
<=
'z'
)
ch
=
(
char
)(
ch
-
(
'a'
-
'A'
));
algo_name
[
i
]
=
ch
;
}
algo_name
[
algo_len
]
=
'\0'
;
const
char
*
evp_name
=
NULL
;
if
(
strcmp
(
algo_name
,
"SHA-1"
)
==
0
||
strcmp
(
algo_name
,
"SHA1"
)
==
0
)
evp_name
=
"sha1"
;
else
if
(
strcmp
(
algo_name
,
"SHA-256"
)
==
0
||
strcmp
(
algo_name
,
"SHA256"
)
==
0
)
evp_name
=
"sha256"
;
else
if
(
strcmp
(
algo_name
,
"SHA-384"
)
==
0
||
strcmp
(
algo_name
,
"SHA384"
)
==
0
)
evp_name
=
"sha384"
;
else
if
(
strcmp
(
algo_name
,
"SHA-512"
)
==
0
||
strcmp
(
algo_name
,
"SHA512"
)
==
0
)
evp_name
=
"sha512"
;
else
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Unsupported digest algorithm"
);
const
EVP_MD
*
md
=
EVP_get_digestbyname
(
evp_name
);
if
(
!
md
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Unsupported digest algorithm"
);
const
uint8_t
*
bytes
=
NULL
;
size_t
len
=
0
;
if
(
!
buffer_source_get_bytes
(
js
,
data
,
&
bytes
,
&
len
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"digest data must be an ArrayBuffer, TypedArray, DataView, or Buffer"
);
}
unsigned
char
digest
[
EVP_MAX_MD_SIZE
];
unsigned
int
digest_len
=
0
;
if
(
EVP_Digest
(
bytes
,
len
,
digest
,
&
digest_len
,
md
,
NULL
)
!=
1
)
{
return
js_mkerr
(
js
,
"Digest failed"
);
}
ArrayBufferData
*
buffer
=
create_array_buffer_data
(
digest_len
);
if
(
!
buffer
)
return
js_mkerr
(
js
,
"Out of memory"
);
if
(
digest_len
>
0
)
memcpy
(
buffer
->
data
,
digest
,
digest_len
);
return
create_arraybuffer_obj
(
js
,
buffer
);
}
static
ant_value_t
js_crypto_subtle_digest
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
promise
=
js_mkpromise
(
js
);
if
(
nargs
<
2
)
{
js_reject_promise
(
js
,
promise
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"subtle.digest requires algorithm and data"
));
return
promise
;
}
ant_value_t
result
=
crypto_subtle_digest_impl
(
js
,
args
[
0
],
args
[
1
]);
if
(
is_err
(
result
))
js_reject_promise
(
js
,
promise
,
result
);
else
js_resolve_promise
(
js
,
promise
,
result
);
return
promise
;
}
static
ant_value_t
js_hash_update
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_val
=
js_getthis
(
js
);
ant_hash_state_t
*
state
=
NULL
;
const
uint8_t
*
bytes
=
NULL
;
size_t
len
=
0
;
uint8_t
*
owned
=
NULL
;
ant_value_t
err
;
err
=
crypto_require_hash_state
(
js
,
this_val
,
&
state
);
if
(
is_err
(
err
))
return
err
;
if
(
state
->
finalized
)
return
js_mkerr
(
js
,
"Hash digest already called"
);
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"Hash.update requires data"
);
ant_value_t
encoding
=
(
nargs
>=
2
)
?
args
[
1
]
:
js_mkundef
();
err
=
crypto_get_input_bytes
(
js
,
args
[
0
],
encoding
,
&
bytes
,
&
len
,
&
owned
);
if
(
is_err
(
err
))
goto
cleanup
;
if
(
EVP_DigestUpdate
(
state
->
ctx
,
bytes
,
len
)
!=
1
)
{
err
=
js_mkerr
(
js
,
"Hash update failed"
);
goto
cleanup
;
}
err
=
this_val
;
cleanup
:
if
(
owned
)
free
(
owned
);
return
err
;
}
static
ant_value_t
js_hash_digest
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_hash_state_t
*
state
=
NULL
;
ant_value_t
err
=
crypto_require_hash_state
(
js
,
js_getthis
(
js
),
&
state
);
if
(
is_err
(
err
))
return
err
;
if
(
state
->
finalized
)
return
js_mkerr
(
js
,
"Hash digest already called"
);
if
(
EVP_DigestFinal_ex
(
state
->
ctx
,
state
->
digest
,
&
state
->
digest_len
)
!=
1
)
{
return
js_mkerr
(
js
,
"Hash digest failed"
);
}
state
->
finalized
=
true
;
EVP_MD_CTX_free
(
state
->
ctx
);
state
->
ctx
=
NULL
;
return
crypto_digest_result
(
js
,
state
->
digest
,
state
->
digest_len
,
nargs
>=
1
?
args
[
0
]
:
js_mkundef
());
}
static
ant_value_t
js_crypto_create_hash
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"createHash requires an algorithm"
);
ant_value_t
algo_val
=
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
algo_val
))
return
algo_val
;
size_t
algo_len
=
0
;
const
char
*
algo
=
js_getstr
(
js
,
algo_val
,
&
algo_len
);
if
(
!
algo
||
algo_len
==
0
)
return
js_mkerr
(
js
,
"Invalid hash algorithm"
);
char
*
algo_name
=
strndup
(
algo
,
algo_len
);
if
(
!
algo_name
)
return
js_mkerr
(
js
,
"Out of memory"
);
const
EVP_MD
*
md
=
EVP_get_digestbyname
(
algo_name
);
free
(
algo_name
);
if
(
!
md
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Unsupported hash algorithm"
);
ant_hash_state_t
*
state
=
calloc
(
1
,
sizeof
(
*
state
));
if
(
!
state
)
return
js_mkerr
(
js
,
"Out of memory"
);
state
->
ctx
=
EVP_MD_CTX_new
();
state
->
md
=
md
;
if
(
!
state
->
ctx
||
EVP_DigestInit_ex
(
state
->
ctx
,
md
,
NULL
)
!=
1
)
{
if
(
state
->
ctx
)
EVP_MD_CTX_free
(
state
->
ctx
);
free
(
state
);
return
js_mkerr
(
js
,
"Failed to initialize hash"
);
}
ant_value_t
obj
=
js_mkobj
(
js
);
js_set
(
js
,
obj
,
"update"
,
js_mkfun
(
js_hash_update
));
js_set
(
js
,
obj
,
"digest"
,
js_mkfun
(
js_hash_digest
));
js_set_slot
(
obj
,
SLOT_DATA
,
ANT_PTR
(
state
));
js_set_sym
(
js
,
obj
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"Hash"
,
4
));
return
obj
;
}
static
ant_value_t
js_crypto_hash
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
algo_val
;
ant_value_t
output_encoding
;
const
char
*
algo
;
size_t
algo_len
=
0
;
char
*
algo_name
=
NULL
;
const
EVP_MD
*
md
=
NULL
;
const
uint8_t
*
bytes
=
NULL
;
size_t
len
=
0
;
uint8_t
*
owned
=
NULL
;
unsigned
char
digest
[
EVP_MAX_MD_SIZE
];
unsigned
int
digest_len
=
0
;
ant_value_t
err
;
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"hash requires algorithm and data"
);
output_encoding
=
nargs
>=
3
?
args
[
2
]
:
js_mkundef
();
algo_val
=
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
algo_val
))
return
algo_val
;
algo
=
js_getstr
(
js
,
algo_val
,
&
algo_len
);
if
(
!
algo
||
algo_len
==
0
)
return
js_mkerr
(
js
,
"Invalid hash algorithm"
);
algo_name
=
strndup
(
algo
,
algo_len
);
if
(
!
algo_name
)
return
js_mkerr
(
js
,
"Out of memory"
);
md
=
EVP_get_digestbyname
(
algo_name
);
free
(
algo_name
);
if
(
!
md
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Unsupported hash algorithm"
);
err
=
crypto_get_input_bytes
(
js
,
args
[
1
],
js_mkundef
(),
&
bytes
,
&
len
,
&
owned
);
if
(
is_err
(
err
))
goto
cleanup
;
if
(
EVP_Digest
(
bytes
,
len
,
digest
,
&
digest_len
,
md
,
NULL
)
!=
1
)
{
err
=
js_mkerr
(
js
,
"Hash failed"
);
goto
cleanup
;
}
err
=
crypto_digest_result
(
js
,
digest
,
digest_len
,
output_encoding
);
cleanup
:
if
(
owned
)
free
(
owned
);
return
err
;
}
static
ant_value_t
create_crypto_obj
(
ant_t
*
js
)
{
ant_value_t
crypto_obj
=
js_mkobj
(
js
);
ant_value_t
subtle_obj
=
js_mkobj
(
js
);
js_set
(
js
,
crypto_obj
,
"random"
,
js_mkfun
(
js_crypto_random
));
js_set
(
js
,
crypto_obj
,
"randomBytes"
,
js_mkfun
(
js_crypto_random_bytes
));
js_set
(
js
,
crypto_obj
,
"randomFillSync"
,
js_mkfun
(
js_crypto_random_fill_sync
));
js_set
(
js
,
crypto_obj
,
"randomUUID"
,
js_mkfun
(
js_crypto_random_uuid
));
js_set
(
js
,
crypto_obj
,
"randomUUIDv7"
,
js_mkfun
(
js_crypto_random_uuidv7
));
js_set
(
js
,
crypto_obj
,
"getRandomValues"
,
js_mkfun
(
js_crypto_get_random_values
));
js_set
(
js
,
subtle_obj
,
"digest"
,
js_mkfun
(
js_crypto_subtle_digest
));
js_set_sym
(
js
,
subtle_obj
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"SubtleCrypto"
,
12
));
js_set
(
js
,
crypto_obj
,
"subtle"
,
subtle_obj
);
js_set_sym
(
js
,
crypto_obj
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"Crypto"
,
6
));
return
crypto_obj
;
}
void
init_crypto_module
()
{
ant_t
*
js
=
rt
->
js
;
ant_value_t
crypto_obj
=
create_crypto_obj
(
js
);
js_set
(
js
,
js_glob
(
js
),
"crypto"
,
crypto_obj
);
}
ant_value_t
crypto_library
(
ant_t
*
js
)
{
ant_value_t
lib
=
js_mkobj
(
js
);
ant_value_t
webcrypto
=
create_crypto_obj
(
js
);
js_set
(
js
,
lib
,
"webcrypto"
,
webcrypto
);
js_set
(
js
,
lib
,
"hash"
,
js_mkfun
(
js_crypto_hash
));
js_set
(
js
,
lib
,
"createHash"
,
js_mkfun
(
js_crypto_create_hash
));
js_set
(
js
,
lib
,
"randomBytes"
,
js_mkfun
(
js_crypto_random_bytes
));
js_set
(
js
,
lib
,
"randomFillSync"
,
js_mkfun
(
js_crypto_random_fill_sync
));
js_set
(
js
,
lib
,
"randomUUID"
,
js_mkfun
(
js_crypto_random_uuid
));
js_set
(
js
,
lib
,
"getRandomValues"
,
js_mkfun
(
js_crypto_get_random_values
));
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"crypto"
,
6
));
return
lib
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 7:20 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
541670
Default Alt Text
crypto.c (20 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment