Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,17 @@ jobs:
library-arch: riscv32-linux-gnu-ilp32
jit_target_arch: "riscv32"

# libsodium enabled build
- os: "ubuntu-24.04"
cc: "cc"
cxx: "c++"
cflags: ""
otp: "28"
elixir_version: "1.17"
rebar3_version: "3.24.0"
cmake_opts_other: "-DAVM_USE_LIBSODIUM=ON"
compiler_pkgs: "libsodium-dev"

env:
ImageOS: ${{ matrix.container == 'ubuntu:20.04' && 'ubuntu20' || matrix.os == 'ubuntu-20.04' && 'ubuntu20' || matrix.os == 'ubuntu-22.04' && 'ubuntu22' || matrix.os == 'ubuntu-24.04' && 'ubuntu24' || 'ubuntu24' }}
CC: ${{ matrix.cc }}
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/esp32-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ jobs:
idf-version: 'release-v5.4'
- esp-idf-target: "esp32p4"
idf-version: 'v5.5.2'
- esp-idf-target: "esp32"
idf-version: 'v5.4.3'
libsodium: 'ON'

steps:
- name: Checkout repo
Expand All @@ -76,14 +79,21 @@ jobs:
build-mode: manual
queries: +./code-queries/term-to-non-term-func.ql,./code-queries/non-term-to-term-func.ql,./code-queries/mismatched-atom-string-length.ql,./code-queries/mismatched-free-type.ql,./code-queries/term-use-after-gc.ql,./code-queries/allocations-exceeding-ensure-free.ql,./code-queries/allocations-without-ensure-free.ql

- name: Add libsodium dependency
if: matrix.libsodium == 'ON'
working-directory: ./src/platforms/esp32/
run: |
. $IDF_PATH/export.sh
idf.py add-dependency "espressif/libsodium^1.0.20~4"

- name: Build with idf.py
shell: bash
working-directory: ./src/platforms/esp32/
run: |
. $IDF_PATH/export.sh
export IDF_TARGET=${{matrix.esp-idf-target}}
idf.py set-target ${{matrix.esp-idf-target}}
idf.py build
idf.py ${{ matrix.libsodium == 'ON' && '-DAVM_USE_LIBSODIUM=ON' || '' }} build
idf.py size

- name: Print component size info with idf.py
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ encoding/decoding options, also Elixir `(url_)encode64`/`(url_)decode64` have be
`crypto:mac_finalN/2`
- Added `crypto:info_lib/0`
- Added `erlang:crc32/1`, `erlang:crc32/2` and `erlang:crc32_combine/3`
- Added support for Ed25519 curve when libsodium support is enabled at build time
(`AVM_USE_LIBSODIUM=ON`)

### Changed

Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ option(AVM_CREATE_STACKTRACES "Create stacktraces" ON)
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)

# JIT & execution of precompiled code
if(NOT Erlang_VERSION VERSION_GREATER_EQUAL "23")
Expand Down
2 changes: 2 additions & 0 deletions doc/src/programmers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,8 @@ 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.

## ESP32-specific APIs

Certain APIs are specific to and only supported on the ESP32 platform. This section describes these APIs.
Expand Down
126 changes: 71 additions & 55 deletions libs/estdlib/src/crypto.erl
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,8 @@
-type crypto_opt() :: {encrypt, boolean()} | {padding, padding()}.
-type crypto_opts() :: boolean() | [crypto_opt()].

-type pk_type() :: eddh | eddsa | ecdh.

%% Curves/params accepted by the current AtomVM implementation.
%% Note: not all combinations are supported by every function (see docs below).
-type pk_param() ::
x25519
| x448
| ed25519
| ed448
| secp256r1
| secp384r1
| secp521r1
| secp256k1
| brainpoolP256r1
| brainpoolP384r1
| brainpoolP512r1.

