Skip to content

Commit 9c99b60

Browse files
committed
Merge pull request #1575 from pguyot/w06/vendor-crypto-from-mbedtls
Add support for crypto with emscripten by fetching mbedtls This effectively adds support for `erlang:md5/1` on emscripten as listed in #1509 These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 943c798 + f1537d7 commit 9c99b60

File tree

8 files changed

+184
-14
lines changed

8 files changed

+184
-14
lines changed

.github/workflows/wasm-build.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
cd build
4848
cmake .. -G Ninja -DAVM_WARNINGS_ARE_ERRORS=ON
4949
# test_eavmlib does not work with wasm due to http + ssl test
50-
ninja AtomVM atomvmlib test_etest test_alisp hello_world run_script call_cast html5_events wasm_webserver
50+
ninja AtomVM atomvmlib erlang_test_modules test_etest test_alisp test_estdlib hello_world run_script call_cast html5_events wasm_webserver
5151
5252
- name: Upload AtomVM and test modules
5353
uses: actions/upload-artifact@v4
@@ -112,9 +112,11 @@ jobs:
112112
node src/AtomVM.js ../../../../build/examples/erlang/hello_world.beam ../../../../build/libs/eavmlib/src/eavmlib.avm
113113
# Run tests that pass
114114
node src/AtomVM.js ../../../../build/tests/libs/alisp/test_alisp.avm
115+
node src/AtomVM.js ../../../../build/tests/libs/estdlib/test_estdlib.avm
115116
# test_eavmlib does not work with wasm due to http + ssl test
116117
# node src/AtomVM.js ../../../../build/tests/libs/eavmlib/test_eavmlib.avm
117118
node src/AtomVM.js ../../../../build/tests/libs/etest/test_etest.avm
119+
node src/AtomVM.js ../../../../build/tests/erlang_tests/test_crypto.beam
118120
119121
- name: "Rename and write sha256sum (node)"
120122
if: startsWith(github.ref, 'refs/tags/')

CMakeModules/FetchMbedTLS.cmake

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#
2+
# This file is part of AtomVM.
3+
#
4+
# Copyright 2025 Paul Guyot <[email protected]>
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
#
20+
21+
include(FetchContent)
22+
23+
FetchContent_Declare(
24+
mbedtls
25+
GIT_REPOSITORY http://github.com/mbed-TLS/mbedtls.git
26+
GIT_TAG v3.6.2
27+
GIT_SHALLOW 1
28+
)
29+
30+
FetchContent_MakeAvailable(mbedtls)

src/platforms/emscripten/src/lib/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,19 @@
2020

2121
cmake_minimum_required (VERSION 3.13)
2222
project (libAtomVMPlatformEmscripten)
23+
include(FetchMbedTLS)
2324

2425
set(HEADER_FILES
2526
emscripten_sys.h
27+
"../../../../libAtomVM/otp_crypto.h"
2628
)
2729

2830
set(SOURCE_FILES
2931
platform_defaultatoms.c
3032
platform_nifs.c
3133
smp.c
3234
sys.c
35+
"../../../../libAtomVM/otp_crypto.c"
3336
)
3437

3538
set(
@@ -41,3 +44,4 @@ add_library(libAtomVM${PLATFORM_LIB_SUFFIX} ${SOURCE_FILES} ${HEADER_FILES})
4144
target_compile_features(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC c_std_11)
4245

4346
target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC libAtomVM)
47+
target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC MbedTLS::mbedcrypto)

src/platforms/emscripten/src/lib/emscripten_sys.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@
3333
#include <emscripten/fetch.h>
3434
#include <emscripten/promise.h>
3535

36+
#include <mbedtls/ctr_drbg.h>
37+
#include <mbedtls/entropy.h>
38+
39+
#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000)
40+
#include <mbedtls/build_info.h>
41+
#else
42+
#include <mbedtls/config.h>
43+
#endif
44+
45+
#include "sys_mbedtls.h"
46+
3647
struct PromiseResource
3748
{
3849
em_promise_t promise;
@@ -104,6 +115,18 @@ struct EmscriptenPlatformData
104115
struct ListHead messages;
105116
ErlNifResourceType *promise_resource_type;
106117
ErlNifResourceType *htmlevent_user_data_resource_type;
118+
119+
#ifndef AVM_NO_SMP
120+
Mutex *entropy_mutex;
121+
#endif
122+
mbedtls_entropy_context entropy_ctx;
123+
bool entropy_is_initialized;
124+
125+
#ifndef AVM_NO_SMP
126+
Mutex *random_mutex;
127+
#endif
128+
mbedtls_ctr_drbg_context random_ctx;
129+
bool random_is_initialized;
107130
};
108131

