diff --git a/Cargo.lock b/Cargo.lock
index db9a2ca356433e92bf54114d57e47d29d6e5b4dd..49507ecd4f93916aa68ea989f9d6fcc93a9ab715 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18225,6 +18225,7 @@ dependencies = [
  "sp-core",
  "sp-crypto-hashing",
  "sp-io",
+ "sp-keystore",
  "sp-mmr-primitives",
  "sp-runtime",
  "sp-std 14.0.0",
diff --git a/substrate/client/consensus/beefy/Cargo.toml b/substrate/client/consensus/beefy/Cargo.toml
index 3522697427875034ec330d31b80b22085c1f042b..56c38bf2e479a216aa3343b553583b5079c99a61 100644
--- a/substrate/client/consensus/beefy/Cargo.toml
+++ b/substrate/client/consensus/beefy/Cargo.toml
@@ -52,3 +52,12 @@ sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa" }
 sp-keyring = { path = "../../../primitives/keyring" }
 sp-tracing = { path = "../../../primitives/tracing" }
 substrate-test-runtime-client = { path = "../../../test-utils/runtime/client" }
+
+[features]
+# This feature adds BLS crypto primitives. It should not be used in production since
+# the BLS implementation and interface may still be subject to significant change.
+bls-experimental = [
+	"sp-application-crypto/bls-experimental",
+	"sp-consensus-beefy/bls-experimental",
+	"sp-core/bls-experimental",
+]
diff --git a/substrate/client/consensus/beefy/src/communication/gossip.rs b/substrate/client/consensus/beefy/src/communication/gossip.rs
index 645a10b2a1d43f9bb879a799ac9625b2f623506c..8a0b0a74308f1a6d6bf3bae327b87fac6fb6cdde 100644
--- a/substrate/client/consensus/beefy/src/communication/gossip.rs
+++ b/substrate/client/consensus/beefy/src/communication/gossip.rs
@@ -485,8 +485,8 @@ pub(crate) mod tests {
 	use sc_network_test::Block;
 	use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE;
 	use sp_consensus_beefy::{
-		ecdsa_crypto::Signature, known_payloads, Commitment, Keyring, MmrRootHash, Payload,
-		SignedCommitment, VoteMessage,
+		ecdsa_crypto::Signature, known_payloads, test_utils::Keyring, Commitment, MmrRootHash,
+		Payload, SignedCommitment, VoteMessage,
 	};
 	use sp_keystore::{testing::MemoryKeystore, Keystore};
 
@@ -507,10 +507,13 @@ pub(crate) mod tests {
 		}
 	}
 
-	pub fn sign_commitment<BN: Encode>(who: &Keyring, commitment: &Commitment<BN>) -> Signature {
+	pub fn sign_commitment<BN: Encode>(
+		who: &Keyring<AuthorityId>,
+		commitment: &Commitment<BN>,
+	) -> Signature {
 		let store = MemoryKeystore::new();
 		store.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&who.to_seed())).unwrap();
-		let beefy_keystore: BeefyKeystore = Some(store.into()).into();
+		let beefy_keystore: BeefyKeystore<AuthorityId> = Some(store.into()).into();
 		beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap()
 	}
 
