Skip to content

Commit

Permalink
- Implement boiler-plates for BLS12-381 crypto.
Browse files Browse the repository at this point in the history
- Make BEEFY pair crypto to be (ECDSA,BLS12-381) instead of (ECDSA,BLS12-377)
  • Loading branch information
drskalman committed Jun 1, 2024
1 parent 8d8c0e1 commit c8af3df
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 25 deletions.
23 changes: 12 additions & 11 deletions substrate/client/consensus/beefy/src/keystore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by

// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

Expand All @@ -20,7 +21,7 @@ use log::warn;

use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, AppCrypto, RuntimeAppPublic};
#[cfg(feature = "bls-experimental")]
use sp_core::ecdsa_bls377;
use sp_core::ecdsa_bls381;
use sp_core::{ecdsa, keccak_256};

use sp_keystore::KeystorePtr;
Expand Down Expand Up @@ -100,13 +101,13 @@ impl<AuthorityId: AuthorityIdBound> BeefyKeystore<AuthorityId> {
},

#[cfg(feature = "bls-experimental")]
ecdsa_bls377::CRYPTO_ID => {
let public: ecdsa_bls377::Public =
ecdsa_bls377::Public::try_from(public.as_slice()).unwrap();
ecdsa_bls381::CRYPTO_ID => {
let public: ecdsa_bls381::Public =
ecdsa_bls381::Public::try_from(public.as_slice()).unwrap();
let sig = store
.ecdsa_bls377_sign_with_keccak256(BEEFY_KEY_TYPE, &public, &message)
.ecdsa_bls381_sign_with_keccak256(BEEFY_KEY_TYPE, &public, &message)
.map_err(|e| error::Error::Keystore(e.to_string()))?
.ok_or_else(|| error::Error::Signature("bls377_sign() failed".to_string()))?;
.ok_or_else(|| error::Error::Signature("bls381_sign() failed".to_string()))?;
let sig_ref: &[u8] = sig.as_ref();
sig_ref.to_vec()
},
Expand Down Expand Up @@ -146,8 +147,8 @@ impl<AuthorityId: AuthorityIdBound> BeefyKeystore<AuthorityId> {
}),

#[cfg(feature = "bls-experimental")]
ecdsa_bls377::CRYPTO_ID => store
.ecdsa_bls377_public_keys(BEEFY_KEY_TYPE)
ecdsa_bls381::CRYPTO_ID => store
.ecdsa_bls381_public_keys(BEEFY_KEY_TYPE)
.drain(..)
.map(|pk| AuthorityId::try_from(pk.as_ref()))
.collect::<Result<Vec<_>, _>>()
Expand Down Expand Up @@ -254,9 +255,9 @@ pub mod tests {
AuthorityId::decode(&mut pk.as_ref()).unwrap()
},
#[cfg(feature = "bls-experimental")]
ecdsa_bls377::CRYPTO_ID => {
ecdsa_bls381::CRYPTO_ID => {
let pk = store
.ecdsa_bls377_generate_new(key_type, optional_seed.as_deref())
.ecdsa_bls381_generate_new(key_type, optional_seed.as_deref())
.ok()
.unwrap();
AuthorityId::decode(&mut pk.as_ref()).unwrap()
Expand Down Expand Up @@ -452,7 +453,7 @@ pub mod tests {
#[cfg(feature = "bls-experimental")]
#[test]
fn sign_error_for_ecdsa_n_bls() {
sign_error::<ecdsa_bls_crypto::AuthorityId>("bls377_sign() failed");
sign_error::<ecdsa_bls_crypto::AuthorityId>("bls381_sign() failed");
}

#[test]
Expand Down
39 changes: 38 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, ecdsa_bls377, KeccakHasher};
use sp_core::{bls377, bls381, ecdsa_bls377, ecdsa_bls381, KeccakHasher};
}

use crate::{Error, Result};
Expand Down Expand Up @@ -418,6 +418,43 @@ impl Keystore for LocalKeystore {
Ok(sig)
}

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

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

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

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


}
}
Expand Down
4 changes: 3 additions & 1 deletion substrate/primitives/application-crypto/src/bls377.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ mod app {
crate::app_crypto!(super, sp_core::testing::BLS377);
}

pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature};
#[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;
Expand Down
29 changes: 29 additions & 0 deletions substrate/primitives/application-crypto/src/bls381.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
// limitations under the License.

//! BLS12-381 crypto applications.
use crate::{KeyTypeId, RuntimePublic};

pub use sp_core::bls::bls381::*;
use sp_std::vec::Vec;

mod app {
crate::app_crypto!(super, sp_core::testing::BLS381);
Expand All @@ -26,3 +28,30 @@ mod app {
#[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::bls381_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)
}
}
58 changes: 58 additions & 0 deletions substrate/primitives/application-crypto/src/ecdsa_bls381.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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-381 paired crypto applications.
use crate::{KeyTypeId, RuntimePublic};
use sp_std::vec::Vec;

pub use sp_core::paired_crypto::ecdsa_bls381::*;

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

#[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_bls381_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 @@ -49,6 +49,8 @@ pub mod bls381;
pub mod ecdsa;
#[cfg(feature = "bls-experimental")]
pub mod ecdsa_bls377;
#[cfg(feature = "bls-experimental")]
pub mod ecdsa_bls381;
pub mod ed25519;
pub mod sr25519;
mod traits;
Expand Down
12 changes: 6 additions & 6 deletions substrate/primitives/consensus/beefy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ pub mod ecdsa_crypto {
#[cfg(feature = "bls-experimental")]
pub mod bls_crypto {
use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
use sp_application_crypto::{app_crypto, bls377};
use sp_core::{bls377::Pair as BlsPair, crypto::Wraps, Pair as _};
use sp_application_crypto::{app_crypto, bls381};
use sp_core::{bls381::Pair as BlsPair, crypto::Wraps, Pair as _};

app_crypto!(bls377, KEY_TYPE);
app_crypto!(bls381, KEY_TYPE);

/// Identity of a BEEFY authority using BLS as its crypto.
pub type AuthorityId = Public;
Expand Down Expand Up @@ -184,10 +184,10 @@ pub mod bls_crypto {
#[cfg(feature = "bls-experimental")]
pub mod ecdsa_bls_crypto {
use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
use sp_application_crypto::{app_crypto, ecdsa_bls377};
use sp_core::{crypto::Wraps, ecdsa_bls377::Pair as EcdsaBlsPair};
use sp_application_crypto::{app_crypto, ecdsa_bls381};
use sp_core::{crypto::Wraps, ecdsa_bls381::Pair as EcdsaBlsPair};

app_crypto!(ecdsa_bls377, KEY_TYPE);
app_crypto!(ecdsa_bls381, KEY_TYPE);

/// Identity of a BEEFY authority using (ECDSA,BLS) as its crypto.
pub type AuthorityId = Public;
Expand Down
3 changes: 3 additions & 0 deletions substrate/primitives/core/src/bls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ pub mod bls381 {
/// An identifier used to match public keys against BLS12-381 keys
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"bls8");

#[doc(hidden)]
pub type Bls381Tag = TinyBLS381;

/// BLS12-381 key pair.
pub type Pair = super::Pair<TinyBLS381>;
/// BLS12-381 public key.
Expand Down
2 changes: 1 addition & 1 deletion substrate/primitives/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub mod sr25519;
#[cfg(feature = "bls-experimental")]
pub use bls::{bls377, bls381};
#[cfg(feature = "bls-experimental")]
pub use paired_crypto::ecdsa_bls377;
pub use paired_crypto::{ecdsa_bls377, ecdsa_bls381};

pub use self::{
hash::{convert_hash, H160, H256, H512},
Expand Down
100 changes: 100 additions & 0 deletions substrate/primitives/core/src/paired_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,106 @@ pub mod ecdsa_bls377 {
}
}

/// ECDSA and BLS12-381 paired crypto scheme
#[cfg(feature = "bls-experimental")]
pub mod ecdsa_bls381 {
use crate::{bls381, crypto::CryptoTypeId, ecdsa};
#[cfg(feature = "full_crypto")]
use crate::{
crypto::{Pair as PairT, UncheckedFrom},
Hasher,
};

/// An identifier used to match public keys against BLS12-381 keys
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ecb8");

const PUBLIC_KEY_LEN: usize =
ecdsa::PUBLIC_KEY_SERIALIZED_SIZE + bls381::PUBLIC_KEY_SERIALIZED_SIZE;
const SIGNATURE_LEN: usize =
ecdsa::SIGNATURE_SERIALIZED_SIZE + bls381::SIGNATURE_SERIALIZED_SIZE;

#[doc(hidden)]
pub struct EcdsaBls381Tag(ecdsa::EcdsaTag, bls381::Bls381Tag);

impl super::PairedCryptoSubTagBound for EcdsaBls381Tag {}

/// (ECDSA,BLS12-381) key-pair pair.
pub type Pair =
super::Pair<ecdsa::Pair, bls381::Pair, PUBLIC_KEY_LEN, SIGNATURE_LEN, EcdsaBls381Tag>;

/// (ECDSA,BLS12-381) public key pair.
pub type Public = super::Public<PUBLIC_KEY_LEN, EcdsaBls381Tag>;

/// (ECDSA,BLS12-381) signature pair.
pub type Signature = super::Signature<SIGNATURE_LEN, EcdsaBls381Tag>;

impl super::CryptoType for Public {
type Pair = Pair;
}

impl super::CryptoType for Signature {
type Pair = Pair;
}

impl super::CryptoType for Pair {
type Pair = Pair;
}

#[cfg(feature = "full_crypto")]
impl Pair {
/// Hashes the `message` with the specified [`Hasher`] before signing with the ECDSA secret
/// component.
///
/// The hasher does not affect the BLS12-381 component. This generates BLS12-381 Signature
/// according to IETF standard.
pub fn sign_with_hasher<H>(&self, message: &[u8]) -> Signature
where
H: Hasher,
H::Out: Into<[u8; 32]>,
{
let msg_hash = H::hash(message).into();

let mut raw: [u8; SIGNATURE_LEN] = [0u8; SIGNATURE_LEN];
raw[..ecdsa::SIGNATURE_SERIALIZED_SIZE]
.copy_from_slice(self.left.sign_prehashed(&msg_hash).as_ref());
raw[ecdsa::SIGNATURE_SERIALIZED_SIZE..]
.copy_from_slice(self.right.sign(message).as_ref());
<Self as PairT>::Signature::unchecked_from(raw)
}

/// Hashes the `message` with the specified [`Hasher`] before verifying with the ECDSA
/// public component.
///
/// The hasher does not affect the the BLS12-381 component. This verifies whether the
/// BLS12-381 signature was hashed and signed according to IETF standard
pub fn verify_with_hasher<H>(sig: &Signature, message: &[u8], public: &Public) -> bool
where
H: Hasher,
H::Out: Into<[u8; 32]>,
{
let msg_hash = H::hash(message).into();

let Ok(left_pub) = public.0[..ecdsa::PUBLIC_KEY_SERIALIZED_SIZE].try_into() else {
return false
};
let Ok(left_sig) = sig.0[..ecdsa::SIGNATURE_SERIALIZED_SIZE].try_into() else {
return false
};
if !ecdsa::Pair::verify_prehashed(&left_sig, &msg_hash, &left_pub) {
return false
}

let Ok(right_pub) = public.0[ecdsa::PUBLIC_KEY_SERIALIZED_SIZE..].try_into() else {
return false
};
let Ok(right_sig) = sig.0[ecdsa::SIGNATURE_SERIALIZED_SIZE..].try_into() else {
return false
};
bls381::Pair::verify(&right_sig, message, &right_pub)
}
}
}

/// Secure seed length.
///
/// Currently only supporting sub-schemes whose seed is a 32-bytes array.
Expand Down
Loading

0 comments on commit c8af3df

Please sign in to comment.