109132
void sys_enqueue_emscripten_cast_message(GlobalContext *glb, const char *target, const char *message);

src/platforms/emscripten/src/lib/platform_nifs.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <interop.h>
2626
#include <memory.h>
2727
#include <nifs.h>
28+
#include <otp_crypto.h>
2829
#include <term.h>
2930
#include <term_typedef.h>
3031

@@ -771,6 +772,9 @@ const struct Nif *platform_nifs_get_nif(const char *nifname)
771772
if (strcmp("atomvm:random/0", nifname) == 0) {
772773
return &atomvm_random_nif;
773774
}
775+
if (memcmp("crypto:", nifname, strlen("crypro:")) == 0) {
776+
return otp_crypto_nif_get_nif(nifname);
777+
}
774778
if (memcmp("emscripten:", nifname, strlen("emscripten:"))) {
775779
return NULL;
776780
}

src/platforms/emscripten/src/lib/sys.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,20 @@ void sys_init_platform(GlobalContext *glb)
169169
fprintf(stderr, "Cannot initialize promise_resource_type");
170170
AVM_ABORT();
171171
}
172+
173+
#ifndef AVM_NO_SMP
174+
platform->entropy_mutex = smp_mutex_create();
175+
if (IS_NULL_PTR(platform->entropy_mutex)) {
176+
AVM_ABORT();
177+
}
178+
platform->random_mutex = smp_mutex_create();
179+
if (IS_NULL_PTR(platform->random_mutex)) {
180+
AVM_ABORT();
181+
}
182+
#endif
183+
platform->entropy_is_initialized = false;
184+
platform->random_is_initialized = false;
185+
172186
glb->platform_data = platform;
173187
}
174188

@@ -177,6 +191,12 @@ void sys_free_platform(GlobalContext *glb)
177191
struct EmscriptenPlatformData *platform = glb->platform_data;
178192
pthread_cond_destroy(&platform->poll_cond);
179193
pthread_mutex_destroy(&platform->poll_mutex);
194+
if (platform->random_is_initialized) {
195+
mbedtls_ctr_drbg_free(&platform->random_ctx);
196+
}
197+
if (platform->entropy_is_initialized) {
198+
mbedtls_entropy_free(&platform->entropy_ctx);
199+
}
180200
free(platform);
181201
}
182202

@@ -730,3 +750,70 @@ term sys_get_info(Context *ctx, term key)
730750
UNUSED(key);
731751
return UNDEFINED_ATOM;
732752
}
753+
754+
int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size)
755+
{
756+
#ifndef MBEDTLS_THREADING_C
757+
struct EmscriptenPlatformData *platform
758+
= CONTAINER_OF(entropy, struct EmscriptenPlatformData, entropy_ctx);
759+
SMP_MUTEX_LOCK(platform->entropy_mutex);
760+
int result = mbedtls_entropy_func(entropy, buf, size);
761+
SMP_MUTEX_UNLOCK(platform->entropy_mutex);
762+
763+
return result;
764+
#else
765+
return mbedtls_entropy_func(entropy, buf, size);
766+
#endif
767+
}
768+
769+
mbedtls_entropy_context *sys_mbedtls_get_entropy_context_lock(GlobalContext *global)
770+
{
771+
struct EmscriptenPlatformData *platform = global->platform_data;
772+
773+
SMP_MUTEX_LOCK(platform->entropy_mutex);
774+
775+
if (!platform->entropy_is_initialized) {
776+
mbedtls_entropy_init(&platform->entropy_ctx);
777+
platform->entropy_is_initialized = true;
778+
}
779+
780+
return &platform->entropy_ctx;
781+
}
782+
783+
void sys_mbedtls_entropy_context_unlock(GlobalContext *global)
784+
{
785+
struct EmscriptenPlatformData *platform = global->platform_data;
786+
SMP_MUTEX_UNLOCK(platform->entropy_mutex);
787+
}
788+
789+
mbedtls_ctr_drbg_context *sys_mbedtls_get_ctr_drbg_context_lock(GlobalContext *global)
790+
{
791+
struct EmscriptenPlatformData *platform = global->platform_data;
792+
793+
SMP_MUTEX_LOCK(platform->random_mutex);
794+
795+
if (!platform->random_is_initialized) {
796+
mbedtls_ctr_drbg_init(&platform->random_ctx);
797+
798+
mbedtls_entropy_context *entropy_ctx = sys_mbedtls_get_entropy_context_lock(global);
799+
// Safe to unlock it now, sys_mbedtls_entropy_func will lock it again later
800+
sys_mbedtls_entropy_context_unlock(global);
801+
802+
const char *seed = "AtomVM Mbed-TLS initial seed.";
803+
int seed_len = strlen(seed);
804+
int seed_err = mbedtls_ctr_drbg_seed(&platform->random_ctx, sys_mbedtls_entropy_func,
805+
entropy_ctx, (const unsigned char *) seed, seed_len);
806+
if (UNLIKELY(seed_err != 0)) {
807+
abort();
808+
}
809+
platform->random_is_initialized = true;
810+
}
811+
812+
return &platform->random_ctx;
813+
}
814+
815+
void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global)
816+
{
817+
struct EmscriptenPlatformData *platform = global->platform_data;
818+
SMP_MUTEX_UNLOCK(platform->random_mutex);
819+
}

