Skip to content

Commit b121523

Browse files
drskalmandavxyLederstrumpf
authored andcommitted
Application Crypto and BEEFY Support for paired (ECDSA,BLS) crypto (#1815)
Next step in process of making BEEFY being able to generate both ECDSA and BLS signature after #1705. It allows BEEFY to use a pair of ECDSA and BLS key as a AuthorityId. --------- Co-authored-by: Davide Galassi <[email protected]> Co-authored-by: Robert Hambrock <[email protected]>
1 parent 7f798c5 commit b121523

11 files changed

Lines changed: 273 additions & 17 deletions

File tree

substrate/client/keystore/src/local.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use sp_core::bandersnatch;
3737
}
3838

3939
sp_keystore::bls_experimental_enabled! {
40-
use sp_core::{bls377, bls381};
40+
use sp_core::{bls377, bls381, ecdsa_bls377};
4141
}
4242

4343
use crate::{Error, Result};
@@ -366,6 +366,31 @@ impl Keystore for LocalKeystore {
366366
) -> std::result::Result<Option<bls377::Signature>, TraitError> {
367367
self.sign::<bls377::Pair>(key_type, public, msg)
368368
}
369+
370+
fn ecdsa_bls377_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa_bls377::Public> {
371+
self.public_keys::<ecdsa_bls377::Pair>(key_type)
372+
}
373+
374+
/// Generate a new pair of paired-keys compatible with the '(ecdsa,bls377)' signature scheme.
375+
///
376+
/// If `[seed]` is `Some` then the key will be ephemeral and stored in memory.
377+
fn ecdsa_bls377_generate_new(
378+
&self,
379+
key_type: KeyTypeId,
380+
seed: Option<&str>,
381+
) -> std::result::Result<ecdsa_bls377::Public, TraitError> {
382+
self.generate_new::<ecdsa_bls377::Pair>(key_type, seed)
383+
}
384+
385+
fn ecdsa_bls377_sign(
386+
&self,
387+
key_type: KeyTypeId,
388+
public: &ecdsa_bls377::Public,
389+
msg: &[u8],
390+
) -> std::result::Result<Option<ecdsa_bls377::Signature>, TraitError> {
391+
self.sign::<ecdsa_bls377::Pair>(key_type, public, msg)
392+
}
393+
369394
}
370395
}
371396

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// This file is part of Substrate.
2+
3+
// Copyright (C) Parity Technologies (UK) Ltd.
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
//! ECDSA and BLS12-377 paired crypto applications.
19+
20+
use crate::{KeyTypeId, RuntimePublic};
21+
22+
pub use sp_core::paired_crypto::ecdsa_bls377::*;
23+
24+
mod app {
25+
crate::app_crypto!(super, sp_core::testing::ECDSA_BLS377);
26+
}
27+
28+
#[cfg(feature = "full_crypto")]
29+
pub use app::Pair as AppPair;
30+
pub use app::{Public as AppPublic, Signature as AppSignature};
31+
32+
impl RuntimePublic for Public {
33+
type Signature = Signature;
34+
35+
/// Dummy implementation. Returns an empty vector.
36+
fn all(_key_type: KeyTypeId) -> Vec<Self> {
37+
Vec::new()
38+
}
39+
40+
fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
41+
sp_io::crypto::ecdsa_bls377_generate(key_type, seed)
42+
}
43+
44+
/// Dummy implementation. Returns `None`.
45+
fn sign<M: AsRef<[u8]>>(&self, _key_type: KeyTypeId, _msg: &M) -> Option<Self::Signature> {
46+
None
47+
}
48+
49+
/// Dummy implementation. Returns `false`.
50+
fn verify<M: AsRef<[u8]>>(&self, _msg: &M, _signature: &Self::Signature) -> bool {
51+
false
52+
}
53+
54+
fn to_raw_vec(&self) -> Vec<u8> {
55+
sp_core::crypto::ByteArray::to_raw_vec(self)
56+
}
57+
}

substrate/primitives/application-crypto/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ pub mod bls377;
5050
#[cfg(feature = "bls-experimental")]
5151
pub mod bls381;
5252
pub mod ecdsa;
53+
#[cfg(feature = "bls-experimental")]
54+
pub mod ecdsa_bls377;
5355
pub mod ed25519;
5456
pub mod sr25519;
5557
mod traits;

