From 4a3d5f65af684bd3e814e791fcbda0ff4f9c8ac1 Mon Sep 17 00:00:00 2001 From: bchamagne Date: Thu, 6 Jun 2024 17:53:55 +0200 Subject: [PATCH 1/2] add an optional seed to ec_encrypt to achieve idempotency --- lib/archethic/crypto.ex | 22 +++++++++++++++++----- test/archethic/crypto_test.exs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/lib/archethic/crypto.ex b/lib/archethic/crypto.ex index 58f5fd4e92..f1bb145c50 100755 --- a/lib/archethic/crypto.ex +++ b/lib/archethic/crypto.ex @@ -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, <> = _public_key) + @spec ec_encrypt(message :: binary(), public_key :: key(), random_seed :: binary() | :undefined) :: + binary() + def ec_encrypt( + message, + <> = _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 = @@ -618,8 +624,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, 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) diff --git a/test/archethic/crypto_test.exs b/test/archethic/crypto_test.exs index 61e3aa23d8..1b26f8c70a 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 + 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), @@ -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), @@ -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) From 33536340f42673784326b253ccd217d530374f26 Mon Sep 17 00:00:00 2001 From: bchamagne Date: Fri, 7 Jun 2024 16:06:30 +0200 Subject: [PATCH 2/2] rename random_seed, remove the hash --- lib/archethic/crypto.ex | 20 ++++++++++++-------- test/archethic/crypto_test.exs | 18 +++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/archethic/crypto.ex b/lib/archethic/crypto.ex index f1bb145c50..a7f7f49c1d 100755 --- a/lib/archethic/crypto.ex +++ b/lib/archethic/crypto.ex @@ -585,12 +585,16 @@ defmodule Archethic.Crypto do 40, 0, 68, 224, 177, 110, 180, 24>> ``` """ - @spec ec_encrypt(message :: binary(), public_key :: key(), random_seed :: binary() | :undefined) :: + @spec ec_encrypt( + message :: binary(), + public_key :: key(), + ephemeral_entropy_priv_key :: binary() | nil + ) :: binary() def ec_encrypt( message, <> = _public_key, - random_seed \\ :undefined + ephemeral_entropy_priv_key \\ nil ) when is_binary(message) do start_time = System.monotonic_time() @@ -598,7 +602,7 @@ defmodule Archethic.Crypto do curve = ID.to_curve(curve_id) {ephemeral_public_key, ephemeral_private_key} = - generate_ephemeral_encryption_keys(curve, random_seed) + 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 = @@ -624,14 +628,14 @@ defmodule Archethic.Crypto do <> end - defp generate_ephemeral_encryption_keys(:ed25519, random_seed), - do: generate_ephemeral_encryption_keys(:x25519, random_seed) + 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, :undefined), + defp generate_ephemeral_encryption_keys(curve, nil), 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 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 1b26f8c70a..53baf71870 100644 --- a/test/archethic/crypto_test.exs +++ b/test/archethic/crypto_test.exs @@ -15,13 +15,13 @@ defmodule CryptoTest do doctest Crypto test "giving a seed always result in the same result" do - random_seed = :crypto.strong_rand_bytes(32) + 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, random_seed) == - Crypto.ec_encrypt("msg", pub, random_seed) + 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 @@ -45,14 +45,14 @@ defmodule CryptoTest do end end - property "symmetric EC encryption and decryption with ECDSA (with fixed random_seed)" do + 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), - random_seed <- StreamData.binary(length: 32) + ephemeral_entropy_priv_key <- StreamData.binary(length: 32) ) do {pub, pv} = Crypto.generate_deterministic_keypair(seed, :secp256r1) - cipher = Crypto.ec_encrypt(data, pub, random_seed) + 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 @@ -68,14 +68,14 @@ defmodule CryptoTest do end end - property "symmetric EC encryption and decryption with Ed25519 (with fixed random_seed)" do + 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), - random_seed <- StreamData.binary(length: 32) + ephemeral_entropy_priv_key <- StreamData.binary(length: 32) ) do {pub, pv} = Crypto.generate_deterministic_keypair(seed, :ed25519) - cipher = Crypto.ec_encrypt(data, pub, random_seed) + 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