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

Application Crypto and BEEFY Support for paired (ECDSA,BLS) crypto #1815

Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5d3dce1
First definition for pair public keys
drskalman Aug 3, 2023
be890d4
Two example of implementation of pair for demonestration
drskalman Aug 3, 2023
fb6a018
- implement paired crypto `Public` as tuple of two `Public`s - unsuce…
drskalman Aug 14, 2023
62ef8b1
keep both public key object and their continous serialization in pair…
drskalman Aug 21, 2023
8d4e523
implement PassBy and From<Pair> for paired_crypto
drskalman Aug 21, 2023
eea74e2
implement rest of aux traits for `paired_crypto::Public` implement so…
drskalman Aug 22, 2023
f407d87
Attempt to implement trait `Pair` for `pair_cyrpto::Pair`
drskalman Aug 29, 2023
2606cc4
- Implement trait `Pair` for `paired_crypto::Pair` - Implement a pair…
drskalman Aug 31, 2023
00933da
implement sgin and verify for
drskalman Sep 5, 2023
e7719ab
Actually implementing `paired_crypto::{Pair, Public, Signatrue}` for …
drskalman Sep 10, 2023
7a9b677
Implement and pass all test for `paired_crypto`
drskalman Sep 11, 2023
a01a814
- move to signle seed for both schemes in `primitives/core/src/paired…
drskalman Sep 25, 2023
7c02658
replace `hex!` → `array_bytes::hex2xx`
drskalman Sep 25, 2023
7f8d958
Apply suggestions from `paired_crypto` code review on type nam, hash …
drskalman Oct 5, 2023
eef2cec
Do not panic in `paired::Signature::try_from`
drskalman Oct 5, 2023
efb215f
Remove `DoublePair` trait.
drskalman Oct 5, 2023
65584bd
Do not empty implement `paired::Pair`
drskalman Oct 5, 2023
e047f75
Use `paired_crypto::Seed` instead of `[u8; SECURE_SEED_LEN]`
drskalman Oct 5, 2023
c12b82e
use `ecdsa::PUBLIC_KEY_SERIALIZED_SIZE` and `ecdsa::SIGNATURE_SERIALI…
drskalman Oct 5, 2023
dba854a
Remove `paired::DoublePair` impl as well
drskalman Oct 5, 2023
5182c86
- Implement `BytesArray` for both ecdsa and bls Signatures
drskalman Oct 5, 2023
17d0077
Implement encode_and_decode_(public_key/signature)_works test for pai…
drskalman Oct 5, 2023
a740589
cargo fmt
drskalman Oct 5, 2023
79f25e9
- Implement RuntimeAppCrypto and necessery hostApi for ecdsa_bls377 c…
drskalman Oct 6, 2023
067dece
cargo fmt
drskalman Oct 6, 2023
3cf7594
Merge branch 'master' into skalman--paired-ecdsa-bls-support-for-beef…
drskalman Oct 17, 2023
652990e
`cargo fmt`
drskalman Oct 17, 2023
388ed14
Nitpicks
davxy Oct 19, 2023
9f07bd5
implement ecdsa_bls377 `Keystore` functions for `LocalKeystore`.
drskalman Oct 20, 2023
fdd1bdf
nitpick tuple consistency
Lederstrumpf Oct 24, 2023
305a003
reuse `Ord` impl for `PartialOrd` impl of `Public`
Lederstrumpf Oct 24, 2023
3a8c46e
Merge branch 'master' into skalman--paired-ecdsa-bls-support-for-beef…
Lederstrumpf Oct 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions substrate/primitives/application-crypto/src/ecdsa_bls377.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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.

//! BLS12-377 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
55 changes: 55 additions & 0 deletions substrate/primitives/consensus/beefy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,45 @@ pub mod bls_crypto {
}
}
}

/// BEEFY cryptographic types for (ECDSA,BLS) crypto pair
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why here you've not specified BLS12-377?
This will be changed to use 381?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly.

The BLS in the BLS12 curves and BLS in the signature are not the same thing though. (I think only the L in the BLS signature and in the BLS12 curves are the same person). You could do BLS signature with any other BLS friendly curves such as BN or BLS24. Here I just wanted to say that we are generating a BLS type signature without going into the technicality of which curve we are using under the hood.

Copy link
Member

@davxy davxy Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But here we are not defining types which are bounded by a generic pairing fiendly curve to produce BLS signatures. Here we are using a specific curve (which at the moment is bls12-377). And the same applies to bls_crypto module.

So maybe the question now is: is intentional to not be explicit about the curve in the modules names?

I'm not saying that this is not the right choice. I'm just curious

///
/// This module basically introduces four crypto types:
/// - `bls_crypto::Pair`
/// - `bls_crypto::Public`
/// - `bls_crypto::Signature`
/// - `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 as BEEFY_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, BEEFY_KEY_TYPE);

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

/// Signature for a BEEFY authority using 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 +497,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: 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
12 changes: 8 additions & 4 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 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 @@ -452,7 +456,7 @@ where
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
22 changes: 21 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,26 @@ pub trait Crypto {
.expect("`bls377_generate` failed")
}

