Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added support for `process_info/1` and `process_info/2` with list argument
- Added `erlang:term_to_binary/2`, `erlang:is_builtin/3` and `erlang:bitstring_to_list/1`
- Added `lists:mapfoldr/3`
- Added ML-KEM-768 (FIPS 203) key encapsulation to `crypto`, requiring libsodium >= 1.0.22
- Added `AVM_STATIC_LIBSODIUM` CMake option to statically link libsodium
- Added `emscripten:run_script_tracked/1` and `emscripten:get_tracked/2` to hold handles to
JavaScript values from Erlang, tying the JavaScript value lifetime to the Erlang term lifetime.
The emscripten module object gained `trackedObjectsMap`, `nextTrackedObjectKey()` and the
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ option(AVM_BUILD_RUNTIME_ONLY "Only build the AtomVM runtime" OFF)
option(COVERAGE "Build for code coverage" OFF)
option(AVM_PRINT_PROCESS_CRASH_DUMPS "Print crash reports when processes die with non-standard reasons" ON)
option(AVM_USE_LIBSODIUM "Enable optional libsodium backend for Ed25519 curve" OFF)
option(AVM_STATIC_LIBSODIUM "Static link libsodium." OFF)
option(AVM_MINIMAL_OPCODES "Reduce VM size by excluding opcodes for optional compiler flags (no_bs_match, no_ssa_opt_bs_ensure)" OFF)

# JIT & execution of precompiled code
Expand Down
8 changes: 7 additions & 1 deletion doc/src/programmers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,13 @@ for details on how to ensure the entropy source is properly initialized before p
cryptographic operations.
```

When AtomVM is built with `-DAVM_USE_LIBSODIUM=ON`, Ed25519 signing and verification (`crypto:sign/4`, `crypto:verify/5`) and X25519 key agreement (`crypto:generate_key/2`, `crypto:compute_key/4`) are also available. This option requires libsodium to be installed on the build host (e.g. `libsodium-dev` on Debian/Ubuntu), or the `espressif/libsodium` component on ESP32.
When AtomVM is built with `-DAVM_USE_LIBSODIUM=ON`, Ed25519 signing and verification
(`crypto:sign/4`, `crypto:verify/5`), X25519 key agreement (`crypto:generate_key/2`,
`crypto:compute_key/4`) and, with libsodium 1.0.22 or later, ML-KEM-768 key encapsulation
(`crypto:encapsulate_key/2`, `crypto:decapsulate_key/3`) are also available. This option requires
libsodium to be installed on the build host (e.g. `libsodium-dev` on Debian/Ubuntu), or the
`espressif/libsodium` component on ESP32. On generic UNIX, `-DAVM_STATIC_LIBSODIUM=ON` links
`libsodium.a` into the AtomVM binary instead of depending on the shared library at run time.

```{important}
**Increase the ESP32 task stack size when using libsodium.**
Expand Down
68 changes: 64 additions & 4 deletions libs/estdlib/src/crypto.erl
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,17 @@
pbkdf2_hmac/5,
hash_equals/2,
strong_rand_bytes/1,
encapsulate_key/2,
decapsulate_key/3,
info_lib/0
]).

-type hash_algorithm() :: md5 | sha | sha224 | sha256 | sha384 | sha512.
-type kem() :: mlkem512 | mlkem768 | mlkem1024.
-type digest() :: binary().

-export_type([kem/0]).

-export_type([hash_state/0]).
-opaque hash_state() :: reference().

Expand Down Expand Up @@ -379,6 +384,7 @@ crypto_final(_State) ->
%% * `ecdh' with `x25519 | secp256k1 | secp256r1 | secp384r1 | secp521r1 |
%% brainpoolP256r1 | brainpoolP384r1 | brainpoolP512r1'
%% * `eddsa' with `ed25519'
%% * `mlkem768' with `[]' (requires libsodium >= 1.0.22)
%%
%% Keys are returned as raw key material, not PEM, DER, or `public_key'
%% records.
Expand All @@ -388,10 +394,9 @@ crypto_final(_State) ->
%% seeded before generating keys.
%% @end
%%-----------------------------------------------------------------------------
-spec generate_key(
Type :: ecdh | eddh | eddsa,
Param :: ecdh_params() | eddsa_params()
) -> {binary(), binary()}.
-spec generate_key
(Type :: ecdh | eddh | eddsa, Param :: ecdh_params() | eddsa_params()) -> {binary(), binary()};
(Type :: kem(), Param :: []) -> {binary(), binary()}.
generate_key(_Type, _Param) ->
erlang:nif_error(undefined).

Expand Down Expand Up @@ -424,6 +429,61 @@ generate_key(_Type, _Param) ->
compute_key(_Type, _OtherPublicKey, _MyPrivateKey, _Param) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param PublicKey the ML-KEM-768 encapsulation (public) key, 1184 bytes
%% @param Type key encapsulation mechanism, `mlkem768'
%% @param OthersPublicKey the other party's encapsulation key (1184 bytes for
%% `mlkem768')
%% @returns `{Secret, EncapSecret}' where `Secret' is the 32-byte shared secret
%% and `EncapSecret' is its 1088-byte encapsulated form
%% @doc ML-KEM (FIPS 203) key encapsulation.
%%
%% Generates a shared secret and the encapsulated form to send to the
%% owner of `OthersPublicKey', who recovers the same secret with their
%% private key. Used to implement post-quantum hybrid SSH key exchange
%% (`mlkem768x25519-sha256').
%%
%% Of the mechanisms OTP names, AtomVM implements `mlkem768' only,
%% because that is the one libsodium provides; `mlkem512' and
%% `mlkem1024' raise `badarg'.
%%
%% Only available on builds with libsodium >= 1.0.22.
%% @end
%%-----------------------------------------------------------------------------
-spec encapsulate_key(Type :: kem(), OthersPublicKey :: binary()) ->
{Secret :: binary(), EncapSecret :: binary()}.
encapsulate_key(_Type, _OthersPublicKey) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Type key encapsulation mechanism, `mlkem768'
%% @param MyPrivKey our decapsulation key (2400 bytes for `mlkem768')
%% @param EncapSecret the encapsulated secret received from the peer
%% (1088 bytes for `mlkem768')
%% @returns the 32-byte shared secret
%% @doc ML-KEM (FIPS 203) key decapsulation.
%%
%% Recovers the secret a peer encapsulated to our public key with
%% {@link encapsulate_key/2}.
%%
%% A ciphertext that does not belong to this key is not reported as an
%% error: FIPS 203 specifies implicit rejection, so decapsulation
%% returns a pseudorandom secret instead, and the mismatch only shows
%% up when the two sides disagree about what they derived.
%%
%% Of the mechanisms OTP names, AtomVM implements `mlkem768' only,
%% because that is the one libsodium provides; `mlkem512' and
%% `mlkem1024' raise `badarg'.
%%
%% Only available on builds with libsodium >= 1.0.22.
%% @end
%%-----------------------------------------------------------------------------
-spec decapsulate_key(
Type :: kem(), MyPrivKey :: binary(), EncapSecret :: binary()
) -> Secret :: binary().
decapsulate_key(_Type, _MyPrivKey, _EncapSecret) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Algorithm signing algorithm (`ecdsa' or `eddsa')
%% @param DigestType hash algorithm identifier for `ecdsa', or `none' for `eddsa'
Expand Down
Loading
Loading