src/platforms/emscripten/src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ static int start(void)
104104
fprintf(stdout, "\n");
105105

106106
int status;
107-
if (ret_value == OK_ATOM) {
107+
if (ret_value == OK_ATOM || ret_value == term_from_int(0)) {
108108
status = EXIT_SUCCESS;
109109
} else {
110110
status = EXIT_FAILURE;

tests/libs/estdlib/tests.erl

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,26 @@
2323
-export([start/0]).
2424

2525
start() ->
26-
ok = etest:test(get_tests(get_otp_version())).
26+
OTPVersion = get_otp_version(),
27+
NonNetworkingTests = get_non_networking_tests(OTPVersion),
28+
Networking =
29+
case OTPVersion of
30+
atomvm ->
31+
case atomvm:platform() of
32+
emscripten ->
33+
false;
34+
_ ->
35+
true
36+
end;
37+
_ ->
38+
true
39+
end,
40+
NetworkingTests =
41+
if
42+
Networking -> get_networking_tests(OTPVersion);
43+
true -> []
44+
end,
45+
ok = etest:test(NonNetworkingTests ++ NetworkingTests).
2746

2847
get_otp_version() ->
2948
case erlang:system_info(machine) of
@@ -33,25 +52,19 @@ get_otp_version() ->
3352
atomvm
3453
end.
3554

36-
get_tests(OTPVersion) when
37-
(is_integer(OTPVersion) andalso OTPVersion >= 27) orelse OTPVersion == atomvm
55+
% test_sets heavily relies on is_equal that is from OTP-27
56+
get_non_networking_tests(OTPVersion) when
57+
(is_integer(OTPVersion) andalso OTPVersion >= 27) orelse OTPVersion =:= atomvm
3858
->
39-
[test_tcp_socket, test_udp_socket, test_net, test_ssl, test_sets | get_tests(undefined)];
40-
get_tests(OTPVersion) when
41-
(is_integer(OTPVersion) andalso OTPVersion >= 24)
42-
->
43-
% test_sets heavily relies on is_equal that is from OTP-27
44-
[test_tcp_socket, test_udp_socket, test_net, test_ssl | get_tests(undefined)];
45-
get_tests(_OTPVersion) ->
59+
[test_sets | get_non_networking_tests(undefined)];
60+
get_non_networking_tests(_OTPVersion) ->
4661
[
4762
test_apply,
4863
test_lists,
4964
test_calendar,
5065
test_gen_event,
5166
test_gen_server,
5267
test_gen_statem,
53-
test_gen_udp,
54-
test_gen_tcp,
5568
test_io_lib,
5669
test_logger,
5770
test_maps,
@@ -62,3 +75,10 @@ get_tests(_OTPVersion) ->
6275
test_supervisor,
6376
test_lists_subtraction
6477
].
78+
79+
get_networking_tests(OTPVersion) when
80+
(is_integer(OTPVersion) andalso OTPVersion >= 24) orelse OTPVersion =:= atomvm
81+
->
82+
[test_tcp_socket, test_udp_socket, test_net, test_ssl | get_networking_tests(undefined)];
83+
get_networking_tests(_OTPVersion) ->
84+
[test_gen_udp, test_gen_tcp].

0 commit comments

Comments
 (0)