%% ECDSA is currently supported only on short Weierstrass secp* and brainpool curves.
-type ecdsa_curve() ::
%% Named elliptic curves supported by AtomVM (subset of OTP's ec_named_curve()).
-type ec_named_curve() ::
secp256k1
| secp256r1
| secp384r1
Expand All @@ -121,8 +104,22 @@
| brainpoolP384r1
| brainpoolP512r1.

-type ecdsa_private_key() :: [binary() | ecdsa_curve()].
-type ecdsa_public_key() :: [binary() | ecdsa_curve()].
-type edwards_curve_dh() :: x25519.
%% Supported EdDSA curve (Ed25519 via libsodium when AVM_USE_LIBSODIUM is enabled).
-type edwards_curve_ed() :: ed25519.

-type ecdh_params() :: ec_named_curve() | edwards_curve_dh().
-type ecdsa_params() :: ec_named_curve().
-type eddsa_params() :: edwards_curve_ed().

-type ecdsa_private() :: binary().
-type ecdsa_public() :: binary().
-type eddsa_private() :: binary().
-type eddsa_public() :: binary().

-type sha1() :: sha.
-type sha2() :: sha224 | sha256 | sha384 | sha512.
-type ecdsa_digest_type() :: sha1() | sha2().

-type mac_type() :: cmac | hmac.

Expand Down Expand Up @@ -378,20 +375,23 @@ crypto_final(_State) ->
%% @doc Generate a public/private key pair.
%%
%% Supported forms:
%% * `eddh' with `x25519 | x448'
%% * `ecdh' with `x25519 | x448 | secp256k1 | secp256r1 | secp384r1 | secp521r1 |
%% brainpoolP256r1 | brainpoolP384r1 | brainpoolP512r1`
%% * `eddsa' with `ed25519 | ed448' (availability depends on the underlying mbedTLS build)
%% * `eddh' with `x25519'
%% * `ecdh' with `x25519 | secp256k1 | secp256r1 | secp384r1 | secp521r1 |
%% brainpoolP256r1 | brainpoolP384r1 | brainpoolP512r1'
%% * `eddsa' with `ed25519'
%%
%% Keys are returned as **raw exported key material**, not PEM, DER, or `public_key'
%% Keys are returned as raw key material, not PEM, DER, or `public_key'
%% records.
%%
%% Key generation draws from the platform entropy source. Consult
%% your platform documentation to ensure the hardware RNG is properly
%% seeded before generating keys.
%% @end
%%-----------------------------------------------------------------------------
-spec generate_key(Type :: pk_type(), Param :: pk_param()) -> {binary(), binary()}.
-spec generate_key(
Type :: ecdh | eddh | eddsa,
Param :: ecdh_params() | eddsa_params()
) -> {binary(), binary()}.
generate_key(_Type, _Param) ->
erlang:nif_error(undefined).

Expand All @@ -404,38 +404,45 @@ generate_key(_Type, _Param) ->
%% @doc Compute a shared secret.
%%
%% Supported forms:
%% * `eddh' with `x25519 | x448'
%% * `ecdh' with `x25519 | x448 | secp256k1 | secp256r1 | secp384r1 | secp521r1 |
%% brainpoolP256r1 | brainpoolP384r1 | brainpoolP512r1`
%% * `eddh' with `x25519'
%% * `ecdh' with `x25519 | secp256k1 | secp256r1 | secp384r1 | secp521r1 |
%% brainpoolP256r1 | brainpoolP384r1 | brainpoolP512r1'
%%
%% The public/private key binaries must be in the same format as returned by
%% `generate_key/2'.
%%
%% For `x25519', the returned value is the raw shared secret. Applications
%% should derive a symmetric key from it with a KDF before use.
%% @end
%%-----------------------------------------------------------------------------
-spec compute_key(
Type :: pk_type(),
Type :: ecdh | eddh,
OtherPublicKey :: binary(),
MyPrivateKey :: binary(),
Param :: pk_param()
Param :: ecdh_params()
) -> binary().
compute_key(_Type, _OtherPublicKey, _MyPrivateKey, _Param) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Algorithm signing algorithm (AtomVM supports `ecdsa')
%% @param DigestType hash algorithm identifier
%% @param Data message bytes (iodata)
%% @param Algorithm signing algorithm (`ecdsa' or `eddsa')
%% @param DigestType hash algorithm identifier for `ecdsa', or `none' for `eddsa'
%% @param Msg message bytes (iodata)
%% @param Key signing key material
%% @returns Returns a DER-encoded signature as a binary.
%% @returns Returns the signature as a binary.
%% @doc Create a digital signature.
%%
%% AtomVM currently supports:
%% * `Algorithm = ecdsa'
%% * `Key = [PrivateKeyBin, Curve]' where `Curve' is one of
%% `secp256k1 | secp256r1 | secp384r1 | secp521r1 |
%% brainpoolP256r1 | brainpoolP384r1 | brainpoolP512r1'
%% * `Algorithm = eddsa' (requires build with libsodium support)
%% * `Key = [PrivateKeyBin, ed25519]' and `DigestType = none'
%%
%% The signature is returned in **DER** form.
%% Signature encoding depends on `Algorithm':
%% * `ecdsa' returns a DER-encoded signature
%% * `eddsa' with `ed25519' returns a raw 64-byte Ed25519 signature
%%
%% ECDSA signing requires a random nonce internally. The quality of
%% this nonce depends on the platform entropy source. Consult your
Expand All @@ -444,19 +451,19 @@ compute_key(_Type, _OtherPublicKey, _MyPrivateKey, _Param) ->
%% @end
%%-----------------------------------------------------------------------------
-spec sign(
Algorithm :: ecdsa,
DigestType :: hash_algorithm(),
Data :: iodata(),
Key :: ecdsa_private_key()
Algorithm :: ecdsa | eddsa,
DigestType :: ecdsa_digest_type() | none,
Msg :: iodata(),
Key :: [ecdsa_private() | ecdsa_params()] | [eddsa_private() | eddsa_params()]
) -> binary().
sign(_Algorithm, _DigestType, _Data, _Key) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Algorithm verification algorithm (AtomVM supports `ecdsa')
%% @param DigestType hash algorithm identifier
%% @param Data message bytes (iodata)
%% @param Signature DER-encoded signature
%% @param Algorithm verification algorithm (`ecdsa' or `eddsa')
%% @param DigestType hash algorithm identifier for `ecdsa', or `none' for `eddsa'
%% @param Msg message bytes (iodata)
%% @param Signature signature to verify
%% @param Key verification key material
%% @returns Returns `true' if the signature is valid, otherwise `false'.
%% @doc Verify a digital signature.
Expand All @@ -465,17 +472,23 @@ sign(_Algorithm, _DigestType, _Data, _Key) ->
%% * `Algorithm = ecdsa'
%% * `Key = [PublicKeyBin, Curve]' where `Curve' is one of
%% `secp256k1 | secp256r1 | secp384r1 | secp521r1 |
%% brainpoolP256r1 | brainpoolP384r1 | brainpoolP512r1`
%% brainpoolP256r1 | brainpoolP384r1 | brainpoolP512r1'
%% * `Algorithm = eddsa' (requires build with libsodium support)
%% * `Key = [PublicKeyBin, ed25519]' and `DigestType = none'
%%
%% Signature encoding depends on `Algorithm':
%% * `ecdsa' expects a DER-encoded signature
%% * `eddsa' with `ed25519' expects a raw 64-byte Ed25519 signature
%%
%% Invalid DER signatures yield `false' (not an exception).
%% Invalid signatures yield `false' (not an exception).
%% @end
%%-----------------------------------------------------------------------------
-spec verify(
Algorithm :: ecdsa,
DigestType :: hash_algorithm(),
Data :: iodata(),
Algorithm :: ecdsa | eddsa,
DigestType :: ecdsa_digest_type() | none,
Msg :: iodata(),
Signature :: binary(),
Key :: ecdsa_public_key()
Key :: [ecdsa_public() | ecdsa_params()] | [eddsa_public() | eddsa_params()]
) -> boolean().
verify(_Algorithm, _DigestType, _Data, _Signature, _Key) ->
erlang:nif_error(undefined).
Expand Down Expand Up @@ -603,15 +616,18 @@ strong_rand_bytes(_N) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns Returns a list of tuples describing the crypto library name, version number,
%% and version string.
%% @returns Returns a list of tuples describing the crypto libraries used by crypto.
%% @doc Get the name and version of the libraries used by crypto.
%%
%% Returns a list containing a single tuple `{Name, VerNum, VerStr}' where:
%% * `Name' is the library name as a binary (e.g., `<<"mbedtls">>')
%% Returns a list of one or more tuples `{Name, VerNum, VerStr}' where:
%% * `Name' is the library name as a binary (for example, `<<"mbedtls">>'
%% or `<<"libsodium">>')
%% * `VerNum' is the numeric version according to the library's versioning scheme
%% * `VerStr' is the version string as a binary
%%
%% AtomVM may return multiple entries, for example one for Mbed TLS and one
%% for libsodium.
%%
%% Example: `[{<<"mbedtls">>, 50790144, <<"Mbed TLS 3.6.1">>}]'
%% @end
%%-----------------------------------------------------------------------------
Expand Down
Loading
Loading