diff --git a/lib/archethic/crypto.ex b/lib/archethic/crypto.ex index 58f5fd4e9..a7f7f49c1 100755 --- a/lib/archethic/crypto.ex +++ b/lib/archethic/crypto.ex @@ -585,14 +585,24 @@ defmodule Archethic.Crypto do 40, 0, 68, 224, 177, 110, 180, 24>> ``` """ - @spec ec_encrypt(message :: binary(), public_key :: key()) :: binary() - def ec_encrypt(message, <> = _public_key) + @spec ec_encrypt( + message :: binary(), + public_key :: key(), + ephemeral_entropy_priv_key :: binary() | nil + ) :: + binary() + def ec_encrypt( + message, + <> = _public_key, + ephemeral_entropy_priv_key \\ nil + ) when is_binary(message) do start_time = System.monotonic_time() curve = ID.to_curve(curve_id) - {ephemeral_public_key, ephemeral_private_key} = generate_ephemeral_encryption_keys(curve) + {ephemeral_public_key, ephemeral_private_key} = + generate_ephemeral_encryption_keys(curve, ephemeral_entropy_priv_key) # Derivate secret using ECDH with the given public key and the ephemeral private key shared_key = @@ -618,8 +628,14 @@ defmodule Archethic.Crypto do <> end - defp generate_ephemeral_encryption_keys(:ed25519), do: :crypto.generate_key(:ecdh, :x25519) - defp generate_ephemeral_encryption_keys(curve), do: :crypto.generate_key(:ecdh, curve) + defp generate_ephemeral_encryption_keys(:ed25519, ephemeral_entropy_priv_key), + do: generate_ephemeral_encryption_keys(:x25519, ephemeral_entropy_priv_key) + + defp generate_ephemeral_encryption_keys(curve, nil), + do: :crypto.generate_key(:ecdh, curve) + + defp generate_ephemeral_encryption_keys(curve, ephemeral_entropy_priv_key), + do: :crypto.generate_key(:ecdh, curve, ephemeral_entropy_priv_key) defp derivate_secrets(dh_key) do pseudorandom_key = :crypto.hash(:sha256, dh_key) diff --git a/test/archethic/crypto_test.exs b/test/archethic/crypto_test.exs index 61e3aa23d..53baf7187 100644 --- a/test/archethic/crypto_test.exs +++ b/test/archethic/crypto_test.exs @@ -14,6 +14,16 @@ defmodule CryptoTest do doctest Crypto + test "giving a seed always result in the same result" do + ephemeral_entropy_priv_key = :crypto.strong_rand_bytes(32) + {pub, _} = Crypto.generate_deterministic_keypair("seed", :secp256r1) + + assert Crypto.ec_encrypt("msg", pub) != Crypto.ec_encrypt("msg", pub) + + assert Crypto.ec_encrypt("msg", pub, ephemeral_entropy_priv_key) == + Crypto.ec_encrypt("msg", pub, ephemeral_entropy_priv_key) + end + property "symmetric aes encryption and decryption" do check all( aes_key <- StreamData.binary(length: 32), @@ -35,6 +45,18 @@ defmodule CryptoTest do end end + property "symmetric EC encryption and decryption with ECDSA (with fixed ephemeral_entropy_priv_key)" do + check all( + seed <- StreamData.binary(length: 32), + data <- StreamData.binary(min_length: 1), + ephemeral_entropy_priv_key <- StreamData.binary(length: 32) + ) do + {pub, pv} = Crypto.generate_deterministic_keypair(seed, :secp256r1) + cipher = Crypto.ec_encrypt(data, pub, :crypto.hash(:sha256, ephemeral_entropy_priv_key)) + is_binary(cipher) and data == Crypto.ec_decrypt!(cipher, pv) + end + end + property "symmetric EC encryption and decryption with Ed25519" do check all( seed <- StreamData.binary(length: 32), @@ -46,6 +68,18 @@ defmodule CryptoTest do end end + property "symmetric EC encryption and decryption with Ed25519 (with fixed ephemeral_entropy_priv_key)" do + check all( + seed <- StreamData.binary(length: 32), + data <- StreamData.binary(min_length: 1), + ephemeral_entropy_priv_key <- StreamData.binary(length: 32) + ) do + {pub, pv} = Crypto.generate_deterministic_keypair(seed, :ed25519) + cipher = Crypto.ec_encrypt(data, pub, :crypto.hash(:sha256, ephemeral_entropy_priv_key)) + is_binary(cipher) and data == Crypto.ec_decrypt!(cipher, pv) + end + end + test "decrypt_and_set_storage_nonce/1 should decrypt storage nonce using node last key and and load storage nonce" do storage_nonce = :crypto.strong_rand_bytes(32)