substrate/primitives/consensus/beefy/src/lib.rs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ pub trait BeefyAuthorityId<MsgHash: Hash>: RuntimeAppPublic {
7474
/// Your code should use the above types as concrete types for all crypto related
7575
/// functionality.
7676
pub mod ecdsa_crypto {
77-
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE as BEEFY_KEY_TYPE};
77+
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
7878
use sp_application_crypto::{app_crypto, ecdsa};
7979
use sp_core::crypto::Wraps;
80-
app_crypto!(ecdsa, BEEFY_KEY_TYPE);
80+
81+
app_crypto!(ecdsa, KEY_TYPE);
8182

8283
/// Identity of a BEEFY authority using ECDSA as its crypto.
8384
pub type AuthorityId = Public;
@@ -115,10 +116,11 @@ pub mod ecdsa_crypto {
115116
116117
#[cfg(feature = "bls-experimental")]
117118
pub mod bls_crypto {
118-
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE as BEEFY_KEY_TYPE};
119+
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
119120
use sp_application_crypto::{app_crypto, bls377};
120121
use sp_core::{bls377::Pair as BlsPair, crypto::Wraps, Pair as _};
121-
app_crypto!(bls377, BEEFY_KEY_TYPE);
122+
123+
app_crypto!(bls377, KEY_TYPE);
122124

123125
/// Identity of a BEEFY authority using BLS as its crypto.
124126
pub type AuthorityId = Public;
@@ -140,6 +142,46 @@ pub mod bls_crypto {
140142
}
141143
}
142144
}
145+
146+
/// BEEFY cryptographic types for (ECDSA,BLS) crypto pair
147+
///
148+
/// This module basically introduces four crypto types:
149+
/// - `ecdsa_bls_crypto::Pair`
150+
/// - `ecdsa_bls_crypto::Public`
151+
/// - `ecdsa_bls_crypto::Signature`
152+
/// - `ecdsa_bls_crypto::AuthorityId`
153+
///
154+
/// Your code should use the above types as concrete types for all crypto related
155+
/// functionality.
156+
#[cfg(feature = "bls-experimental")]
157+
pub mod ecdsa_bls_crypto {
158+
use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
159+
use sp_application_crypto::{app_crypto, ecdsa_bls377};
160+
use sp_core::{crypto::Wraps, ecdsa_bls377::Pair as EcdsaBlsPair, Pair as _};
161+
162+
app_crypto!(ecdsa_bls377, KEY_TYPE);
163+
164+
/// Identity of a BEEFY authority using (ECDSA,BLS) as its crypto.
165+
pub type AuthorityId = Public;
166+
167+
/// Signature for a BEEFY authority using (ECDSA,BLS) as its crypto.
168+
pub type AuthoritySignature = Signature;
169+
170+
impl<MsgHash: Hash> BeefyAuthorityId<MsgHash> for AuthorityId
171+
where
172+
<MsgHash as Hash>::Output: Into<[u8; 32]>,
173+
{
174+
fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
175+
// `w3f-bls` library uses IETF hashing standard and as such does not exposes
176+
// a choice of hash to field function.
177+
// We are directly calling into the library to avoid introducing new host call.
178+
// and because BeefyAuthorityId::verify is being called in the runtime so we don't have
179+
180+
EcdsaBlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())
181+
}
182+
}
183+
}
184+
143185
/// The `ConsensusEngineId` of BEEFY.
144186
pub const BEEFY_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"BEEF";
145187

@@ -458,4 +500,20 @@ mod tests {
458500
let (other_pair, _) = bls_crypto::Pair::generate();
459501
assert!(!BeefyAuthorityId::<Keccak256>::verify(&other_pair.public(), &signature, msg,));
460502
}
503+
504+
#[test]
505+
#[cfg(feature = "bls-experimental")]
506+
fn ecdsa_bls_beefy_verify_works() {
507+
let msg = &b"test-message"[..];
508+
let (pair, _) = ecdsa_bls_crypto::Pair::generate();
509+
510+
let signature: ecdsa_bls_crypto::Signature = pair.as_inner_ref().sign(&msg).into();
511+
512+
// Verification works if same hashing function is used when signing and verifying.
513+
assert!(BeefyAuthorityId::<Keccak256>::verify(&pair.public(), &signature, msg));
514+
515+
// Other public key doesn't work
516+
let (other_pair, _) = ecdsa_bls_crypto::Pair::generate();
517+
assert!(!BeefyAuthorityId::<Keccak256>::verify(&other_pair.public(), &signature, msg,));
518+
}
461519
}

substrate/primitives/core/src/bls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl<T> Eq for Public<T> {}
134134

135135
impl<T> PartialOrd for Public<T> {
136136
fn partial_cmp(&self, other: &Self) -> Option<sp_std::cmp::Ordering> {
137-
self.inner.partial_cmp(&other.inner)
137+
Some(self.cmp(other))
138138
}
139139
}
140140

substrate/primitives/core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub mod uint;
7575

