Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ETF Milestone 2: Add ACSS Recovery Capability #6

Closed
wants to merge 13 commits into from
3,533 changes: 1,793 additions & 1,740 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions substrate/client/keystore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ thiserror = { workspace = true }
sp-application-crypto = { path = "../../primitives/application-crypto" }
sp-core = { path = "../../primitives/core" }
sp-keystore = { path = "../../primitives/keystore" }
etf-crypto-primitives = { git = "http://github.com/ideal-lab5/etf-sdk", branch = "w3fbls-migration" }
ark-serialize = "0.4.0"
w3f-bls = "0.1.3"

[dev-dependencies]
tempfile = "3.1.0"
Expand All @@ -44,3 +47,5 @@ bandersnatch-experimental = [
"sp-core/bandersnatch-experimental",
"sp-keystore/bandersnatch-experimental",
]

etf = []
29 changes: 24 additions & 5 deletions substrate/client/keystore/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ use sp_core::bandersnatch;

sp_keystore::bls_experimental_enabled! {
use sp_core::{bls377, bls381, ecdsa_bls377, KeccakHasher};
pub const ETF_KEY_TYPE: KeyTypeId = KeyTypeId(*b"etfn");
}

use crate::{Error, Result};

/// A local based keystore that is either memory-based or filesystem-based.
Expand Down Expand Up @@ -405,20 +405,39 @@ impl Keystore for LocalKeystore {
self.sign::<ecdsa_bls377::Pair>(key_type, public, msg)
}

