Skip to content

Commit

Permalink
Application Crypto and BEEFY Support for paired (ECDSA,BLS) crypto (p…
Browse files Browse the repository at this point in the history
…aritytech#1815)

Next step in process of making BEEFY being able to generate both ECDSA
and BLS signature after paritytech#1705. It allows BEEFY to use a pair of ECDSA
and BLS key as a AuthorityId.

---------

Co-authored-by: Davide Galassi <davxy@datawok.net>
Co-authored-by: Robert Hambrock <roberthambrock@gmail.com>
  • Loading branch information
3 people authored Oct 24, 2023
1 parent 3a53c77 commit 9291c4d
Show file tree
Hide file tree
Showing 11 changed files with 273 additions and 17 deletions.
27 changes: 26 additions & 1 deletion substrate/client/keystore/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use sp_core::bandersnatch;
}

sp_keystore::bls_experimental_enabled! {
use sp_core::{bls377, bls381};
use sp_core::{bls377, bls381, ecdsa_bls377};
}

use crate::{Error, Result};
Expand Down Expand Up @@ -366,6 +366,31 @@ impl Keystore for LocalKeystore {
) -> std::result::Result<Option<bls377::Signature>, TraitError> {
self.sign::<bls377::Pair>(key_type, public, msg)
}

fn ecdsa_bls377_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa_bls377::Public> {
self.public_keys::<ecdsa_bls377::Pair>(key_type)
}

/// Generate a new pair of paired-keys compatible with the '(ecdsa,bls377)' signature scheme.
///
/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
fn ecdsa_bls377_generate_new(
&self,
key_type: KeyTypeId,
seed: Option<&str>,
) -> std::result::Result<ecdsa_bls377::Public, TraitError> {
self.generate_new::<ecdsa_bls377::Pair>(key_type, seed)
}

fn ecdsa_bls377_sign(
&self,
key_type: KeyTypeId,
public: &ecdsa_bls377::Public,
msg: &[u8],
) -> std::result::Result<Option<ecdsa_bls377::Signature>, TraitError> {
self.sign::<ecdsa_bls377::Pair>(key_type, public, msg)
}

}
}

Expand Down
57 changes: 57 additions & 0 deletions substrate/primitives/application-crypto/src/ecdsa_bls377.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! ECDSA and BLS12-377 paired crypto applications.

use crate::{KeyTypeId, RuntimePublic};

pub use sp_core::paired_crypto::ecdsa_bls377::*;

mod app {
crate::app_crypto!(super, sp_core::testing::ECDSA_BLS377);
}

#[cfg(feature = "full_crypto")]
pub use app::Pair as AppPair;
pub use app::{Public as AppPublic, Signature as AppSignature};

impl RuntimePublic for Public {
type Signature = Signature;

/// Dummy implementation. Returns an empty vector.
fn all(_key_type: KeyTypeId) -> Vec<Self> {
Vec::new()
}

fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
sp_io::crypto::ecdsa_bls377_generate(key_type, seed)
}

/// Dummy implementation. Returns `None`.
fn sign<M: AsRef<[u8]>>(&self, _key_type: KeyTypeId, _msg: &M) -> Option<Self::Signature> {
None
}

/// Dummy implementation. Returns `false`.
fn verify<M: AsRef<[u8]>>(&self, _msg: &M, _signature: &Self::Signature) -> bool {
false
}

fn to_raw_vec(&self) -> Vec<u8> {
sp_core::crypto::ByteArray::to_raw_vec(self)
}
}
2 changes: 2 additions & 0 deletions substrate/primitives/application-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pub mod bls377;
#[cfg(feature = "bls-experimental")]
pub mod bls381;
pub mod ecdsa;
#[cfg(feature = "bls-experimental")]
pub mod ecdsa_bls377;
pub mod ed25519;
pub mod sr25519;
mod traits;
Expand Down
66 changes: 62 additions & 4 deletions substrate/primitives/consensus/beefy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,11 @@ pub trait BeefyAuthorityId<MsgHash: Hash>: RuntimeAppPublic {
/// Your code should use the above types as concrete types for all crypto related
/// functionality.
pub mod ecdsa_crypto {
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE as BEEFY_KEY_TYPE};
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
use sp_application_crypto::{app_crypto, ecdsa};
use sp_core::crypto::Wraps;
app_crypto!(ecdsa, BEEFY_KEY_TYPE);

app_crypto!(ecdsa, KEY_TYPE);

/// Identity of a BEEFY authority using ECDSA as its crypto.
pub type AuthorityId = Public;
Expand Down Expand Up @@ -115,10 +116,11 @@ pub mod ecdsa_crypto {

#[cfg(feature = "bls-experimental")]
pub mod bls_crypto {
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE as BEEFY_KEY_TYPE};
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
use sp_application_crypto::{app_crypto, bls377};
use sp_core::{bls377::Pair as BlsPair, crypto::Wraps, Pair as _};
app_crypto!(bls377, BEEFY_KEY_TYPE);

app_crypto!(bls377, KEY_TYPE);

/// Identity of a BEEFY authority using BLS as its crypto.
pub type AuthorityId = Public;
Expand All @@ -140,6 +142,46 @@ pub mod bls_crypto {
}
}
}