@@ -538,7 +541,10 @@ pub(crate) mod tests {
 			.validators()
 			.iter()
 			.map(|validator: &AuthorityId| {
-				Some(sign_commitment(&Keyring::from_public(validator).unwrap(), &commitment))
+				Some(sign_commitment(
+					&Keyring::<AuthorityId>::from_public(validator).unwrap(),
+					&commitment,
+				))
 			})
 			.collect();
 
@@ -547,7 +553,7 @@ pub(crate) mod tests {
 
 	#[test]
 	fn should_validate_messages() {
-		let keys = vec![Keyring::Alice.public()];
+		let keys = vec![Keyring::<AuthorityId>::Alice.public()];
 		let validator_set = ValidatorSet::<AuthorityId>::new(keys.clone(), 0).unwrap();
 		let (gv, mut report_stream) =
 			GossipValidator::<Block>::new(Arc::new(Mutex::new(KnownPeers::new())));
diff --git a/substrate/client/consensus/beefy/src/justification.rs b/substrate/client/consensus/beefy/src/justification.rs
index 483184e2374a2b9239554e4ce2709cd7a294cb8c..7f1b9e5237c3970eb8cba4936debd18c77117288 100644
--- a/substrate/client/consensus/beefy/src/justification.rs
+++ b/substrate/client/consensus/beefy/src/justification.rs
@@ -76,7 +76,7 @@ pub(crate) fn verify_with_validator_set<Block: BlockT>(
 						.as_ref()
 						.map(|sig| {
 							signatures_checked += 1;
-							BeefyKeystore::verify(id, sig, &message[..])
+							BeefyKeystore::verify(*id, sig, &message[..])
 						})
 						.unwrap_or(false)
 				})
@@ -93,7 +93,8 @@ pub(crate) fn verify_with_validator_set<Block: BlockT>(
 #[cfg(test)]
 pub(crate) mod tests {
 	use sp_consensus_beefy::{
-		known_payloads, Commitment, Keyring, Payload, SignedCommitment, VersionedFinalityProof,
+		known_payloads, test_utils::Keyring, Commitment, Payload, SignedCommitment,
+		VersionedFinalityProof,
 	};
 	use substrate_test_runtime_client::runtime::Block;
 
@@ -103,7 +104,7 @@ pub(crate) mod tests {
 	pub(crate) fn new_finality_proof(
 		block_num: NumberFor<Block>,
 		validator_set: &ValidatorSet<AuthorityId>,
-		keys: &[Keyring],
+		keys: &[Keyring<AuthorityId>],
 	) -> BeefyVersionedFinalityProof<Block> {
 		let commitment = Commitment {
 			payload: Payload::from_single_entry(known_payloads::MMR_ROOT_ID, vec![]),
@@ -174,7 +175,7 @@ pub(crate) mod tests {
 		};
 		// change a signature to a different key
 		*bad_signed_commitment.signatures.first_mut().unwrap() =
-			Some(Keyring::Dave.sign(&bad_signed_commitment.commitment.encode()));
+			Some(Keyring::<AuthorityId>::Dave.sign(&bad_signed_commitment.commitment.encode()));
 		match verify_with_validator_set::<Block>(block_num, &validator_set, &bad_proof.into()) {
 			Err((ConsensusError::InvalidJustification, 3)) => (),
 			e => assert!(false, "Got unexpected {:?}", e),
diff --git a/substrate/client/consensus/beefy/src/keystore.rs b/substrate/client/consensus/beefy/src/keystore.rs
index 75c44de3324cee7b4f0cc4a08335640708952157..2ddc938fbc6ce33aa74c62d051cf77a519c634ee 100644
--- a/substrate/client/consensus/beefy/src/keystore.rs
+++ b/substrate/client/consensus/beefy/src/keystore.rs
@@ -16,41 +16,45 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, RuntimeAppPublic};
+use sp_application_crypto::{key_types::BEEFY as BEEFY_KEY_TYPE, AppCrypto, RuntimeAppPublic};
+use sp_consensus_beefy::{AuthorityIdBound, BeefyAuthorityId, BeefySignatureHasher};
+use sp_core::ecdsa;
+#[cfg(feature = "bls-experimental")]
+use sp_core::ecdsa_bls377;
 use sp_crypto_hashing::keccak_256;
 use sp_keystore::KeystorePtr;
 
+use codec::Decode;
 use log::warn;
-
-use sp_consensus_beefy::{
-	ecdsa_crypto::{Public, Signature},
-	BeefyAuthorityId,
-};
+use std::marker::PhantomData;
 
 use crate::{error, LOG_TARGET};
 
-/// Hasher used for BEEFY signatures.
-pub(crate) type BeefySignatureHasher = sp_runtime::traits::Keccak256;
-
 /// A BEEFY specific keystore implemented as a `Newtype`. This is basically a
 /// wrapper around [`sp_keystore::Keystore`] and allows to customize
 /// common cryptographic functionality.
-pub(crate) struct BeefyKeystore(Option<KeystorePtr>);
+pub(crate) struct BeefyKeystore<AuthorityId: AuthorityIdBound>(
+	Option<KeystorePtr>,
+	PhantomData<fn() -> AuthorityId>,
+);
 
-impl BeefyKeystore {
+impl<AuthorityId: AuthorityIdBound> BeefyKeystore<AuthorityId> {
 	/// Check if the keystore contains a private key for one of the public keys
 	/// contained in `keys`. A public key with a matching private key is known
 	/// as a local authority id.
 	///
 	/// Return the public key for which we also do have a private key. If no
 	/// matching private key is found, `None` will be returned.
-	pub fn authority_id(&self, keys: &[Public]) -> Option<Public> {
+	pub fn authority_id(&self, keys: &[AuthorityId]) -> Option<AuthorityId> {
 		let store = self.0.clone()?;
 
 		// we do check for multiple private keys as a key store sanity check.
-		let public: Vec<Public> = keys
+		let public: Vec<AuthorityId> = keys
 			.iter()
-			.filter(|k| store.has_keys(&[(k.to_raw_vec(), BEEFY_KEY_TYPE)]))
+			.filter(|k| {
+				store
+					.has_keys(&[(<AuthorityId as RuntimeAppPublic>::to_raw_vec(k), BEEFY_KEY_TYPE)])
+			})
 			.cloned()
 			.collect();
 
@@ -71,55 +75,125 @@ impl BeefyKeystore {
 	/// Note that `message` usually will be pre-hashed before being signed.
 	///
 	/// Return the message signature or an error in case of failure.
-	pub fn sign(&self, public: &Public, message: &[u8]) -> Result<Signature, error::Error> {
+	pub fn sign(
+		&self,
+		public: &AuthorityId,
+		message: &[u8],
+	) -> Result<<AuthorityId as RuntimeAppPublic>::Signature, error::Error> {
 		let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?;
 
-		let msg = keccak_256(message);
-		let public = public.as_ref();
-
-		let sig = store
-			.ecdsa_sign_prehashed(BEEFY_KEY_TYPE, public, &msg)
-			.map_err(|e| error::Error::Keystore(e.to_string()))?
-			.ok_or_else(|| error::Error::Signature("ecdsa_sign_prehashed() failed".to_string()))?;
-
-		// check that `sig` has the expected result type
-		let sig = sig.clone().try_into().map_err(|_| {
-			error::Error::Signature(format!("invalid signature {:?} for key {:?}", sig, public))
+		// ECDSA should use ecdsa_sign_prehashed since it needs to be hashed by keccak_256 instead
+		// of blake2. As such we need to deal with producing the signatures case-by-case
+		let signature_byte_array: Vec<u8> = match <AuthorityId as AppCrypto>::CRYPTO_ID {
+			ecdsa::CRYPTO_ID => {
+				let msg_hash = keccak_256(message);
+				let public: ecdsa::Public = ecdsa::Public::try_from(public.as_slice()).unwrap();
+
+				let sig = store
+					.ecdsa_sign_prehashed(BEEFY_KEY_TYPE, &public, &msg_hash)
+					.map_err(|e| error::Error::Keystore(e.to_string()))?
+					.ok_or_else(|| {
+						error::Error::Signature("ecdsa_sign_prehashed() failed".to_string())
+					})?;
+				let sig_ref: &[u8] = sig.as_ref();
+				sig_ref.to_vec()
+			},
+
+			#[cfg(feature = "bls-experimental")]
+			ecdsa_bls377::CRYPTO_ID => {
+				let public: ecdsa_bls377::Public =
+					ecdsa_bls377::Public::try_from(public.as_slice()).unwrap();
+				let sig = store
+					.ecdsa_bls377_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()))?;
+				let sig_ref: &[u8] = sig.as_ref();
+				sig_ref.to_vec()
+			},
+
+			_ => Err(error::Error::Keystore("key type is not supported by BEEFY Keystore".into()))?,
+		};
+
+		//check that `sig` has the expected result type
+		let signature = <AuthorityId as RuntimeAppPublic>::Signature::decode(
+			&mut signature_byte_array.as_slice(),
+		)
+		.map_err(|_| {
+			error::Error::Signature(format!(
+				"invalid signature {:?} for key {:?}",
+				signature_byte_array, public
+			))
 		})?;
 
-		Ok(sig)
+		Ok(signature)
 	}
 
 	/// Returns a vector of [`sp_consensus_beefy::crypto::Public`] keys which are currently
 	/// supported (i.e. found in the keystore).
-	pub fn public_keys(&self) -> Result<Vec<Public>, error::Error> {
+	pub fn public_keys(&self) -> Result<Vec<AuthorityId>, error::Error> {
 		let store = self.0.clone().ok_or_else(|| error::Error::Keystore("no Keystore".into()))?;
 
-		let pk: Vec<Public> =
-			store.ecdsa_public_keys(BEEFY_KEY_TYPE).drain(..).map(Public::from).collect();
-
-		Ok(pk)
+		let pk = match <AuthorityId as AppCrypto>::CRYPTO_ID {
+			ecdsa::CRYPTO_ID => store
+				.ecdsa_public_keys(BEEFY_KEY_TYPE)
+				.drain(..)
+				.map(|pk| AuthorityId::try_from(pk.as_ref()))
+				.collect::<Result<Vec<_>, _>>()
+				.or_else(|_| {
+					Err(error::Error::Keystore(
+						"unable to convert public key into authority id".into(),
+					))
+				}),
+
+			#[cfg(feature = "bls-experimental")]
+			ecdsa_bls377::CRYPTO_ID => store
+				.ecdsa_bls377_public_keys(BEEFY_KEY_TYPE)
+				.drain(..)
+				.map(|pk| AuthorityId::try_from(pk.as_ref()))
+				.collect::<Result<Vec<_>, _>>()
+				.or_else(|_| {
+					Err(error::Error::Keystore(
+						"unable to convert public key into authority id".into(),
+					))
+				}),
+
+			_ => Err(error::Error::Keystore("key type is not supported by BEEFY Keystore".into())),
+		};
+
+		pk
 	}
 
 	/// Use the `public` key to verify that `sig` is a valid signature for `message`.
 	///
 	/// Return `true` if the signature is authentic, `false` otherwise.
-	pub fn verify(public: &Public, sig: &Signature, message: &[u8]) -> bool {
+	pub fn verify(
+		public: &AuthorityId,
+		sig: &<AuthorityId as RuntimeAppPublic>::Signature,
+		message: &[u8],
+	) -> bool {
 		BeefyAuthorityId::<BeefySignatureHasher>::verify(public, sig, message)
 	}
 }
 
-impl From<Option<KeystorePtr>> for BeefyKeystore {
-	fn from(store: Option<KeystorePtr>) -> BeefyKeystore {
-		BeefyKeystore(store)
+impl<AuthorityId: AuthorityIdBound> From<Option<KeystorePtr>> for BeefyKeystore<AuthorityId>
+where
+	<AuthorityId as RuntimeAppPublic>::Signature: Send + Sync,
+{
+	fn from(store: Option<KeystorePtr>) -> BeefyKeystore<AuthorityId> {
+		BeefyKeystore(store, PhantomData)
 	}
 }
 
 #[cfg(test)]
 pub mod tests {
-	use sp_consensus_beefy::{ecdsa_crypto, Keyring};
-	use sp_core::{ecdsa, Pair};
-	use sp_keystore::testing::MemoryKeystore;
+	#[cfg(feature = "bls-experimental")]
+	use sp_consensus_beefy::ecdsa_bls_crypto;
+	use sp_consensus_beefy::{
+		ecdsa_crypto,
+		test_utils::{BeefySignerAuthority, Keyring},
+	};
+	use sp_core::Pair as PairT;
+	use sp_keystore::{testing::MemoryKeystore, Keystore};
 
 	use super::*;
 	use crate::error::Error;
@@ -128,152 +202,265 @@ pub mod tests {
 		MemoryKeystore::new().into()
 	}
 
-	#[test]
-	fn verify_should_work() {
-		let msg = keccak_256(b"I am Alice!");
-		let sig = Keyring::Alice.sign(b"I am Alice!");
-
-		assert!(ecdsa::Pair::verify_prehashed(
-			&sig.clone().into(),
-			&msg,
-			&Keyring::Alice.public().into(),
+	fn pair_verify_should_work<
+		AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+	>()
+	where
+		<AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
+			Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+		<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
+	{
+		let msg = b"I am Alice!";
+		let sig = Keyring::<AuthorityId>::Alice.sign(b"I am Alice!");
+
+		assert!(<AuthorityId as BeefyAuthorityId<BeefySignatureHasher>>::verify(
+			&Keyring::Alice.public(),
+			&sig,
+			&msg.as_slice(),
 		));
 
 		// different public key -> fail
-		assert!(!ecdsa::Pair::verify_prehashed(
-			&sig.clone().into(),
-			&msg,
-			&Keyring::Bob.public().into(),
+		assert!(!<AuthorityId as BeefyAuthorityId<BeefySignatureHasher>>::verify(
+			&Keyring::Bob.public(),
+			&sig,
+			&msg.as_slice(),
 		));
 
-		let msg = keccak_256(b"I am not Alice!");
+		let msg = b"I am not Alice!";
 
 		// different msg -> fail
-		assert!(
-			!ecdsa::Pair::verify_prehashed(&sig.into(), &msg, &Keyring::Alice.public().into(),)
-		);
+		assert!(!<AuthorityId as BeefyAuthorityId<BeefySignatureHasher>>::verify(
+			&Keyring::Alice.public(),
+			&sig,
+			&msg.as_slice(),
+		));
+	}
+
+	/// Generate key pair in the given store using the provided seed
+	fn generate_in_store<AuthorityId>(
+		store: KeystorePtr,
+		key_type: sp_application_crypto::KeyTypeId,
+		owner: Option<Keyring<AuthorityId>>,
+	) -> AuthorityId
+	where
+		AuthorityId:
+			AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+		<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<BeefySignatureHasher>,
+		<AuthorityId as RuntimeAppPublic>::Signature:
+			Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+	{
+		let optional_seed: Option<String> = owner.map(|owner| owner.to_seed());
+
+		match <AuthorityId as AppCrypto>::CRYPTO_ID {
+			ecdsa::CRYPTO_ID => {
+				let pk = store.ecdsa_generate_new(key_type, optional_seed.as_deref()).ok().unwrap();
+				AuthorityId::decode(&mut pk.as_ref()).unwrap()
+			},
+			#[cfg(feature = "bls-experimental")]
+			ecdsa_bls377::CRYPTO_ID => {
+				let pk = store
+					.ecdsa_bls377_generate_new(key_type, optional_seed.as_deref())
+					.ok()
+					.unwrap();
+				AuthorityId::decode(&mut pk.as_ref()).unwrap()
+			},
+			_ => panic!("Requested CRYPTO_ID is not supported by the BEEFY Keyring"),
+		}
 	}
 
 	#[test]
-	fn pair_works() {
-		let want = ecdsa_crypto::Pair::from_string("//Alice", None)
+	fn pair_verify_should_work_ecdsa() {
+		pair_verify_should_work::<ecdsa_crypto::AuthorityId>();
+	}
+
+	#[cfg(feature = "bls-experimental")]
+	#[test]
+	fn pair_verify_should_work_ecdsa_n_bls() {
+		pair_verify_should_work::<ecdsa_bls_crypto::AuthorityId>();
+	}
+
+	fn pair_works<
+		AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+	>()
+	where
+		<AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
+			Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+		<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
+	{
+		let want = <AuthorityId as AppCrypto>::Pair::from_string("//Alice", None)
 			.expect("Pair failed")
 			.to_raw_vec();
-		let got = Keyring::Alice.pair().to_raw_vec();
+		let got = Keyring::<AuthorityId>::Alice.pair().to_raw_vec();
 		assert_eq!(want, got);
 
-		let want = ecdsa_crypto::Pair::from_string("//Bob", None)
+		let want = <AuthorityId as AppCrypto>::Pair::from_string("//Bob", None)
 			.expect("Pair failed")
 			.to_raw_vec();
-		let got = Keyring::Bob.pair().to_raw_vec();
+		let got = Keyring::<AuthorityId>::Bob.pair().to_raw_vec();
 		assert_eq!(want, got);
 
-		let want = ecdsa_crypto::Pair::from_string("//Charlie", None)
+		let want = <AuthorityId as AppCrypto>::Pair::from_string("//Charlie", None)
 			.expect("Pair failed")
 			.to_raw_vec();
-		let got = Keyring::Charlie.pair().to_raw_vec();
+		let got = Keyring::<AuthorityId>::Charlie.pair().to_raw_vec();
 		assert_eq!(want, got);
 
-		let want = ecdsa_crypto::Pair::from_string("//Dave", None)
+		let want = <AuthorityId as AppCrypto>::Pair::from_string("//Dave", None)
 			.expect("Pair failed")
 			.to_raw_vec();
-		let got = Keyring::Dave.pair().to_raw_vec();
+		let got = Keyring::<AuthorityId>::Dave.pair().to_raw_vec();
 		assert_eq!(want, got);
 
-		let want = ecdsa_crypto::Pair::from_string("//Eve", None)
+		let want = <AuthorityId as AppCrypto>::Pair::from_string("//Eve", None)
 			.expect("Pair failed")
 			.to_raw_vec();
-		let got = Keyring::Eve.pair().to_raw_vec();
+		let got = Keyring::<AuthorityId>::Eve.pair().to_raw_vec();
 		assert_eq!(want, got);
 
-		let want = ecdsa_crypto::Pair::from_string("//Ferdie", None)
+		let want = <AuthorityId as AppCrypto>::Pair::from_string("//Ferdie", None)
 			.expect("Pair failed")
 			.to_raw_vec();
-		let got = Keyring::Ferdie.pair().to_raw_vec();
+		let got = Keyring::<AuthorityId>::Ferdie.pair().to_raw_vec();
 		assert_eq!(want, got);
 
-		let want = ecdsa_crypto::Pair::from_string("//One", None)
+		let want = <AuthorityId as AppCrypto>::Pair::from_string("//One", None)
 			.expect("Pair failed")
 			.to_raw_vec();
-		let got = Keyring::One.pair().to_raw_vec();
+		let got = Keyring::<AuthorityId>::One.pair().to_raw_vec();
 		assert_eq!(want, got);
 
-		let want = ecdsa_crypto::Pair::from_string("//Two", None)
+		let want = <AuthorityId as AppCrypto>::Pair::from_string("//Two", None)
 			.expect("Pair failed")
 			.to_raw_vec();
-		let got = Keyring::Two.pair().to_raw_vec();
+		let got = Keyring::<AuthorityId>::Two.pair().to_raw_vec();
 		assert_eq!(want, got);
 	}
 
 	#[test]
-	fn authority_id_works() {
+	fn ecdsa_pair_works() {
+		pair_works::<ecdsa_crypto::AuthorityId>();
+	}
+
+	#[cfg(feature = "bls-experimental")]
+	#[test]
+	fn ecdsa_n_bls_pair_works() {
+		pair_works::<ecdsa_bls_crypto::AuthorityId>();
+	}
+
+	fn authority_id_works<
+		AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+	>()
+	where
+		<AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
+			Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+		<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
+	{
 		let store = keystore();
 
-		let alice: ecdsa_crypto::Public = store
-			.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed()))
-			.ok()
-			.unwrap()
-			.into();
+		generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice));
+
+		let alice = Keyring::<AuthorityId>::Alice.public();
 
 		let bob = Keyring::Bob.public();
 		let charlie = Keyring::Charlie.public();
 
-		let store: BeefyKeystore = Some(store).into();
+		let beefy_store: BeefyKeystore<AuthorityId> = Some(store).into();
 
 		let mut keys = vec![bob, charlie];
 
-		let id = store.authority_id(keys.as_slice());
+		let id = beefy_store.authority_id(keys.as_slice());
 		assert!(id.is_none());
 
 		keys.push(alice.clone());
 
-		let id = store.authority_id(keys.as_slice()).unwrap();
+		let id = beefy_store.authority_id(keys.as_slice()).unwrap();
 		assert_eq!(id, alice);
 	}
 
 	#[test]
-	fn sign_works() {
+	fn authority_id_works_for_ecdsa() {
+		authority_id_works::<ecdsa_crypto::AuthorityId>();
+	}
+
+	#[cfg(feature = "bls-experimental")]
+	#[test]
+	fn authority_id_works_for_ecdsa_n_bls() {
+		authority_id_works::<ecdsa_bls_crypto::AuthorityId>();
+	}
+
+	fn sign_works<
+		AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+	>()
+	where
+		<AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
+			Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+		<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
+	{
 		let store = keystore();
 
-		let alice: ecdsa_crypto::Public = store
-			.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed()))
-			.ok()
-			.unwrap()
-			.into();
+		generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice));
 
-		let store: BeefyKeystore = Some(store).into();
+		let alice = Keyring::Alice.public();
+
+		let store: BeefyKeystore<AuthorityId> = Some(store).into();
 
 		let msg = b"are you involved or commited?";
 
 		let sig1 = store.sign(&alice, msg).unwrap();
-		let sig2 = Keyring::Alice.sign(msg);
+		let sig2 = Keyring::<AuthorityId>::Alice.sign(msg);
 
 		assert_eq!(sig1, sig2);
 	}
 
 	#[test]
-	fn sign_error() {
+	fn sign_works_for_ecdsa() {
+		sign_works::<ecdsa_crypto::AuthorityId>();
+	}
+
+	#[cfg(feature = "bls-experimental")]
+	#[test]
+	fn sign_works_for_ecdsa_n_bls() {
+		sign_works::<ecdsa_bls_crypto::AuthorityId>();
+	}
+
+	fn sign_error<
+		AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+	>(
+		expected_error_message: &str,
+	) where
+		<AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
+			Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+		<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
+	{
 		let store = keystore();
 
-		store
-			.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Bob.to_seed()))
-			.ok()
-			.unwrap();
+		generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Bob));
 
-		let store: BeefyKeystore = Some(store).into();
+		let store: BeefyKeystore<AuthorityId> = Some(store).into();
 
 		let alice = Keyring::Alice.public();
 
 		let msg = b"are you involved or commited?";
 		let sig = store.sign(&alice, msg).err().unwrap();
-		let err = Error::Signature("ecdsa_sign_prehashed() failed".to_string());
+		let err = Error::Signature(expected_error_message.to_string());
 
 		assert_eq!(sig, err);
 	}
 
+	#[test]
+	fn sign_error_for_ecdsa() {
+		sign_error::<ecdsa_crypto::AuthorityId>("ecdsa_sign_prehashed() failed");
+	}
+
+	#[cfg(feature = "bls-experimental")]
+	#[test]
+	fn sign_error_for_ecdsa_n_bls() {
+		sign_error::<ecdsa_bls_crypto::AuthorityId>("bls377_sign()  failed");
+	}
+
 	#[test]
 	fn sign_no_keystore() {
-		let store: BeefyKeystore = None.into();
+		let store: BeefyKeystore<ecdsa_crypto::Public> = None.into();
 
 		let alice = Keyring::Alice.public();
 		let msg = b"are you involved or commited";
@@ -283,17 +470,21 @@ pub mod tests {
 		assert_eq!(sig, err);
 	}
 
-	#[test]
-	fn verify_works() {
+	fn verify_works<
+		AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+	>()
+	where
+		<AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
+			Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+		<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
+	{
 		let store = keystore();
 
-		let alice: ecdsa_crypto::Public = store
-			.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&Keyring::Alice.to_seed()))
-			.ok()
-			.unwrap()
-			.into();
+		generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Alice));
 
-		let store: BeefyKeystore = Some(store).into();
+		let store: BeefyKeystore<AuthorityId> = Some(store).into();
+
+		let alice = Keyring::Alice.public();
 
 		// `msg` and `sig` match
 		let msg = b"are you involved or commited?";
@@ -305,32 +496,48 @@ pub mod tests {
 		assert!(!BeefyKeystore::verify(&alice, &sig, msg));
 	}
 
-	// Note that we use keys with and without a seed for this test.
 	#[test]
-	fn public_keys_works() {
+	fn verify_works_for_ecdsa() {
+		verify_works::<ecdsa_crypto::AuthorityId>();
+	}
+
+	#[cfg(feature = "bls-experimental")]
+	#[test]
+
+	fn verify_works_for_ecdsa_n_bls() {
+		verify_works::<ecdsa_bls_crypto::AuthorityId>();
+	}
+
+	// Note that we use keys with and without a seed for this test.
+	fn public_keys_works<
+		AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+	>()
+	where
+		<AuthorityId as sp_runtime::RuntimeAppPublic>::Signature:
+			Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+		<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<sp_runtime::traits::Keccak256>,
+	{
 		const TEST_TYPE: sp_application_crypto::KeyTypeId =
 			sp_application_crypto::KeyTypeId(*b"test");
 
 		let store = keystore();
 
-		let add_key =
-			|key_type, seed: Option<&str>| store.ecdsa_generate_new(key_type, seed).unwrap();
-
 		// test keys
-		let _ = add_key(TEST_TYPE, Some(Keyring::Alice.to_seed().as_str()));
-		let _ = add_key(TEST_TYPE, Some(Keyring::Bob.to_seed().as_str()));
-
-		let _ = add_key(TEST_TYPE, None);
-		let _ = add_key(TEST_TYPE, None);
+		let _ = generate_in_store::<AuthorityId>(store.clone(), TEST_TYPE, Some(Keyring::Alice));
+		let _ = generate_in_store::<AuthorityId>(store.clone(), TEST_TYPE, Some(Keyring::Bob));
 
 		// BEEFY keys
-		let _ = add_key(BEEFY_KEY_TYPE, Some(Keyring::Dave.to_seed().as_str()));
-		let _ = add_key(BEEFY_KEY_TYPE, Some(Keyring::Eve.to_seed().as_str()));
+		let _ =
+			generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Dave));
+		let _ = generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, Some(Keyring::Eve));
+
+		let _ = generate_in_store::<AuthorityId>(store.clone(), TEST_TYPE, None);
+		let _ = generate_in_store::<AuthorityId>(store.clone(), TEST_TYPE, None);
 
-		let key1: ecdsa_crypto::Public = add_key(BEEFY_KEY_TYPE, None).into();
-		let key2: ecdsa_crypto::Public = add_key(BEEFY_KEY_TYPE, None).into();
+		let key1 = generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, None);
+		let key2 = generate_in_store::<AuthorityId>(store.clone(), BEEFY_KEY_TYPE, None);
 
-		let store: BeefyKeystore = Some(store).into();
+		let store: BeefyKeystore<AuthorityId> = Some(store).into();
 
 		let keys = store.public_keys().ok().unwrap();
 
@@ -340,4 +547,16 @@ pub mod tests {
 		assert!(keys.contains(&key1));
 		assert!(keys.contains(&key2));
 	}
+
+	#[test]
+	fn public_keys_works_for_ecdsa_keystore() {
+		public_keys_works::<ecdsa_crypto::AuthorityId>();
+	}
+
+	#[cfg(feature = "bls-experimental")]
+	#[test]
+
+	fn public_keys_works_for_ecdsa_n_bls() {
+		public_keys_works::<ecdsa_bls_crypto::AuthorityId>();
+	}
 }
diff --git a/substrate/client/consensus/beefy/src/round.rs b/substrate/client/consensus/beefy/src/round.rs
index 47414c60fdb5fc8fc94e60136481ebaefd4d7560..0045dc70c260ee43ca1137d7d7f92b097220aeaf 100644
--- a/substrate/client/consensus/beefy/src/round.rs
+++ b/substrate/client/consensus/beefy/src/round.rs
@@ -207,7 +207,7 @@ mod tests {
 	use sc_network_test::Block;
 
 	use sp_consensus_beefy::{
-		known_payloads::MMR_ROOT_ID, Commitment, EquivocationProof, Keyring, Payload,
+		known_payloads::MMR_ROOT_ID, test_utils::Keyring, Commitment, EquivocationProof, Payload,
 		SignedCommitment, ValidatorSet, VoteMessage,
 	};
 
@@ -226,7 +226,7 @@ mod tests {
 	#[test]
 	fn round_tracker() {
 		let mut rt = RoundTracker::default();
-		let bob_vote = (Keyring::Bob.public(), Keyring::Bob.sign(b"I am committed"));
+		let bob_vote = (Keyring::Bob.public(), Keyring::<AuthorityId>::Bob.sign(b"I am committed"));
 		let threshold = 2;
 
 		// adding new vote allowed
@@ -237,7 +237,8 @@ mod tests {
 		// vote is not done
 		assert!(!rt.is_done(threshold));
 
-		let alice_vote = (Keyring::Alice.public(), Keyring::Alice.sign(b"I am committed"));
+		let alice_vote =
+			(Keyring::Alice.public(), Keyring::<AuthorityId>::Alice.sign(b"I am committed"));
 		// adding new vote (self vote this time) allowed
 		assert!(rt.add_vote(alice_vote));
 
@@ -271,7 +272,11 @@ mod tests {
 		assert_eq!(42, rounds.validator_set_id());
 		assert_eq!(1, rounds.session_start());
 		assert_eq!(
-			&vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()],
+			&vec![
+				Keyring::<AuthorityId>::Alice.public(),
+				Keyring::<AuthorityId>::Bob.public(),
+				Keyring::<AuthorityId>::Charlie.public()
+			],
 			rounds.validators()
 		);
 	}
@@ -301,7 +306,7 @@ mod tests {
 		let mut vote = VoteMessage {
 			id: Keyring::Alice.public(),
 			commitment: commitment.clone(),
-			signature: Keyring::Alice.sign(b"I am committed"),
+			signature: Keyring::<AuthorityId>::Alice.sign(b"I am committed"),
 		};
 		// add 1st good vote
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok);
@@ -310,26 +315,26 @@ mod tests {
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok);
 
 		vote.id = Keyring::Dave.public();
-		vote.signature = Keyring::Dave.sign(b"I am committed");
+		vote.signature = Keyring::<AuthorityId>::Dave.sign(b"I am committed");
 		// invalid vote (Dave is not a validator)
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Invalid);
 
 		vote.id = Keyring::Bob.public();
-		vote.signature = Keyring::Bob.sign(b"I am committed");
+		vote.signature = Keyring::<AuthorityId>::Bob.sign(b"I am committed");
 		// add 2nd good vote
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Ok);
 
 		vote.id = Keyring::Charlie.public();
-		vote.signature = Keyring::Charlie.sign(b"I am committed");
+		vote.signature = Keyring::<AuthorityId>::Charlie.sign(b"I am committed");
 		// add 3rd good vote -> round concluded -> signatures present
 		assert_eq!(
 			rounds.add_vote(vote.clone()),
 			VoteImportResult::RoundConcluded(SignedCommitment {
 				commitment,
 				signatures: vec![
-					Some(Keyring::Alice.sign(b"I am committed")),
-					Some(Keyring::Bob.sign(b"I am committed")),
-					Some(Keyring::Charlie.sign(b"I am committed")),
+					Some(Keyring::<AuthorityId>::Alice.sign(b"I am committed")),
+					Some(Keyring::<AuthorityId>::Bob.sign(b"I am committed")),
+					Some(Keyring::<AuthorityId>::Charlie.sign(b"I am committed")),
 					None,
 				]
 			})
@@ -337,7 +342,7 @@ mod tests {
 		rounds.conclude(block_number);
 
 		vote.id = Keyring::Eve.public();
-		vote.signature = Keyring::Eve.sign(b"I am committed");
+		vote.signature = Keyring::<AuthorityId>::Eve.sign(b"I am committed");
 		// Eve is a validator, but round was concluded, adding vote disallowed
 		assert_eq!(rounds.add_vote(vote), VoteImportResult::Stale);
 	}
@@ -364,7 +369,7 @@ mod tests {
 		let mut vote = VoteMessage {
 			id: Keyring::Alice.public(),
 			commitment,
-			signature: Keyring::Alice.sign(b"I am committed"),
+			signature: Keyring::<AuthorityId>::Alice.sign(b"I am committed"),
 		};
 		// add vote for previous session, should fail
 		assert_eq!(rounds.add_vote(vote.clone()), VoteImportResult::Stale);
@@ -407,22 +412,22 @@ mod tests {
 		let mut alice_vote = VoteMessage {
 			id: Keyring::Alice.public(),
 			commitment: commitment.clone(),
-			signature: Keyring::Alice.sign(b"I am committed"),
+			signature: Keyring::<AuthorityId>::Alice.sign(b"I am committed"),
 		};
 		let mut bob_vote = VoteMessage {
 			id: Keyring::Bob.public(),
 			commitment: commitment.clone(),
-			signature: Keyring::Bob.sign(b"I am committed"),
+			signature: Keyring::<AuthorityId>::Bob.sign(b"I am committed"),
 		};
 		let mut charlie_vote = VoteMessage {
 			id: Keyring::Charlie.public(),
 			commitment,
-			signature: Keyring::Charlie.sign(b"I am committed"),
+			signature: Keyring::<AuthorityId>::Charlie.sign(b"I am committed"),
 		};
 		let expected_signatures = vec![
-			Some(Keyring::Alice.sign(b"I am committed")),
-			Some(Keyring::Bob.sign(b"I am committed")),
-			Some(Keyring::Charlie.sign(b"I am committed")),
+			Some(Keyring::<AuthorityId>::Alice.sign(b"I am committed")),
+			Some(Keyring::<AuthorityId>::Bob.sign(b"I am committed")),
+			Some(Keyring::<AuthorityId>::Charlie.sign(b"I am committed")),
 		];
 
 		// round 1 - only 2 out of 3 vote
@@ -484,7 +489,7 @@ mod tests {
 		let alice_vote1 = VoteMessage {
 			id: Keyring::Alice.public(),
 			commitment: commitment1,
-			signature: Keyring::Alice.sign(b"I am committed"),
+			signature: Keyring::<AuthorityId>::Alice.sign(b"I am committed"),
 		};
 		let mut alice_vote2 = alice_vote1.clone();
 		alice_vote2.commitment = commitment2;
diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs
index 7e61e877c1ddac692c6ae5f7199cf5ff35752ff7..0e6ff210b158c18334a749992d096e2d54c77a01 100644
--- a/substrate/client/consensus/beefy/src/tests.rs
+++ b/substrate/client/consensus/beefy/src/tests.rs
@@ -57,9 +57,10 @@ use sp_consensus_beefy::{
 	ecdsa_crypto::{AuthorityId, Signature},
 	known_payloads,
 	mmr::{find_mmr_root_digest, MmrRootProvider},
-	BeefyApi, Commitment, ConsensusLog, EquivocationProof, Keyring as BeefyKeyring, MmrRootHash,
-	OpaqueKeyOwnershipProof, Payload, SignedCommitment, ValidatorSet, ValidatorSetId,
-	VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID,
+	test_utils::Keyring as BeefyKeyring,
+	BeefyApi, Commitment, ConsensusLog, EquivocationProof, MmrRootHash, OpaqueKeyOwnershipProof,
+	Payload, SignedCommitment, ValidatorSet, ValidatorSetId, VersionedFinalityProof, VoteMessage,
+	BEEFY_ENGINE_ID,
 };
 use sp_core::H256;
 use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr};
@@ -349,11 +350,11 @@ fn add_auth_change_digest(builder: &mut impl BlockBuilderExt, new_auth_set: Beef
 		.unwrap();
 }
 
-pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring]) -> Vec<AuthorityId> {
-	keys.iter().map(|&key| key.public().into()).collect()
+pub(crate) fn make_beefy_ids(keys: &[BeefyKeyring<AuthorityId>]) -> Vec<AuthorityId> {
+	keys.iter().map(|key| key.public().into()).collect()
 }
 
-pub(crate) fn create_beefy_keystore(authority: BeefyKeyring) -> KeystorePtr {
+pub(crate) fn create_beefy_keystore(authority: &BeefyKeyring<AuthorityId>) -> KeystorePtr {
 	let keystore = MemoryKeystore::new();
 	keystore
 		.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&authority.to_seed()))
@@ -393,7 +394,7 @@ async fn voter_init_setup(
 // Spawns beefy voters. Returns a future to spawn on the runtime.
 fn initialize_beefy<API>(
 	net: &mut BeefyTestNet,
-	peers: Vec<(usize, &BeefyKeyring, Arc<API>)>,
+	peers: Vec<(usize, &BeefyKeyring<AuthorityId>, Arc<API>)>,
 	min_block_delta: u32,
 ) -> impl Future<Output = ()>
 where
@@ -413,7 +414,7 @@ where
 	for (peer_id, key, api) in peers.into_iter() {
 		let peer = &net.peers[peer_id];
 
-		let keystore = create_beefy_keystore(*key);
+		let keystore = create_beefy_keystore(key);
 
 		let (_, _, peer_data) = net.make_block_import(peer.client().clone());
 		let PeerData { beefy_rpc_links, beefy_voter_links, .. } = peer_data;
@@ -471,7 +472,7 @@ async fn run_for(duration: Duration, net: &Arc<Mutex<BeefyTestNet>>) {
 pub(crate) fn get_beefy_streams(
 	net: &mut BeefyTestNet,
 	// peer index and key
-	peers: impl Iterator<Item = (usize, BeefyKeyring)>,
+	peers: impl Iterator<Item = (usize, BeefyKeyring<AuthorityId>)>,
 ) -> (Vec<NotificationReceiver<H256>>, Vec<NotificationReceiver<BeefyVersionedFinalityProof<Block>>>)
 {
 	let mut best_block_streams = Vec::new();
@@ -569,7 +570,7 @@ async fn streams_empty_after_timeout<T>(
 async fn finalize_block_and_wait_for_beefy(
 	net: &Arc<Mutex<BeefyTestNet>>,
 	// peer index and key
-	peers: impl Iterator<Item = (usize, BeefyKeyring)> + Clone,
+	peers: impl Iterator<Item = (usize, BeefyKeyring<AuthorityId>)> + Clone,
 	finalize_target: &H256,
 	expected_beefy: &[u64],
 ) {
diff --git a/substrate/client/consensus/beefy/src/worker.rs b/substrate/client/consensus/beefy/src/worker.rs
index e67e3e0f76ad996c4666142bf28fead1aed200b6..19a24b9bc1a548936483537f7ea057cba1592d8c 100644
--- a/substrate/client/consensus/beefy/src/worker.rs
+++ b/substrate/client/consensus/beefy/src/worker.rs
@@ -26,7 +26,7 @@ use crate::{
 	error::Error,
 	expect_validator_set,
 	justification::BeefyVersionedFinalityProof,
-	keystore::{BeefyKeystore, BeefySignatureHasher},
+	keystore::BeefyKeystore,
 	metric_inc, metric_set,
 	metrics::VoterMetrics,
 	round::{Rounds, VoteImportResult},
@@ -45,8 +45,8 @@ use sp_consensus::SyncOracle;
 use sp_consensus_beefy::{
 	check_equivocation_proof,
 	ecdsa_crypto::{AuthorityId, Signature},
-	BeefyApi, Commitment, ConsensusLog, EquivocationProof, PayloadProvider, ValidatorSet,
-	VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID,
+	BeefyApi, BeefySignatureHasher, Commitment, ConsensusLog, EquivocationProof, PayloadProvider,
+	ValidatorSet, VersionedFinalityProof, VoteMessage, BEEFY_ENGINE_ID,
 };
 use sp_runtime::{
 	generic::{BlockId, OpaqueDigestItemId},
@@ -340,7 +340,7 @@ pub(crate) struct BeefyWorkerBase<B: Block, BE, RuntimeApi> {
 	// utilities
 	pub backend: Arc<BE>,
 	pub runtime: Arc<RuntimeApi>,
-	pub key_store: BeefyKeystore,
+	pub key_store: BeefyKeystore<AuthorityId>,
 
 	/// BEEFY client metrics.
 	pub metrics: Option<VoterMetrics>,
@@ -1278,8 +1278,11 @@ pub(crate) mod tests {
 	use sc_network_test::TestNetFactory;
 	use sp_blockchain::Backend as BlockchainBackendT;
 	use sp_consensus_beefy::{
-		generate_equivocation_proof, known_payloads, known_payloads::MMR_ROOT_ID,
-		mmr::MmrRootProvider, Keyring, Payload, SignedCommitment,
+		known_payloads,
+		known_payloads::MMR_ROOT_ID,
+		mmr::MmrRootProvider,
+		test_utils::{generate_equivocation_proof, Keyring},
+		Payload, SignedCommitment,
 	};
 	use sp_runtime::traits::{Header as HeaderT, One};
 	use substrate_test_runtime_client::{
@@ -1309,7 +1312,7 @@ pub(crate) mod tests {
 
 	fn create_beefy_worker(
 		peer: &mut BeefyPeer,
-		key: &Keyring,
+		key: &Keyring<AuthorityId>,
 		min_block_delta: u32,
 		genesis_validator_set: ValidatorSet<AuthorityId>,
 	) -> BeefyWorker<
@@ -1319,7 +1322,7 @@ pub(crate) mod tests {
 		TestApi,
 		Arc<SyncingService<Block>>,
 	> {
-		let keystore = create_beefy_keystore(*key);
+		let keystore = create_beefy_keystore(key);
 
 		let (to_rpc_justif_sender, from_voter_justif_stream) =
 			BeefyVersionedFinalityProofStream::<Block>::channel();
diff --git a/substrate/client/keystore/src/local.rs b/substrate/client/keystore/src/local.rs
index 3b29f435e2a942ffe96dd02a8009af4d33693995..ca4a87ef383e810bab522c41a33f8102316fdc45 100644
--- a/substrate/client/keystore/src/local.rs
+++ b/substrate/client/keystore/src/local.rs
@@ -37,7 +37,7 @@ use sp_core::bandersnatch;
 }
 
 sp_keystore::bls_experimental_enabled! {
-use sp_core::{bls377, bls381, ecdsa_bls377};
+use sp_core::{bls377, bls381, ecdsa_bls377, KeccakHasher};
 }
 
 use crate::{Error, Result};
@@ -391,6 +391,20 @@ impl Keystore for LocalKeystore {
 			self.sign::<ecdsa_bls377::Pair>(key_type, public, msg)
 		}
 
+			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));
+			Ok(sig)
+		}
+
+
 	}
 }
 
diff --git a/substrate/frame/beefy/src/tests.rs b/substrate/frame/beefy/src/tests.rs
index bf5ae19510ce9320c7aac3b705db605db16ecebd..453cf19a4fe156a76ed13a7da4fe59d95d4260f0 100644
--- a/substrate/frame/beefy/src/tests.rs
+++ b/substrate/frame/beefy/src/tests.rs
@@ -15,21 +15,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use std::vec;
-
 use codec::Encode;
-use sp_consensus_beefy::{
-	check_equivocation_proof, generate_equivocation_proof, known_payloads::MMR_ROOT_ID,
-	Keyring as BeefyKeyring, Payload, ValidatorSet, KEY_TYPE as BEEFY_KEY_TYPE,
-};
-
-use sp_runtime::DigestItem;
+use std::vec;
 
 use frame_support::{
 	assert_err, assert_ok,
 	dispatch::{GetDispatchInfo, Pays},
 	traits::{Currency, KeyOwnerProofSystem, OnInitialize},
 };
+use sp_consensus_beefy::{
+	check_equivocation_proof,
+	known_payloads::MMR_ROOT_ID,
+	test_utils::{generate_equivocation_proof, Keyring as BeefyKeyring},
+	Payload, ValidatorSet, KEY_TYPE as BEEFY_KEY_TYPE,
+};
+use sp_runtime::DigestItem;
 
 use crate::{mock::*, Call, Config, Error, Weight, WeightInfo};
 
diff --git a/substrate/primitives/consensus/beefy/Cargo.toml b/substrate/primitives/consensus/beefy/Cargo.toml
index 6232eca5ff89b5e979d1a098206607bf2a55ef0d..9fd9ce5d123b81f5bb016e78dceadacc39fa763c 100644
--- a/substrate/primitives/consensus/beefy/Cargo.toml
+++ b/substrate/primitives/consensus/beefy/Cargo.toml
@@ -25,6 +25,7 @@ sp-crypto-hashing = { path = "../../crypto/hashing", default-features = false }
 sp-io = { path = "../../io", default-features = false }
 sp-mmr-primitives = { path = "../../merkle-mountain-range", default-features = false }
 sp-runtime = { path = "../../runtime", default-features = false }
+sp-keystore = { path = "../../keystore", default-features = false }
 sp-std = { path = "../../std", default-features = false }
 strum = { version = "0.24.1", features = ["derive"], default-features = false }
 lazy_static = { version = "1.4.0", optional = true }
@@ -45,6 +46,7 @@ std = [
 	"sp-core/std",
 	"sp-crypto-hashing/std",
 	"sp-io/std",
+	"sp-keystore/std",
 	"sp-mmr-primitives/std",
 	"sp-runtime/std",
 	"sp-std/std",
diff --git a/substrate/primitives/consensus/beefy/src/commitment.rs b/substrate/primitives/consensus/beefy/src/commitment.rs
index 37be1a4f6fc3e20977e6ee1beb90cf2cf8e67874..1f0fb34ebf10b3e357429bf9bb501677dbea4695 100644
--- a/substrate/primitives/consensus/beefy/src/commitment.rs
+++ b/substrate/primitives/consensus/beefy/src/commitment.rs
@@ -398,7 +398,7 @@ mod tests {
 		assert_eq!(
 			encoded,
 			array_bytes::hex2bytes_unchecked(
-				"046d68343048656c6c6f20576f726c642105000000000000000000000000000000000000000000000004300400000008558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba01667603fc041cf9d7147d22bf54b15e5778893d6986b71a929747befd3b4d233fbe668bc480e8865116b94db46ca25a01e03c71955f2582604e415da68f2c3c406b9d5f4ad416230ec5453f05ac16a50d8d0923dfb0413cc956ae3fa6334465bd1f2cacec8e9cd606438390fe2a29dc052d6e1f8105c337a86cdd9aaacdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6a0046395a71681be3d0c2a00df61d3b2be0963eb6caa243cc505d327aec73e1bb7ffe9a14b1354b0c406792ac6d6f47c06987c15dec9993f43eefa001d866fe0850d986702c414840f0d9ec0fdc04832ef91ae37c8d49e2f573ca50cb37f152801d489a19395cb04e5fc8f2ab6954b58a3bcc40ef9b6409d2ff7ef07"
+				"046d68343048656c6c6f20576f726c642105000000000000000000000000000000000000000000000004300400000008558455ad81279df0795cc985580e4fb75d72d948d1107b2ac80a09abed4da8480c746cc321f2319a5e99a830e314d10dd3cd68ce3dc0c33c86e99bcb7816f9ba015dd1c9b2237e54baa93d232cdf83a430b58a5efbc2f86ca1bab173a315ff6f15bef161425750c028055e9a23947b73002889a8b22168628438875a8ef25d76db998a80187b50719471286f054f3b3809b77a0cd87d7fe9c1a9d5d562683e25a70610f0804e92340549a43a7159b77b0c2d6e1f8105c337a86cdd9aaacdc496577f3db8c55ef9e6fd48f2c5c05a2274707491635d8ba3df64f324575b7b2a34487bca2324b6a0046395a71681be3d0c2a001074884b6998c82331bd57ffa0a02cbfd02483c765b9216eab6a1fc119206236bf7971be68acaebff7400edee943240006a6096c9cfa65e9eb4e67f025c27112d14b4574fb208c439500f45cf3a8060f6cf009044f3141cce0364a7c2710a19b1bdf4abf27f86e5e3db08bddd35a7d12"
 			)
 		);
 	}
diff --git a/substrate/primitives/consensus/beefy/src/lib.rs b/substrate/primitives/consensus/beefy/src/lib.rs
index 68eeeb3c680071dff08aa0aac9de3bf6e62c140e..1c3801e3a506bc9c04976713e1fae9cf02511a3d 100644
--- a/substrate/primitives/consensus/beefy/src/lib.rs
+++ b/substrate/primitives/consensus/beefy/src/lib.rs
@@ -32,20 +32,22 @@
 //! while GRANDPA uses `ed25519`.
 
 mod commitment;
-pub mod mmr;
 mod payload;
-#[cfg(feature = "std")]
-mod test_utils;
+
+pub mod mmr;
 pub mod witness;
 
+/// Test utilities
+#[cfg(feature = "std")]
+pub mod test_utils;
+
 pub use commitment::{Commitment, SignedCommitment, VersionedFinalityProof};
 pub use payload::{known_payloads, BeefyPayloadId, Payload, PayloadProvider};
-#[cfg(feature = "std")]
-pub use test_utils::*;
 
 use codec::{Codec, Decode, Encode};
+use core::fmt::{Debug, Display};
 use scale_info::TypeInfo;
-use sp_application_crypto::RuntimeAppPublic;
+use sp_application_crypto::{AppCrypto, AppPublic, ByteArray, RuntimeAppPublic};
 use sp_core::H256;
 use sp_runtime::traits::{Hash, Keccak256, NumberFor};
 use sp_std::prelude::*;
@@ -63,6 +65,25 @@ pub trait BeefyAuthorityId<MsgHash: Hash>: RuntimeAppPublic {
 	fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool;
 }
 
+/// Hasher used for BEEFY signatures.
+pub type BeefySignatureHasher = sp_runtime::traits::Keccak256;
+
+/// A trait bound which lists all traits which are required to be implemented by
+/// a BEEFY AuthorityId type in order to be able to be used in BEEFY Keystore
+pub trait AuthorityIdBound:
+	Codec
+	+ Debug
+	+ Clone
+	+ AsRef<[u8]>
+	+ ByteArray
+	+ AppPublic
+	+ AppCrypto
+	+ RuntimeAppPublic
+	+ Display
+	+ BeefyAuthorityId<BeefySignatureHasher>
+{
+}
+
 /// BEEFY cryptographic types for ECDSA crypto
 ///
 /// This module basically introduces four crypto types:
@@ -74,7 +95,7 @@ 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};
+	use super::{AuthorityIdBound, BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
 	use sp_application_crypto::{app_crypto, ecdsa};
 	use sp_core::crypto::Wraps;
 
@@ -101,6 +122,7 @@ pub mod ecdsa_crypto {
 			}
 		}
 	}
+	impl AuthorityIdBound for AuthorityId {}
 }
 
 /// BEEFY cryptographic types for BLS crypto
@@ -116,7 +138,7 @@ pub mod ecdsa_crypto {
 
 #[cfg(feature = "bls-experimental")]
 pub mod bls_crypto {
-	use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
+	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 _};
 
@@ -134,13 +156,14 @@ pub mod bls_crypto {
 	{
 		fn verify(&self, signature: &<Self as RuntimeAppPublic>::Signature, msg: &[u8]) -> bool {
 			// `w3f-bls` library uses IETF hashing standard and as such does not expose
-			// a choice of hash to field function.
+			// 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
 
 			BlsPair::verify(signature.as_inner_ref(), msg, self.as_inner_ref())
 		}
 	}
+	impl AuthorityIdBound for AuthorityId {}
 }
 
 /// BEEFY cryptographic types for (ECDSA,BLS) crypto pair
@@ -155,7 +178,7 @@ pub mod bls_crypto {
 /// functionality.
 #[cfg(feature = "bls-experimental")]
 pub mod ecdsa_bls_crypto {
-	use super::{BeefyAuthorityId, Hash, RuntimeAppPublic, KEY_TYPE};
+	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};
 
@@ -187,6 +210,8 @@ pub mod ecdsa_bls_crypto {
 			)
 		}
 	}
+
+	impl AuthorityIdBound for AuthorityId {}
 }
 
 /// The `ConsensusEngineId` of BEEFY.
diff --git a/substrate/primitives/consensus/beefy/src/test_utils.rs b/substrate/primitives/consensus/beefy/src/test_utils.rs
index a6e65e5bff0b0d6bc75d93ea3d5fc7b49741287f..ec13c9c690046ae795fe50af3bfc775721dfb6ee 100644
--- a/substrate/primitives/consensus/beefy/src/test_utils.rs
+++ b/substrate/primitives/consensus/beefy/src/test_utils.rs
@@ -15,18 +15,24 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#![cfg(feature = "std")]
+#[cfg(feature = "bls-experimental")]
+use crate::ecdsa_bls_crypto;
+use crate::{
+	ecdsa_crypto, AuthorityIdBound, BeefySignatureHasher, Commitment, EquivocationProof, Payload,
+	ValidatorSetId, VoteMessage,
+};
+use sp_application_crypto::{AppCrypto, AppPair, RuntimeAppPublic, Wraps};
+use sp_core::{ecdsa, Pair};
+use sp_runtime::traits::Hash;
 
-use crate::{ecdsa_crypto, Commitment, EquivocationProof, Payload, ValidatorSetId, VoteMessage};
 use codec::Encode;
-use sp_core::{ecdsa, Pair};
-use std::collections::HashMap;
+use std::{collections::HashMap, marker::PhantomData};
 use strum::IntoEnumIterator;
 
 /// Set of test accounts using [`crate::ecdsa_crypto`] types.
 #[allow(missing_docs)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)]
-pub enum Keyring {
+pub enum Keyring<AuthorityId> {
 	Alice,
 	Bob,
 	Charlie,
@@ -35,71 +41,110 @@ pub enum Keyring {
 	Ferdie,
 	One,
 	Two,
+	_Marker(PhantomData<AuthorityId>),
+}
+
+/// Trait representing BEEFY specific generation and signing behavior of authority id
+///
+/// Accepts custom hashing fn for the message and custom convertor fn for the signer.
+pub trait BeefySignerAuthority<MsgHash: Hash>: AppPair {
+	/// Generate and return signature for `message` using custom hashing `MsgHash`
+	fn sign_with_hasher(&self, message: &[u8]) -> <Self as AppCrypto>::Signature;
+}
+
+impl<MsgHash> BeefySignerAuthority<MsgHash> for <ecdsa_crypto::AuthorityId as AppCrypto>::Pair
+where
+	MsgHash: Hash,
+	<MsgHash as Hash>::Output: Into<[u8; 32]>,
+{
+	fn sign_with_hasher(&self, message: &[u8]) -> <Self as AppCrypto>::Signature {
+		let hashed_message = <MsgHash as Hash>::hash(message).into();
+		self.as_inner_ref().sign_prehashed(&hashed_message).into()
+	}
+}
+
+#[cfg(feature = "bls-experimental")]
+impl<MsgHash> BeefySignerAuthority<MsgHash> for <ecdsa_bls_crypto::AuthorityId as AppCrypto>::Pair
+where
+	MsgHash: Hash,
+	<MsgHash as Hash>::Output: Into<[u8; 32]>,
+{
+	fn sign_with_hasher(&self, message: &[u8]) -> <Self as AppCrypto>::Signature {
+		self.as_inner_ref().sign_with_hasher::<MsgHash>(&message).into()
+	}
 }
 
-impl Keyring {
+/// Implement Keyring functionalities generically over AuthorityId
+impl<AuthorityId> Keyring<AuthorityId>
+where
+	AuthorityId: AuthorityIdBound + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Public>,
+	<AuthorityId as AppCrypto>::Pair: BeefySignerAuthority<BeefySignatureHasher>,
+	<AuthorityId as RuntimeAppPublic>::Signature:
+		Send + Sync + From<<<AuthorityId as AppCrypto>::Pair as AppCrypto>::Signature>,
+{
 	/// Sign `msg`.
-	pub fn sign(self, msg: &[u8]) -> ecdsa_crypto::Signature {
-		// todo: use custom signature hashing type
-		let msg = sp_crypto_hashing::keccak_256(msg);
-		ecdsa::Pair::from(self).sign_prehashed(&msg).into()
+	pub fn sign(&self, msg: &[u8]) -> <AuthorityId as RuntimeAppPublic>::Signature {
+		let key_pair: <AuthorityId as AppCrypto>::Pair = self.pair();
+		key_pair.sign_with_hasher(&msg).into()
 	}
 
 	/// Return key pair.
-	pub fn pair(self) -> ecdsa_crypto::Pair {
-		ecdsa::Pair::from_string(self.to_seed().as_str(), None).unwrap().into()
+	pub fn pair(&self) -> <AuthorityId as AppCrypto>::Pair {
+		<AuthorityId as AppCrypto>::Pair::from_string(self.to_seed().as_str(), None)
+			.unwrap()
+			.into()
 	}
 
 	/// Return public key.
-	pub fn public(self) -> ecdsa_crypto::Public {
-		self.pair().public()
+	pub fn public(&self) -> AuthorityId {
+		self.pair().public().into()
 	}
 
 	/// Return seed string.
-	pub fn to_seed(self) -> String {
+	pub fn to_seed(&self) -> String {
 		format!("//{}", self)
 	}
 
 	/// Get Keyring from public key.
-	pub fn from_public(who: &ecdsa_crypto::Public) -> Option<Keyring> {
-		Self::iter().find(|&k| &ecdsa_crypto::Public::from(k) == who)
+	pub fn from_public(who: &AuthorityId) -> Option<Keyring<AuthorityId>> {
+		Self::iter().find(|k| k.public() == *who)
 	}
 }
 
 lazy_static::lazy_static! {
-	static ref PRIVATE_KEYS: HashMap<Keyring, ecdsa_crypto::Pair> =
-		Keyring::iter().map(|i| (i, i.pair())).collect();
-	static ref PUBLIC_KEYS: HashMap<Keyring, ecdsa_crypto::Public> =
-		PRIVATE_KEYS.iter().map(|(&name, pair)| (name, pair.public())).collect();
+	static ref PRIVATE_KEYS: HashMap<Keyring<ecdsa_crypto::AuthorityId>, ecdsa_crypto::Pair> =
+		Keyring::iter().map(|i| (i.clone(), i.pair())).collect();
+	static ref PUBLIC_KEYS: HashMap<Keyring<ecdsa_crypto::AuthorityId>, ecdsa_crypto::Public> =
+		PRIVATE_KEYS.iter().map(|(name, pair)| (name.clone(), sp_application_crypto::Pair::public(pair))).collect();
 }
 
-impl From<Keyring> for ecdsa_crypto::Pair {
-	fn from(k: Keyring) -> Self {
+impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa_crypto::Pair {
+	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
 		k.pair()
 	}
 }
 
-impl From<Keyring> for ecdsa::Pair {
-	fn from(k: Keyring) -> Self {
+impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa::Pair {
+	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
 		k.pair().into()
 	}
 }
 
-impl From<Keyring> for ecdsa_crypto::Public {
-	fn from(k: Keyring) -> Self {
+impl From<Keyring<ecdsa_crypto::AuthorityId>> for ecdsa_crypto::Public {
+	fn from(k: Keyring<ecdsa_crypto::AuthorityId>) -> Self {
 		(*PUBLIC_KEYS).get(&k).cloned().unwrap()
 	}
 }
 
 /// Create a new `EquivocationProof` based on given arguments.
 pub fn generate_equivocation_proof(
-	vote1: (u64, Payload, ValidatorSetId, &Keyring),
-	vote2: (u64, Payload, ValidatorSetId, &Keyring),
+	vote1: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
+	vote2: (u64, Payload, ValidatorSetId, &Keyring<ecdsa_crypto::AuthorityId>),
 ) -> EquivocationProof<u64, ecdsa_crypto::Public, ecdsa_crypto::Signature> {
 	let signed_vote = |block_number: u64,
 	                   payload: Payload,
 	                   validator_set_id: ValidatorSetId,
-	                   keyring: &Keyring| {
+	                   keyring: &Keyring<ecdsa_crypto::AuthorityId>| {
 		let commitment = Commitment { validator_set_id, block_number, payload };
 		let signature = keyring.sign(&commitment.encode());
 		VoteMessage { commitment, id: keyring.public(), signature }
diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs
index 96b0ff19e561a41a43584142e62f8bc6d8e886b1..61e7162544a602ba2c5577807cfbc94d143dad22 100644
--- a/substrate/primitives/core/src/bandersnatch.rs
+++ b/substrate/primitives/core/src/bandersnatch.rs
@@ -985,6 +985,19 @@ mod tests {
 		assert!(res.is_err());
 	}
 
+	#[test]
+	fn generate_with_phrase_should_be_recoverable_with_from_string() {
+		let (pair, phrase, seed) = Pair::generate_with_phrase(None);
+		let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_seed.public());
+		let (repair_phrase, reseed) =
+			Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid");
+		assert_eq!(seed, reseed);
+		assert_eq!(pair.public(), repair_phrase.public());
+		let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_string.public());
+	}
+
 	#[test]
 	fn sign_verify() {
 		let pair = Pair::from_seed(DEV_SEED);
diff --git a/substrate/primitives/core/src/bls.rs b/substrate/primitives/core/src/bls.rs
index 452c6372d16b3b2dccf60ac0e7bdde01f24f8cc1..0c84d0ba8e6c0fd295690ca5fddf48440f083379 100644
--- a/substrate/primitives/core/src/bls.rs
+++ b/substrate/primitives/core/src/bls.rs
@@ -15,7 +15,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Simple BLS (Boneh–Lynn–Shacham) Signature API.
+//! BLS (Boneh–Lynn–Shacham) Signature along with efficiently verifiable Chaum-Pedersen proof API.
+//! Signatures are implemented according to
+//! [Efficient Aggregatable BLS Signatures with Chaum-Pedersen Proofs](https://eprint.iacr.org/2022/1611)
+//! Hash-to-BLS-curve is using Simplified SWU for AB == 0
+//! [RFC 9380](https://datatracker.ietf.org/doc/rfc9380/) Sect 6.6.3.
+//! Chaum-Pedersen proof uses the same hash-to-field specified in RFC 9380 for the field of the BLS
+//! curve.
 
 #[cfg(feature = "serde")]
 use crate::crypto::Ss58Codec;
@@ -452,11 +458,12 @@ impl<T: BlsBound> TraitPair for Pair<T> {
 	fn derive<Iter: Iterator<Item = DeriveJunction>>(
 		&self,
 		path: Iter,
-		_seed: Option<Seed>,
+		seed: Option<Seed>,
 	) -> Result<(Self, Option<Seed>), DeriveError> {
-		let mut acc: [u8; SECRET_KEY_SERIALIZED_SIZE] = self.0.secret.to_bytes().try_into().expect(
-			"Secret key serializer returns a vector of SECRET_KEY_SERIALIZED_SIZE size; qed",
-		);
+		let mut acc: [u8; SECRET_KEY_SERIALIZED_SIZE] =
+			seed.unwrap_or(self.0.secret.to_bytes().try_into().expect(
+				"Secret key serializer returns a vector of SECRET_KEY_SERIALIZED_SIZE size; qed",
+			));
 		for j in path {
 			match j {
 				DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath),
@@ -544,7 +551,7 @@ mod test {
 			"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
 		);
 		let pair = Pair::from_seed(&seed);
-		// we are using hash to field so this is not going to work
+		// we are using hash-to-field so this is not going to work
 		// assert_eq!(pair.seed(), seed);
 		let path = vec![DeriveJunction::Hard([0u8; 32])];
 		let derived = pair.derive(path.into_iter(), None).ok().unwrap().0;
@@ -588,12 +595,12 @@ mod test {
 		assert_eq!(
 			public,
 			Public::unchecked_from(array_bytes::hex2array_unchecked(
-				"6dc6be608fab3c6bd894a606be86db346cc170db85c733853a371f3db54ae1b12052c0888d472760c81b537572a26f00db865e5963aef8634f9917571c51b538b564b2a9ceda938c8b930969ee3b832448e08e33a79e9ddd28af419a3ce45300f5dbc768b067781f44f3fe05a19e6b07b1c4196151ec3f8ea37e4f89a8963030d2101e931276bb9ebe1f20102239d780"
+				"7a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400"
 			))
 		);
 		let message = b"";
 		let signature =
-	array_bytes::hex2array_unchecked("bbb395bbdee1a35930912034f5fde3b36df2835a0536c865501b0675776a1d5931a3bea2e66eff73b2546c6af2061a8019223e4ebbbed661b2538e0f5823f2c708eb89c406beca8fcb53a5c13dbc7c0c42e4cf2be2942bba96ea29297915a06bd2b1b979c0e2ac8fd4ec684a6b5d110c"
+	array_bytes::hex2array_unchecked("d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b"
 	);
 		let expected_signature = Signature::unchecked_from(signature);
 		println!("signature is {:?}", pair.sign(&message[..]));
@@ -647,12 +654,30 @@ mod test {
 		assert_eq!(pair1.public(), pair2.public());
 	}
 
+	#[test]
+	fn generate_with_phrase_should_be_recoverable_with_from_string() {
+		let (pair, phrase, seed) = Pair::generate_with_phrase(None);
+		let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_seed.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+		let (repair_phrase, reseed) =
+			Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid");
+		assert_eq!(seed, reseed);
+		assert_eq!(pair.public(), repair_phrase.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+
+		let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_string.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+	}
+
 	#[test]
 	fn password_does_something() {
 		let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password"));
 		let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
 
 		assert_ne!(pair1.public(), pair2.public());
+		assert_ne!(pair1.to_raw_vec(), pair2.to_raw_vec());
 	}
 
 	#[test]
diff --git a/substrate/primitives/core/src/ecdsa.rs b/substrate/primitives/core/src/ecdsa.rs
index 1b63db7af7f2b73f41766fc31061e7333af922a2..f172b3a7d02c7a1adb1ff2fc37933d3cf47a165f 100644
--- a/substrate/primitives/core/src/ecdsa.rs
+++ b/substrate/primitives/core/src/ecdsa.rs
@@ -644,12 +644,29 @@ mod test {
 		assert_eq!(pair1.public(), pair2.public());
 	}
 
+	#[test]
+	fn generate_with_phrase_should_be_recoverable_with_from_string() {
+		let (pair, phrase, seed) = Pair::generate_with_phrase(None);
+		let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_seed.public());
+		assert_eq!(pair.secret, repair_seed.secret);
+		let (repair_phrase, reseed) =
+			Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid");
+		assert_eq!(seed, reseed);
+		assert_eq!(pair.public(), repair_phrase.public());
+		assert_eq!(pair.secret, repair_phrase.secret);
+		let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_string.public());
+		assert_eq!(pair.secret, repair_string.secret);
+	}
+
 	#[test]
 	fn password_does_something() {
 		let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password"));
 		let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
 
 		assert_ne!(pair1.public(), pair2.public());
+		assert_ne!(pair1.secret, pair2.secret);
 	}
 
 	#[test]
diff --git a/substrate/primitives/core/src/ed25519.rs b/substrate/primitives/core/src/ed25519.rs
index aa0d77510bd81efdd9186e730842622f65620558..60ebd93e12d43d6d331c6758508f843145031e74 100644
--- a/substrate/primitives/core/src/ed25519.rs
+++ b/substrate/primitives/core/src/ed25519.rs
@@ -501,6 +501,22 @@ mod test {
 		);
 	}
 
+	#[test]
+	fn generate_with_phrase_should_be_recoverable_with_from_string() {
+		let (pair, phrase, seed) = Pair::generate_with_phrase(None);
+		let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_seed.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+		let (repair_phrase, reseed) =
+			Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid");
+		assert_eq!(seed, reseed);
+		assert_eq!(pair.public(), repair_phrase.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+		let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_string.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+	}
+
 	#[test]
 	fn test_vector_should_work() {
 		let pair = Pair::from_seed(&array_bytes::hex2array_unchecked(
@@ -590,6 +606,7 @@ mod test {
 		let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
 
 		assert_ne!(pair1.public(), pair2.public());
+		assert_ne!(pair1.to_raw_vec(), pair2.to_raw_vec());
 	}
 
 	#[test]
diff --git a/substrate/primitives/core/src/paired_crypto.rs b/substrate/primitives/core/src/paired_crypto.rs
index 960b8469249e22b10352545f26d3940df3da2e77..20b32c339bd763d4847f4d36626635a4226f80d9 100644
--- a/substrate/primitives/core/src/paired_crypto.rs
+++ b/substrate/primitives/core/src/paired_crypto.rs
@@ -460,10 +460,11 @@ where
 		path: Iter,
 		seed: Option<Self::Seed>,
 	) -> Result<(Self, Option<Self::Seed>), DeriveError> {
-		let path: Vec<_> = path.collect();
+		let left_path: Vec<_> = path.collect();
+		let right_path: Vec<_> = left_path.clone();
 
-		let left = self.left.derive(path.iter().cloned(), seed.map(|s| s.into()))?;
-		let right = self.right.derive(path.into_iter(), seed.map(|s| s.into()))?;
+		let left = self.left.derive(left_path.into_iter(), seed.map(|s| s.into()))?;
+		let right = self.right.derive(right_path.into_iter(), seed.map(|s| s.into()))?;
 
 		let seed = match (left.1, right.1) {
 			(Some(l), Some(r)) if l.as_ref() == r.as_ref() => Some(l.into()),
@@ -542,13 +543,30 @@ mod test {
 		);
 	}
 
+	#[test]
+	fn generate_with_phrase_should_be_recoverable_with_from_string() {
+		let (pair, phrase, seed) = Pair::generate_with_phrase(None);
+		let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_seed.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+
+		let (repair_phrase, reseed) =
+			Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid");
+		assert_eq!(seed, reseed);
+		assert_eq!(pair.public(), repair_phrase.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+		let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_string.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+	}
+
 	#[test]
 	fn seed_and_derive_should_work() {
 		let seed_for_right_and_left: [u8; SECURE_SEED_LEN] = array_bytes::hex2array_unchecked(
 			"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
 		);
 		let pair = Pair::from_seed(&seed_for_right_and_left);
-		// we are using hash to field so this is not going to work
+		// we are using hash-to-field so this is not going to work
 		// assert_eq!(pair.seed(), seed);
 		let path = vec![DeriveJunction::Hard([0u8; 32])];
 		let derived = pair.derive(path.into_iter(), None).ok().unwrap().0;
@@ -599,13 +617,13 @@ mod test {
 		assert_eq!(
 				public,
 				Public::unchecked_from(
-					array_bytes::hex2array_unchecked("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd916dc6be608fab3c6bd894a606be86db346cc170db85c733853a371f3db54ae1b12052c0888d472760c81b537572a26f00db865e5963aef8634f9917571c51b538b564b2a9ceda938c8b930969ee3b832448e08e33a79e9ddd28af419a3ce45300f5dbc768b067781f44f3fe05a19e6b07b1c4196151ec3f8ea37e4f89a8963030d2101e931276bb9ebe1f20102239d780"
+					array_bytes::hex2array_unchecked("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd917a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400"
 	 ),
 	    		),
 	    	);
 		let message = b"";
 		let signature =
-	array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00bbb395bbdee1a35930912034f5fde3b36df2835a0536c865501b0675776a1d5931a3bea2e66eff73b2546c6af2061a8019223e4ebbbed661b2538e0f5823f2c708eb89c406beca8fcb53a5c13dbc7c0c42e4cf2be2942bba96ea29297915a06bd2b1b979c0e2ac8fd4ec684a6b5d110c"
+	array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b"
 	);
 		let signature = Signature::unchecked_from(signature);
 		assert!(pair.sign(&message[..]) == signature);
@@ -664,6 +682,7 @@ mod test {
 		let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
 
 		assert_ne!(pair1.public(), pair2.public());
+		assert_ne!(pair1.to_raw_vec(), pair2.to_raw_vec());
 	}
 
 	#[test]
diff --git a/substrate/primitives/core/src/sr25519.rs b/substrate/primitives/core/src/sr25519.rs
index b821055e2c56713067bf096899c2b10b6155c877..7c02afc3cd5ff8fe378613d88c088c95ceb805c8 100644
--- a/substrate/primitives/core/src/sr25519.rs
+++ b/substrate/primitives/core/src/sr25519.rs
@@ -970,6 +970,22 @@ mod tests {
 		assert!(Pair::verify(&signature, &message[..], &public));
 	}
 
+	#[test]
+	fn generate_with_phrase_should_be_recoverable_with_from_string() {
+		let (pair, phrase, seed) = Pair::generate_with_phrase(None);
+		let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_seed.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+		let (repair_phrase, reseed) =
+			Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid");
+		assert_eq!(seed, reseed);
+		assert_eq!(pair.public(), repair_phrase.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+		let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid");
+		assert_eq!(pair.public(), repair_string.public());
+		assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
+	}
+
 	#[test]
 	fn generated_pair_should_work() {
 		let (pair, _) = Pair::generate();
diff --git a/substrate/primitives/keystore/src/lib.rs b/substrate/primitives/keystore/src/lib.rs
index 07c4e2d5fd1dc4b8cc903d34154dbc51a050426c..5fae27bc32832eb8fa66e9f9d64491e445bd1e6d 100644
--- a/substrate/primitives/keystore/src/lib.rs
+++ b/substrate/primitives/keystore/src/lib.rs
@@ -355,6 +355,24 @@ pub trait Keystore: Send + Sync {
 		msg: &[u8],
 	) -> Result<Option<ecdsa_bls377::Signature>, Error>;
 
+	/// Hashes the `message` using keccak256 and then signs it using ECDSA
+	/// algorithm. It does not affect the behavior of BLS12-377 component. It generates
+	/// BLS12-377 Signature according to IETF standard.
+	///
+	/// 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_with_keccak256(
+		&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<(), ()>;
 
@@ -661,6 +679,16 @@ impl<T: Keystore + ?Sized> Keystore for Arc<T> {
 		(**self).ecdsa_bls377_sign(key_type, public, msg)
 	}
 
+	#[cfg(feature = "bls-experimental")]
+	fn ecdsa_bls377_sign_with_keccak256(
+		&self,
+		key_type: KeyTypeId,
+		public: &ecdsa_bls377::Public,
+		msg: &[u8],
+	) -> Result<Option<ecdsa_bls377::Signature>, Error> {
+		(**self).ecdsa_bls377_sign_with_keccak256(key_type, public, msg)
+	}
+
 	fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> {
 		(**self).insert(key_type, suri, public)
 	}
diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs
index 2879c458b4f68c40338b544532bdf80b9bf1d32f..e10660b126a33f23e00f5774412f65d665cc6737 100644
--- a/substrate/primitives/keystore/src/testing.rs
+++ b/substrate/primitives/keystore/src/testing.rs
@@ -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, ecdsa_bls377};
+use sp_core::{bls377, bls381, ecdsa_bls377, KeccakHasher};
 use sp_core::{
 	crypto::{ByteArray, KeyTypeId, Pair, VrfSecret},
 	ecdsa, ed25519, sr25519,
@@ -346,6 +346,19 @@ impl Keystore for MemoryKeystore {
 		self.sign::<ecdsa_bls377::Pair>(key_type, public, msg)
 	}
 
+	#[cfg(feature = "bls-experimental")]
+	fn ecdsa_bls377_sign_with_keccak256(
+		&self,
+		key_type: KeyTypeId,
+		public: &ecdsa_bls377::Public,
+		msg: &[u8],
+	) -> Result<Option<ecdsa_bls377::Signature>, Error> {
+		let sig = self
+			.pair::<ecdsa_bls377::Pair>(key_type, public)
+			.map(|pair| pair.sign_with_hasher::<KeccakHasher>(msg));
+		Ok(sig)
+	}
+
 	fn insert(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> {
 		self.keys
 			.write()
@@ -493,6 +506,38 @@ mod tests {
 		assert!(res.is_some());
 	}
 
+	#[test]
+	#[cfg(feature = "bls-experimental")]
+	fn ecdsa_bls377_sign_with_keccak_works() {
+		use sp_core::testing::ECDSA_BLS377;
+
+		let store = MemoryKeystore::new();
+
+		let suri = "//Alice";
+		let pair = ecdsa_bls377::Pair::from_string(suri, None).unwrap();
+
+		let msg = b"this should be a normal unhashed message not ";
+
+		// insert key, sign again
+		store.insert(ECDSA_BLS377, suri, pair.public().as_ref()).unwrap();
+
+		let res = store
+			.ecdsa_bls377_sign_with_keccak256(ECDSA_BLS377, &pair.public(), &msg[..])
+			.unwrap();
+
+		assert!(res.is_some());
+
+		// does not verify with default out-of-the-box verification
+		assert!(!ecdsa_bls377::Pair::verify(&res.clone().unwrap(), &msg[..], &pair.public()));
+
+		// should verify using keccak256 as hasher
+		assert!(ecdsa_bls377::Pair::verify_with_hasher::<KeccakHasher>(
+			&res.unwrap(),
+			msg,
+			&pair.public()
+		));
+	}
+
 	#[test]
 	#[cfg(feature = "bandersnatch-experimental")]
 	fn bandersnatch_vrf_sign() {