fn ecdsa_bls377_sign_with_keccak256(
fn ecdsa_bls377_sign_with_keccak256(
&self,
key_type: KeyTypeId,
public: &ecdsa_bls377::Public,
msg: &[u8],
) -> std::result::Result<Option<ecdsa_bls377::Signature>, TraitError> {
let sig = self.0
.read()
.key_pair_by_type::<ecdsa_bls377::Pair>(public, key_type)?
.map(|pair| pair.sign_with_hasher::<KeccakHasher>(msg));
.read()
.key_pair_by_type::<ecdsa_bls377::Pair>(public, key_type)?
.map(|pair| pair.sign_with_hasher::<KeccakHasher>(msg));
Ok(sig)
}

/// Run the async committee secret sharing `recovery` algorithm using a locally stored bls377 keypair
/// and use the resulting keypair to sign the input message
///
fn acss_recover(
&self,
key_type: KeyTypeId,
public: &bls377::Public,
pok_bytes: &[u8],
message: &[u8],
threshold: u8,
) -> std::result::Result<bls377::Signature, TraitError> {
if let Some(Some(etf_pair)) = self.0.read()
.key_pair_by_type::<bls377::Pair>(public, key_type)?
.map(|pair| pair.acss_recover(pok_bytes, threshold)) {
let extract = etf_pair.sign(&message);
return Ok(extract);
}

Err(TraitError::KeyNotSupported(ETF_KEY_TYPE))
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions substrate/primitives/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ k256 = { version = "0.13.3", features = ["alloc", "ecdsa"], default-features = f
# secp256k1 crate, better performance, intended to be used on host side (std)
secp256k1 = { version = "0.28.0", default-features = false, features = ["alloc", "recovery"], optional = true }

# etf primitives
etf-crypto-primitives = { git = "http://github.com/ideal-lab5/etf-sdk", branch = "w3fbls-migration", default-features = false}
ark-serialize = "0.4.0"
# bls crypto
w3f-bls = { version = "0.1.3", default-features = false, optional = true }
# bandersnatch crypto
Expand Down Expand Up @@ -91,6 +94,7 @@ std = [
"codec/std",
"dyn-clonable",
"ed25519-zebra/std",
"etf-crypto-primitives/std",
"full_crypto",
"futures",
"futures/thread-pool",
Expand Down
26 changes: 25 additions & 1 deletion substrate/primitives/core/src/bls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ use w3f_bls::{
SecretKey, SerializableToBytes, TinyBLS381,
};

use etf_crypto_primitives::{
dpss::Keypair as ETFKeypair,
proofs::hashed_el_gamal_sigma::BatchPoK
};

use ark_serialize::CanonicalDeserialize;

/// BLS-377 specialized types
pub mod bls377 {
pub use super::{PUBLIC_KEY_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE};
Expand Down Expand Up @@ -139,7 +146,24 @@ fn derive_hard_junction<T: HardJunctionId>(secret_seed: &Seed, cc: &[u8; 32]) ->
(T::ID, secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256)
}

impl<T: EngineBLS> Pair<T> {}
impl<T: EngineBLS> Pair<T> {
/// the ACSS Recover algorithm
/// attempt to recover a keypair from the proof of knowledge
pub fn acss_recover(&self, pok_bytes: &[u8], threshold: u8) -> Option<Self> {
let mutable_self = self.clone();
if let Ok(pok) = BatchPoK::<T::PublicKeyGroup>::deserialize_compressed(&pok_bytes[..]) {
let sk = ETFKeypair(mutable_self.0.into_vartime());
if let Ok(recovered) = sk.recover(pok, threshold) {
let secret = w3f_bls::SecretKeyVT(recovered.0).into_split_dirty();
let public = secret.into_public();
return Some(Pair(w3f_bls::Keypair {
secret, public,
}));
}
}
None
}
}

impl<T: BlsBound> TraitPair for Pair<T> {
type Seed = Seed;
Expand Down
7 changes: 6 additions & 1 deletion substrate/primitives/keystore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ sp-externalities = { path = "../externalities", default-features = false }
[dev-dependencies]
rand = "0.8.5"
rand_chacha = "0.3.1"
w3f-bls = "0.1.3"
ark-serialize = "0.4.0"
ark-std = "0.4.0"
ark-ec = "0.4.0"
etf-crypto-primitives = { git = "https://github.com/ideal-lab5/etf-sdk/", branch = "w3fbls-migration" }

[features]
default = ["std"]
Expand All @@ -37,4 +42,4 @@ bls-experimental = ["sp-core/bls-experimental"]
# This feature adds Bandersnatch crypto primitives.
# It should not be used in production since the implementation and interface may still
# be subject to significant changes.
bandersnatch-experimental = ["sp-core/bandersnatch-experimental"]
bandersnatch-experimental = ["sp-core/bandersnatch-experimental"]
23 changes: 23 additions & 0 deletions substrate/primitives/keystore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,16 @@ pub trait Keystore: Send + Sync {
msg: &[u8],
) -> Result<Option<ecdsa_bls377::Signature>, Error>;

#[cfg(feature = "bls-experimental")]
fn acss_recover(
&self,
key_type: KeyTypeId,
public: &bls377::Public,
pok_bytes: &[u8],
message: &[u8],
threshold: u8,
) -> Result<bls377::Signature, Error>;

/// Insert a new secret key.
fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()>;

Expand Down Expand Up @@ -703,6 +713,19 @@ impl<T: Keystore + ?Sized> Keystore for Arc<T> {
(**self).ecdsa_bls377_sign_with_keccak256(key_type, public, msg)
}

#[cfg(feature = "bls-experimental")]
fn acss_recover(
&self,
key_type: KeyTypeId,
public: &bls377::Public,
pok_bytes: &[u8],
message: &[u8],
threshold: u8,
) -> Result<bls377::Signature, Error> {
(**self).acss_recover(key_type, public, pok_bytes, message, threshold)
}


fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> {
(**self).insert(key_type, suri, public)
}
Expand Down
67 changes: 67 additions & 0 deletions substrate/primitives/keystore/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,22 @@ impl Keystore for MemoryKeystore {
Ok(sig)
}

#[cfg(feature = "bls-experimental")]
fn acss_recover(
&self,
key_type: KeyTypeId,
public: &bls377::Public,
pok: &[u8],
msg: &[u8],
threshold: u8
) -> Result<bls377::Signature, Error> {
let sig = self.pair::<bls377::Pair>(key_type, public)
.map(|pair| pair.acss_recover(pok, threshold))
.unwrap().unwrap();
let extract = sig.sign(&msg);
Ok(extract)
}

fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> {
self.keys
.write()
Expand Down Expand Up @@ -398,6 +414,12 @@ mod tests {
sr25519,
testing::{ECDSA, ED25519, SR25519},
};
// only needed for ETF resharing construction
use ark_serialize::CanonicalSerialize;
use ark_std::UniformRand;
use ark_ec::Group;
use rand::rngs::OsRng;
use w3f_bls::{EngineBLS, TinyBLS377, SerializableToBytes};

#[test]
fn store_key_and_extract() {
Expand Down Expand Up @@ -538,6 +560,51 @@ mod tests {
));
}

#[test]
#[cfg(feature = "bls-experimental")]
fn bls377_acss_recover_works() {
use sp_core::testing::BLS377;

let store = MemoryKeystore::new();

let suri = "//Alice";
let pair = bls377::Pair::from_string(suri, None).unwrap();
let msg = b"this is a test message";
// insert key, sign again
store.insert(BLS377, suri, pair.public().as_ref()).unwrap();

let msk = <TinyBLS377 as EngineBLS>::Scalar::rand(&mut OsRng);
let msk_prime = <TinyBLS377 as EngineBLS>::Scalar::rand(&mut OsRng);
// build a resharing
let double_secret = etf_crypto_primitives::dpss::DoubleSecret::<TinyBLS377>(
msk, msk_prime
);

let ibe_pub_param = <TinyBLS377 as EngineBLS>::PublicKeyGroup::generator() * msk;
let mut ibe_pp_bytes = Vec::new();
ibe_pub_param.serialize_compressed(&mut ibe_pp_bytes).unwrap();

let genesis_resharing = double_secret.reshare(
&vec![w3f_bls::single::PublicKey::<TinyBLS377>(
w3f_bls::double::DoublePublicKey::<TinyBLS377>::from_bytes(
&pair.public().to_raw_vec()
).unwrap().1
)],
1,
&mut OsRng,
).unwrap();

let mut pok_bytes = Vec::new();
genesis_resharing[0].1.serialize_compressed(&mut pok_bytes).unwrap();

let t = sp_core::bls::Pair::<TinyBLS377>::from(pair.clone());
let expected_public_key = t.acss_recover(&pok_bytes, 1).unwrap();

let res = store.acss_recover(BLS377, &pair.public(), &pok_bytes[..], msg, 1).unwrap();

assert!(bls377::Pair::verify(&res, &msg[..], &o.public()));
}

#[test]
#[cfg(feature = "bandersnatch-experimental")]
fn bandersnatch_vrf_sign() {
Expand Down
Loading