/// BEEFY cryptographic types for (ECDSA,BLS) crypto pair
///
/// This module basically introduces four crypto types:
/// - `ecdsa_bls_crypto::Pair`
/// - `ecdsa_bls_crypto::Public`
/// - `ecdsa_bls_crypto::Signature`
/// - `ecdsa_bls_crypto::AuthorityId`
///
/// Your code should use the above types as concrete types for all crypto related
/// functionality.
#[cfg(feature = "bls-experimental")]
pub mod ecdsa_bls_crypto {
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
use sp_application_crypto::{app_crypto, ecdsa_bls377};
use sp_core::{crypto::Wraps, ecdsa_bls377::Pair as EcdsaBlsPair, Pair as _};

app_crypto!(ecdsa_bls377, KEY_TYPE);

/// Identity of a BEEFY authority using (ECDSA,BLS) as its crypto.
pub type AuthorityId = Public;

/// Signature for a BEEFY authority using (ECDSA,BLS) as its crypto.
pub type AuthoritySignature = Signature;

impl<MsgHash: Hash> BeefyAuthorityId<MsgHash> for AuthorityId
where
<MsgHash as Hash>::Output: Into<[u8; 32]>,
{
fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
// `w3f-bls` library uses IETF hashing standard and as such does not exposes
// a choice of hash to field function.
// We are directly calling into the library to avoid introducing new host call.
// and because BeefyAuthorityId::verify is being called in the runtime so we don't have

EcdsaBlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())
}
}
}

/// The `ConsensusEngineId` of BEEFY.
pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";

Expand Down Expand Up @@ -458,4 +500,20 @@ mod tests {
let (other_pair, _) = bls_crypto::Pair::generate();
assert!(!BeefyAuthorityId::<Keccak256>::verify(&other_pair.public(), &signature, msg,));
}

#[test]
#[cfg(feature = "bls-experimental")]
fn ecdsa_bls_beefy_verify_works() {
let msg = &b"test-message"[..];
let (pair, _) = ecdsa_bls_crypto::Pair::generate();

let signature: ecdsa_bls_crypto::Signature = pair.as_inner_ref().sign(&msg).into();

// Verification works if same hashing function is used when signing and verifying.
assert!(BeefyAuthorityId::<Keccak256>::verify(&pair.public(), &signature, msg));

// Other public key doesn't work
let (other_pair, _) = ecdsa_bls_crypto::Pair::generate();
assert!(!BeefyAuthorityId::<Keccak256>::verify(&other_pair.public(), &signature, msg,));
}
}
2 changes: 1 addition & 1 deletion substrate/primitives/core/src/bls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl<T> Eq for Public<T> {}

impl<T> PartialOrd for Public<T> {
fn partial_cmp(&self, other: &Self) -> Option<sp_std::cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
Some(self.cmp(other))
}
}

Expand Down
2 changes: 2 additions & 0 deletions substrate/primitives/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ pub mod uint;

#[cfg(feature = "bls-experimental")]
pub use bls::{bls377, bls381};
#[cfg(feature = "bls-experimental")]
pub use paired_crypto::ecdsa_bls377;

