diff --git a/lib/bls.ex b/lib/bls.ex index 426b6e6a2..cc6320e3e 100644 --- a/lib/bls.ex +++ b/lib/bls.ex @@ -54,6 +54,11 @@ defmodule Bls do :erlang.nif_error(:nif_not_loaded) end + @spec derive_pubkey(privkey()) :: {:ok, pubkey()} | {:error, any()} + def derive_pubkey(_private_key) do + :erlang.nif_error(:nif_not_loaded) + end + ##### Helpers ##### @doc """ Same as ``Bls.verify``, but treats errors as invalid signatures. diff --git a/lib/keystore.ex b/lib/keystore.ex index 5f0359e81..259a7499b 100644 --- a/lib/keystore.ex +++ b/lib/keystore.ex @@ -23,8 +23,15 @@ defmodule Keystore do validate_empty_path!(decoded_json["path"]) privkey = decrypt!(decoded_json["crypto"], password) - # TODO: derive from privkey and validate with this pubkey + pubkey = Map.fetch!(decoded_json, "pubkey") |> parse_binary!() + + {:ok, derived_pubkey} = Bls.derive_pubkey(privkey) + + if derived_pubkey != pubkey do + raise("Keystore secret and public keys don't form a valid pair") + end + {pubkey, privkey} end diff --git a/native/bls_nif/src/lib.rs b/native/bls_nif/src/lib.rs index 57faf24df..f048be374 100644 --- a/native/bls_nif/src/lib.rs +++ b/native/bls_nif/src/lib.rs @@ -161,6 +161,17 @@ fn key_validate<'env>(public_key: Binary) -> Result { Ok(true) } +#[rustler::nif] +fn derive_pubkey<'env>(env: Env<'env>, private_key: Binary) -> Result, String> { + let sk = match SecretKey::deserialize(private_key.as_slice()) { + Ok(sk) => sk, + Err(e) => return Err(format!("{:?}", e)), + }; + let public_key = sk.public_key(); + let public_key_bytes = public_key.serialize(); + + Ok(bytes_to_binary(env, &public_key_bytes)) +} rustler::init!( "Elixir.Bls", @@ -172,6 +183,7 @@ rustler::init!( eth_fast_aggregate_verify, eth_aggregate_pubkeys, verify, - key_validate + key_validate, + derive_pubkey ] ); diff --git a/test/unit/bls_test.exs b/test/unit/bls_test.exs index 3440eb4f0..fdf489bfa 100644 --- a/test/unit/bls_test.exs +++ b/test/unit/bls_test.exs @@ -17,4 +17,21 @@ defmodule BlsTest do assert Bls.key_validate(invalid_public_key) == {:error, "BlstError(BLST_BAD_ENCODING)"} end end + + describe "Private to public key" do + test "return the correct public key for a private key" do + valid_public_key = + Base.decode16!( + "8abb15ca57942b6225af4710bbb74ce8466e99fdc2264d9ffd3b335c7396667e45f537ff1f75ed5afa00585db274f3b6", + case: :mixed + ) + + private_key = + Base.decode16!("18363054f52f3f1fdc9ae50d271de853c582c652ebe8dd0f261da3b00cd98984", + case: :mixed + ) + + assert Bls.derive_pubkey(private_key) == {:ok, valid_public_key} + end + end end