7676
#[cfg(feature = "bls-experimental")]
7777
pub use bls::{bls377, bls381};
78+
#[cfg(feature = "bls-experimental")]
79+
pub use paired_crypto::ecdsa_bls377;
7880

7981
pub use self::{
8082
hash::{convert_hash, H160, H256, H512},

substrate/primitives/core/src/paired_crypto.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
3333
#[cfg(all(not(feature = "std"), feature = "serde"))]
3434
use sp_std::alloc::{format, string::String};
3535

36-
use sp_runtime_interface::pass_by::PassByInner;
36+
use sp_runtime_interface::pass_by::{self, PassBy, PassByInner};
3737
use sp_std::convert::TryFrom;
3838

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

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

52-
/// (ECDSA, BLS12-377) key-pair pair.
52+
/// (ECDSA,BLS12-377) key-pair pair.
5353
#[cfg(feature = "full_crypto")]
5454
pub type Pair = super::Pair<ecdsa::Pair, bls377::Pair, PUBLIC_KEY_LEN, SIGNATURE_LEN>;
55-
/// (ECDSA, BLS12-377) public key pair.
55+
/// (ECDSA,BLS12-377) public key pair.
5656
pub type Public = super::Public<PUBLIC_KEY_LEN>;
57-
/// (ECDSA, BLS12-377) signature pair.
57+
/// (ECDSA,BLS12-377) signature pair.
5858
pub type Signature = super::Signature<SIGNATURE_LEN>;
5959

6060
impl super::CryptoType for Public {
@@ -151,6 +151,10 @@ impl<const LEFT_PLUS_RIGHT_LEN: usize> PassByInner for Public<LEFT_PLUS_RIGHT_LE
151151
}
152152
}
153153

154+
impl<const LEFT_PLUS_RIGHT_LEN: usize> PassBy for Public<LEFT_PLUS_RIGHT_LEN> {
155+
type PassBy = pass_by::Inner<Self, [u8; LEFT_PLUS_RIGHT_LEN]>;
156+
}
157+
154158
#[cfg(feature = "full_crypto")]
155159
impl<
156160
LeftPair: PairT,
@@ -244,7 +248,7 @@ pub trait SignatureBound: ByteArray {}
244248
impl<T: ByteArray> SignatureBound for T {}
245249

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

250254
#[cfg(feature = "full_crypto")]
@@ -447,12 +451,12 @@ where
447451
}
448452
}
449453

450-
// Test set exercising the (ECDSA, BLS12-377) implementation
454+
// Test set exercising the (ECDSA,BLS12-377) implementation
451455
#[cfg(all(test, feature = "bls-experimental"))]
452456
mod test {
453457
use super::*;
454458
use crate::crypto::DEV_PHRASE;
455-
use ecdsa_n_bls377::{Pair, Signature};
459+
use ecdsa_bls377::{Pair, Signature};
456460

457461
use crate::{bls377, ecdsa};
458462
#[test]

substrate/primitives/core/src/testing.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pub const BANDERSNATCH: KeyTypeId = KeyTypeId(*b"band");
3131
pub const BLS377: KeyTypeId = KeyTypeId(*b"bls7");
3232
/// Key type for generic BLS12-381 key.
3333
pub const BLS381: KeyTypeId = KeyTypeId(*b"bls8");
34+
/// Key type for (ECDSA,BLS12-377) key pair
35+
pub const ECDSA_BLS377: KeyTypeId = KeyTypeId(*b"ecb7");
3436

3537
/// Macro for exporting functions from wasm in with the expected signature for using it with the
3638
/// wasm executor. This is useful for tests where you need to call a function in wasm.

substrate/primitives/io/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ use sp_core::{
106106
};
107107

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

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

1210+
/// Generate an `(ecdsa,bls12-377)` key for the given key type using an optional `seed` and
1211+
/// store it in the keystore.
1212+
///
1213+
/// The `seed` needs to be a valid utf8.
1214+
///
1215+
/// Returns the public key.
1216+
#[cfg(feature = "bls-experimental")]
1217+
fn ecdsa_bls377_generate(
1218+
&mut self,
1219+
id: KeyTypeId,
1220+
seed: Option<Vec<u8>>,
1221+
) -> ecdsa_bls377::Public {
1222+
let seed = seed.as_ref().map(|s| std::str::from_utf8(s).expect("Seed is valid utf8!"));
1223+
self.extension::<KeystoreExt>()
1224+
.expect("No `keystore` associated for the current context!")
1225+
.ecdsa_bls377_generate_new(id, seed)
1226+
.expect("`ecdsa_bls377_generate` failed")
1227+
}
1228+
12101229
/// Generate a `bandersnatch` key pair for the given key type using an optional
12111230
/// `seed` and store it in the keystore.
12121231
///

0 commit comments

Comments
 (0)