pub use self::{
hash::{convert_hash, H160, H256, H512},
Expand Down
20 changes: 12 additions & 8 deletions substrate/primitives/core/src/paired_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
#[cfg(all(not(feature = "std"), feature = "serde"))]
use sp_std::alloc::{format, string::String};

use sp_runtime_interface::pass_by::PassByInner;
use sp_runtime_interface::pass_by::{self, PassBy, PassByInner};
use sp_std::convert::TryFrom;

/// ECDSA and BLS12-377 paired crypto scheme
#[cfg(feature = "bls-experimental")]
pub mod ecdsa_n_bls377 {
pub mod ecdsa_bls377 {
use crate::{bls377, crypto::CryptoTypeId, ecdsa};

/// An identifier used to match public keys against BLS12-377 keys
Expand All @@ -49,12 +49,12 @@ pub mod ecdsa_n_bls377 {
const SIGNATURE_LEN: usize =
ecdsa::SIGNATURE_SERIALIZED_SIZE + bls377::SIGNATURE_SERIALIZED_SIZE;

/// (ECDSA, BLS12-377) key-pair pair.
/// (ECDSA,BLS12-377) key-pair pair.
#[cfg(feature = "full_crypto")]
pub type Pair = super::Pair<ecdsa::Pair, bls377::Pair, PUBLIC_KEY_LEN, SIGNATURE_LEN>;
/// (ECDSA, BLS12-377) public key pair.
/// (ECDSA,BLS12-377) public key pair.
pub type Public = super::Public<PUBLIC_KEY_LEN>;
/// (ECDSA, BLS12-377) signature pair.
/// (ECDSA,BLS12-377) signature pair.
pub type Signature = super::Signature<SIGNATURE_LEN>;

impl super::CryptoType for Public {
Expand Down Expand Up @@ -151,6 +151,10 @@ impl<const LEFT_PLUS_RIGHT_LEN: usize> PassByInner for Public<LEFT_PLUS_RIGHT_LE
}
}

impl<const LEFT_PLUS_RIGHT_LEN: usize> PassBy for Public<LEFT_PLUS_RIGHT_LEN> {
type PassBy = pass_by::Inner<Self, [u8; LEFT_PLUS_RIGHT_LEN]>;
}

#[cfg(feature = "full_crypto")]
impl<
LeftPair: PairT,
Expand Down Expand Up @@ -244,7 +248,7 @@ pub trait SignatureBound: ByteArray {}
impl<T: ByteArray> SignatureBound for T {}

/// A pair of signatures of different types
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq)]
#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq)]
pub struct Signature<const LEFT_PLUS_RIGHT_LEN: usize>([u8; LEFT_PLUS_RIGHT_LEN]);

#[cfg(feature = "full_crypto")]
Expand Down Expand Up @@ -447,12 +451,12 @@ where
}
}

// Test set exercising the (ECDSA, BLS12-377) implementation
// Test set exercising the (ECDSA,BLS12-377) implementation
#[cfg(all(test, feature = "bls-experimental"))]
mod test {
use super::*;
use crate::crypto::DEV_PHRASE;
use ecdsa_n_bls377::{Pair, Signature};
use ecdsa_bls377::{Pair, Signature};

use crate::{bls377, ecdsa};
#[test]
Expand Down
2 changes: 2 additions & 0 deletions substrate/primitives/core/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub const BANDERSNATCH: KeyTypeId = KeyTypeId(*b"band");
pub const BLS377: KeyTypeId = KeyTypeId(*b"bls7");
/// Key type for generic BLS12-381 key.
pub const BLS381: KeyTypeId = KeyTypeId(*b"bls8");
/// Key type for (ECDSA,BLS12-377) key pair
pub const ECDSA_BLS377: KeyTypeId = KeyTypeId(*b"ecb7");

/// Macro for exporting functions from wasm in with the expected signature for using it with the
/// wasm executor. This is useful for tests where you need to call a function in wasm.
Expand Down
21 changes: 20 additions & 1 deletion substrate/primitives/io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ use sp_core::{
};

#[cfg(feature = "bls-experimental")]
use sp_core::bls377;
use sp_core::{bls377, ecdsa_bls377};

#[cfg(feature = "std")]
use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration};
Expand Down Expand Up @@ -1207,6 +1207,25 @@ pub trait Crypto {
.expect("`bls377_generate` failed")
}

/// Generate an `(ecdsa,bls12-377)` key for the given key type using an optional `seed` and
/// store it in the keystore.
///
/// The `seed` needs to be a valid utf8.
///
/// Returns the public key.
#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_generate(
&mut self,
id: KeyTypeId,
seed: Option<Vec<u8>>,
) -> ecdsa_bls377::Public {
let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.ecdsa_bls377_generate_new(id, seed)
.expect("`ecdsa_bls377_generate` failed")
}

/// Generate a `bandersnatch` key pair for the given key type using an optional
/// `seed` and store it in the keystore.
///
Expand Down
Loading

0 comments on commit 9291c4d

Please sign in to comment.