Skip to content

Commit

Permalink
add an optional seed to ec_encrypt to achieve idempotency
Browse files Browse the repository at this point in the history
  • Loading branch information
bchamagne committed Jun 6, 2024
1 parent 04f613b commit 4a3d5f6
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
22 changes: 17 additions & 5 deletions lib/archethic/crypto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -585,14 +585,20 @@ 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, <<curve_id::8, _::8, public_key::binary>> = _public_key)
@spec ec_encrypt(message :: binary(), public_key :: key(), random_seed :: binary() | :undefined) ::
binary()
def ec_encrypt(
message,
<<curve_id::8, _::8, public_key::binary>> = _public_key,
random_seed \\ :undefined
)
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, random_seed)

# Derivate secret using ECDH with the given public key and the ephemeral private key
shared_key =
Expand All @@ -618,8 +624,14 @@ defmodule Archethic.Crypto do
<<ephemeral_public_key::binary, tag::binary, cipher::binary>>
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, random_seed),
do: generate_ephemeral_encryption_keys(:x25519, random_seed)

defp generate_ephemeral_encryption_keys(curve, :undefined),
do: :crypto.generate_key(:ecdh, curve)

defp generate_ephemeral_encryption_keys(curve, random_seed),
do: :crypto.generate_key(:ecdh, curve, :crypto.hash(:sha256, random_seed))

defp derivate_secrets(dh_key) do
pseudorandom_key = :crypto.hash(:sha256, dh_key)
Expand Down
34 changes: 34 additions & 0 deletions test/archethic/crypto_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ defmodule CryptoTest do

doctest Crypto

test "giving a seed always result in the same result" do
random_seed = :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, random_seed) ==
Crypto.ec_encrypt("msg", pub, random_seed)
end

property "symmetric aes encryption and decryption" do
check all(
aes_key <- StreamData.binary(length: 32),
Expand All @@ -35,6 +45,18 @@ defmodule CryptoTest do
end
end

property "symmetric EC encryption and decryption with ECDSA (with fixed random_seed)" do
check all(
seed <- StreamData.binary(length: 32),
data <- StreamData.binary(min_length: 1),
random_seed <- StreamData.binary(length: 32)
) do
{pub, pv} = Crypto.generate_deterministic_keypair(seed, :secp256r1)
cipher = Crypto.ec_encrypt(data, pub, random_seed)
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),
Expand All @@ -46,6 +68,18 @@ defmodule CryptoTest do
end
end

property "symmetric EC encryption and decryption with Ed25519 (with fixed random_seed)" do
check all(
seed <- StreamData.binary(length: 32),
data <- StreamData.binary(min_length: 1),
random_seed <- StreamData.binary(length: 32)
) do
{pub, pv} = Crypto.generate_deterministic_keypair(seed, :ed25519)
cipher = Crypto.ec_encrypt(data, pub, random_seed)
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)

Expand Down

0 comments on commit 4a3d5f6

Please sign in to comment.