// Generate an pair of `(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
65 changes: 64 additions & 1 deletion substrate/primitives/keystore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub mod testing;
#[cfg(feature = "bandersnatch-experimental")]
use sp_core::bandersnatch;
#[cfg(feature = "bls-experimental")]
use sp_core::{bls377, bls381};
use sp_core::{bls377, bls381, ecdsa_bls377};
use sp_core::{
crypto::{ByteArray, CryptoTypeId, KeyTypeId},
ecdsa, ed25519, sr25519,
Expand Down Expand Up @@ -270,6 +270,10 @@ pub trait Keystore: Send + Sync {
#[cfg(feature = "bls-experimental")]
fn bls377_public_keys(&self, id: KeyTypeId) -> Vec<bls377::Public>;

/// Returns all (ecdsa,bls12-377) paired public keys for the given key type.
#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_public_keys(&self, id: KeyTypeId) -> Vec<ecdsa_bls377::Public>;

/// Generate a new bls381 key pair for the given key type and an optional seed.
///
/// Returns an `bls381::Public` key of the generated key pair or an `Err` if
Expand All @@ -292,6 +296,17 @@ pub trait Keystore: Send + Sync {
seed: Option<&str>,
) -> Result<bls377::Public, Error>;

/// Generate a new (ecdsa,bls377) key pair for the given key type and an optional seed.
///
/// Returns an `ecdsa_bls377::Public` key of the generated key pair or an `Err` if
/// something failed during key generation.
#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_generate_new(
&self,
key_type: KeyTypeId,
seed: Option<&str>,
) -> Result<ecdsa_bls377::Public, Error>;

/// Generate a bls381 signature for a given message.
///
/// Receives [`KeyTypeId`] and a [`bls381::Public`] key to be able to map
Expand Down Expand Up @@ -324,6 +339,22 @@ pub trait Keystore: Send + Sync {
msg: &[u8],
) -> Result<Option<bls377::Signature>, Error>;

/// Generate a (ecdsa, bls377) signature pair for a given message.
///
/// Receives [`KeyTypeId`] and a [`ecdsa_bls377::Public`] key to be able to map
/// them to a private key that exists in the keystore.
///
/// Returns an [`ecdsa_bls377::Signature`] or `None` in case the given `key_type`
/// and `public` combination doesn't exist in the keystore.
/// An `Err` will be returned if generating the signature itself failed.
#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_sign(
&self,
key_type: KeyTypeId,
public: &ecdsa_bls377::Public,
msg: &[u8],
) -> Result<Option<ecdsa_bls377::Signature>, Error>;

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

Expand All @@ -349,6 +380,7 @@ pub trait Keystore: Send + Sync {
/// - bandersnatch
/// - bls381
/// - bls377
/// - (ecdsa,bls377) paired keys
///
/// To support more schemes you can overwrite this method.
///
Expand Down Expand Up @@ -398,6 +430,13 @@ pub trait Keystore: Send + Sync {
.map_err(|_| Error::ValidationError("Invalid public key format".into()))?;
self.bls377_sign(id, &public, msg)?.map(|s| s.encode())
},
#[cfg(feature = "bls-experimental")]
ecdsa_bls377::CRYPTO_ID => {
let public = ecdsa_bls377::Public::from_slice(public)
.map_err(|_| Error::ValidationError("Invalid public key format".into()))?;
self.ecdsa_bls377_sign(id, &public, msg)?.map(|s| s.encode())
},

_ => return Err(Error::KeyNotSupported(id)),
};
Ok(signature)
Expand Down Expand Up @@ -560,6 +599,11 @@ impl<T: Keystore + ?Sized> Keystore for Arc<T> {
(**self).bls377_public_keys(id)
}

#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_public_keys(&self, id: KeyTypeId) -> Vec<ecdsa_bls377::Public> {
(**self).ecdsa_bls377_public_keys(id)
}

#[cfg(feature = "bls-experimental")]
fn bls381_generate_new(
&self,
Expand All @@ -578,6 +622,15 @@ impl<T: Keystore + ?Sized> Keystore for Arc<T> {
(**self).bls377_generate_new(key_type, seed)
}

#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_generate_new(
&self,
key_type: KeyTypeId,
seed: Option<&str>,
) -> Result<ecdsa_bls377::Public, Error> {
(**self).ecdsa_bls377_generate_new(key_type, seed)
}

#[cfg(feature = "bls-experimental")]
fn bls381_sign(
&self,
Expand All @@ -598,6 +651,16 @@ impl<T: Keystore + ?Sized> Keystore for Arc<T> {
(**self).bls377_sign(key_type, public, msg)
}

#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_sign(
&self,
key_type: KeyTypeId,
public: &ecdsa_bls377::Public,
msg: &[u8],
) -> Result<Option<ecdsa_bls377::Signature>, Error> {
(**self).ecdsa_bls377_sign(key_type, public, msg)
}

fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> {
(**self).insert(key_type, suri, public)
}
Expand Down
26 changes: 25 additions & 1 deletion substrate/primitives/keystore/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{Error, Keystore, KeystorePtr};
#[cfg(feature = "bandersnatch-experimental")]
use sp_core::bandersnatch;
#[cfg(feature = "bls-experimental")]
use sp_core::{bls377, bls381};
use sp_core::{bls377, bls381, ecdsa_bls377};
use sp_core::{
crypto::{ByteArray, KeyTypeId, Pair, VrfSecret},
ecdsa, ed25519, sr25519,
Expand Down Expand Up @@ -322,6 +322,30 @@ impl Keystore for MemoryKeystore {
self.sign::<bls377::Pair>(key_type, public, msg)
}

#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa_bls377::Public> {
self.public_keys::<ecdsa_bls377::Pair>(key_type)
}

#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_generate_new(
&self,
key_type: KeyTypeId,
seed: Option<&str>,
) -> Result<ecdsa_bls377::Public, Error> {
self.generate_new::<ecdsa_bls377::Pair>(key_type, seed)
}

#[cfg(feature = "bls-experimental")]
fn ecdsa_bls377_sign(
&self,
key_type: KeyTypeId,
public: &ecdsa_bls377::Public,
msg: &[u8],
) -> Result<Option<ecdsa_bls377::Signature>, Error> {
self.sign::<ecdsa_bls377::Pair>(key_type, public, msg)
}

fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> {
self.keys
.write()
Expand Down
Loading