mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 23:21:06 +00:00
Paired-key Crypto Scheme (#1705)
BEEFY needs two cryptographic keys at the same time. Validators should sign BEEFY payload using both ECDSA and BLS key. The network will gossip a payload which contains a valid ECDSA key. The prover nodes aggregate the BLS keys if aggregation fails to verifies the validator which provided a valid ECDSA signature but an invalid BLS signature is subject to slashing. As such BEEFY session should be initiated with both key. Currently there is no straight forward way of doing so, beside having a session with RuntimeApp corresponding to a crypto scheme contains both keys. This pull request implement a generic paired_crypto scheme as well as implementing it for (ECDSA, BLS) pair. --------- Co-authored-by: Davide Galassi <davxy@datawok.net> Co-authored-by: Robert Hambrock <roberthambrock@gmail.com>
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
|
||||
//! Simple BLS (Boneh–Lynn–Shacham) Signature API.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::crypto::Ss58Codec;
|
||||
use crate::crypto::{ByteArray, CryptoType, Derive, Public as TraitPublic, UncheckedFrom};
|
||||
#[cfg(feature = "full_crypto")]
|
||||
@@ -28,8 +28,12 @@ use sp_std::vec::Vec;
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
#[cfg(feature = "std")]
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(all(not(feature = "std"), feature = "serde"))]
|
||||
use sp_std::alloc::{format, string::String};
|
||||
|
||||
use w3f_bls::{DoublePublicKey, DoubleSignature, EngineBLS, SerializableToBytes, TinyBLS381};
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use w3f_bls::{DoublePublicKeyScheme, Keypair, Message, SecretKey};
|
||||
@@ -39,6 +43,7 @@ use sp_std::{convert::TryFrom, marker::PhantomData, ops::Deref};
|
||||
|
||||
/// BLS-377 specialized types
|
||||
pub mod bls377 {
|
||||
pub use super::{PUBLIC_KEY_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE};
|
||||
use crate::crypto::CryptoTypeId;
|
||||
use w3f_bls::TinyBLS377;
|
||||
|
||||
@@ -60,6 +65,7 @@ pub mod bls377 {
|
||||
|
||||
/// BLS-381 specialized types
|
||||
pub mod bls381 {
|
||||
pub use super::{PUBLIC_KEY_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE};
|
||||
use crate::crypto::CryptoTypeId;
|
||||
use w3f_bls::TinyBLS381;
|
||||
|
||||
@@ -83,17 +89,17 @@ trait BlsBound: EngineBLS + HardJunctionId + Send + Sync + 'static {}
|
||||
|
||||
impl<T: EngineBLS + HardJunctionId + Send + Sync + 'static> BlsBound for T {}
|
||||
|
||||
// Secret key serialized size
|
||||
/// Secret key serialized size
|
||||
#[cfg(feature = "full_crypto")]
|
||||
const SECRET_KEY_SERIALIZED_SIZE: usize =
|
||||
<SecretKey<TinyBLS381> as SerializableToBytes>::SERIALIZED_BYTES_SIZE;
|
||||
|
||||
// Public key serialized size
|
||||
const PUBLIC_KEY_SERIALIZED_SIZE: usize =
|
||||
/// Public key serialized size
|
||||
pub const PUBLIC_KEY_SERIALIZED_SIZE: usize =
|
||||
<DoublePublicKey<TinyBLS381> as SerializableToBytes>::SERIALIZED_BYTES_SIZE;
|
||||
|
||||
// Signature serialized size
|
||||
const SIGNATURE_SERIALIZED_SIZE: usize =
|
||||
/// Signature serialized size
|
||||
pub const SIGNATURE_SERIALIZED_SIZE: usize =
|
||||
<DoubleSignature<TinyBLS381> as SerializableToBytes>::SERIALIZED_BYTES_SIZE;
|
||||
|
||||
/// A secret seed.
|
||||
@@ -258,7 +264,7 @@ impl<T> sp_std::fmt::Debug for Public<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: BlsBound> Serialize for Public<T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
@@ -268,7 +274,7 @@ impl<T: BlsBound> Serialize for Public<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T: BlsBound> Deserialize<'de> for Public<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
@@ -317,6 +323,10 @@ impl<T> sp_std::hash::Hash for Signature<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ByteArray for Signature<T> {
|
||||
const LEN: usize = SIGNATURE_SERIALIZED_SIZE;
|
||||
}
|
||||
|
||||
impl<T> TryFrom<&[u8]> for Signature<T> {
|
||||
type Error = ();
|
||||
|
||||
@@ -330,7 +340,7 @@ impl<T> TryFrom<&[u8]> for Signature<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T> Serialize for Signature<T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
@@ -340,7 +350,7 @@ impl<T> Serialize for Signature<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T> Deserialize<'de> for Signature<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
@@ -444,10 +454,9 @@ impl<T: BlsBound> TraitPair for Pair<T> {
|
||||
path: Iter,
|
||||
_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",
|
||||
);
|
||||
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",
|
||||
);
|
||||
for j in path {
|
||||
match j {
|
||||
DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath),
|
||||
@@ -529,11 +538,10 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
// Only passes if the seed = (seed mod ScalarField)
|
||||
#[test]
|
||||
fn seed_and_derive_should_work() {
|
||||
let seed = array_bytes::hex2array_unchecked(
|
||||
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f00",
|
||||
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
|
||||
);
|
||||
let pair = Pair::from_seed(&seed);
|
||||
// we are using hash to field so this is not going to work
|
||||
@@ -543,7 +551,7 @@ mod test {
|
||||
assert_eq!(
|
||||
derived.to_raw_vec(),
|
||||
array_bytes::hex2array_unchecked::<_, 32>(
|
||||
"a4f2269333b3e87c577aa00c4a2cd650b3b30b2e8c286a47c251279ff3a26e0d"
|
||||
"3a0626d095148813cd1642d38254f1cfff7eb8cc1a2fc83b2a135377c3554c12"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1213,7 +1213,7 @@ macro_rules! impl_from_entropy_base {
|
||||
[$type; 17], [$type; 18], [$type; 19], [$type; 20], [$type; 21], [$type; 22], [$type; 23], [$type; 24],
|
||||
[$type; 25], [$type; 26], [$type; 27], [$type; 28], [$type; 29], [$type; 30], [$type; 31], [$type; 32],
|
||||
[$type; 36], [$type; 40], [$type; 44], [$type; 48], [$type; 56], [$type; 64], [$type; 72], [$type; 80],
|
||||
[$type; 96], [$type; 112], [$type; 128], [$type; 160], [$type; 192], [$type; 224], [$type; 256]
|
||||
[$type; 96], [$type; 112], [$type; 128], [$type; 160], [$type; 177], [$type; 192], [$type; 224], [$type; 256]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,12 @@ use sp_std::vec::Vec;
|
||||
/// An identifier used to match public keys against ecdsa keys
|
||||
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ecds");
|
||||
|
||||
/// The byte length of public key
|
||||
pub const PUBLIC_KEY_SERIALIZED_SIZE: usize = 33;
|
||||
|
||||
/// The byte length of signature
|
||||
pub const SIGNATURE_SERIALIZED_SIZE: usize = 65;
|
||||
|
||||
/// A secret seed (which is bytewise essentially equivalent to a SecretKey).
|
||||
///
|
||||
/// We need it as a different type because `Seed` is expected to be AsRef<[u8]>.
|
||||
@@ -71,11 +77,11 @@ type Seed = [u8; 32];
|
||||
PartialOrd,
|
||||
Ord,
|
||||
)]
|
||||
pub struct Public(pub [u8; 33]);
|
||||
pub struct Public(pub [u8; PUBLIC_KEY_SERIALIZED_SIZE]);
|
||||
|
||||
impl crate::crypto::FromEntropy for Public {
|
||||
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
|
||||
let mut result = Self([0u8; 33]);
|
||||
let mut result = Self([0u8; PUBLIC_KEY_SERIALIZED_SIZE]);
|
||||
input.read(&mut result.0[..])?;
|
||||
Ok(result)
|
||||
}
|
||||
@@ -86,7 +92,7 @@ impl Public {
|
||||
///
|
||||
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
|
||||
/// you are certain that the array actually is a pubkey. GIGO!
|
||||
pub fn from_raw(data: [u8; 33]) -> Self {
|
||||
pub fn from_raw(data: [u8; PUBLIC_KEY_SERIALIZED_SIZE]) -> Self {
|
||||
Self(data)
|
||||
}
|
||||
|
||||
@@ -109,7 +115,7 @@ impl Public {
|
||||
}
|
||||
|
||||
impl ByteArray for Public {
|
||||
const LEN: usize = 33;
|
||||
const LEN: usize = PUBLIC_KEY_SERIALIZED_SIZE;
|
||||
}
|
||||
|
||||
impl TraitPublic for Public {}
|
||||
@@ -148,8 +154,8 @@ impl From<Pair> for Public {
|
||||
}
|
||||
}
|
||||
|
||||
impl UncheckedFrom<[u8; 33]> for Public {
|
||||
fn unchecked_from(x: [u8; 33]) -> Self {
|
||||
impl UncheckedFrom<[u8; PUBLIC_KEY_SERIALIZED_SIZE]> for Public {
|
||||
fn unchecked_from(x: [u8; PUBLIC_KEY_SERIALIZED_SIZE]) -> Self {
|
||||
Public(x)
|
||||
}
|
||||
}
|
||||
@@ -198,14 +204,18 @@ impl<'de> Deserialize<'de> for Public {
|
||||
/// A signature (a 512-bit value, plus 8 bits for recovery ID).
|
||||
#[cfg_attr(feature = "full_crypto", derive(Hash))]
|
||||
#[derive(Encode, Decode, MaxEncodedLen, PassByInner, TypeInfo, PartialEq, Eq)]
|
||||
pub struct Signature(pub [u8; 65]);
|
||||
pub struct Signature(pub [u8; SIGNATURE_SERIALIZED_SIZE]);
|
||||
|
||||
impl ByteArray for Signature {
|
||||
const LEN: usize = SIGNATURE_SERIALIZED_SIZE;
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Signature {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
|
||||
if data.len() == 65 {
|
||||
let mut inner = [0u8; 65];
|
||||
if data.len() == SIGNATURE_SERIALIZED_SIZE {
|
||||
let mut inner = [0u8; SIGNATURE_SERIALIZED_SIZE];
|
||||
inner.copy_from_slice(data);
|
||||
Ok(Signature(inner))
|
||||
} else {
|
||||
@@ -239,7 +249,7 @@ impl<'de> Deserialize<'de> for Signature {
|
||||
|
||||
impl Clone for Signature {
|
||||
fn clone(&self) -> Self {
|
||||
let mut r = [0u8; 65];
|
||||
let mut r = [0u8; SIGNATURE_SERIALIZED_SIZE];
|
||||
r.copy_from_slice(&self.0[..]);
|
||||
Signature(r)
|
||||
}
|
||||
@@ -247,18 +257,18 @@ impl Clone for Signature {
|
||||
|
||||
impl Default for Signature {
|
||||
fn default() -> Self {
|
||||
Signature([0u8; 65])
|
||||
Signature([0u8; SIGNATURE_SERIALIZED_SIZE])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for [u8; 65] {
|
||||
fn from(v: Signature) -> [u8; 65] {
|
||||
impl From<Signature> for [u8; SIGNATURE_SERIALIZED_SIZE] {
|
||||
fn from(v: Signature) -> [u8; SIGNATURE_SERIALIZED_SIZE] {
|
||||
v.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8; 65]> for Signature {
|
||||
fn as_ref(&self) -> &[u8; 65] {
|
||||
impl AsRef<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature {
|
||||
fn as_ref(&self) -> &[u8; SIGNATURE_SERIALIZED_SIZE] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
@@ -287,8 +297,8 @@ impl sp_std::fmt::Debug for Signature {
|
||||
}
|
||||
}
|
||||
|
||||
impl UncheckedFrom<[u8; 65]> for Signature {
|
||||
fn unchecked_from(data: [u8; 65]) -> Signature {
|
||||
impl UncheckedFrom<[u8; SIGNATURE_SERIALIZED_SIZE]> for Signature {
|
||||
fn unchecked_from(data: [u8; SIGNATURE_SERIALIZED_SIZE]) -> Signature {
|
||||
Signature(data)
|
||||
}
|
||||
}
|
||||
@@ -298,7 +308,7 @@ impl Signature {
|
||||
///
|
||||
/// NOTE: No checking goes on to ensure this is a real signature. Only use it if
|
||||
/// you are certain that the array actually is a signature. GIGO!
|
||||
pub fn from_raw(data: [u8; 65]) -> Signature {
|
||||
pub fn from_raw(data: [u8; SIGNATURE_SERIALIZED_SIZE]) -> Signature {
|
||||
Signature(data)
|
||||
}
|
||||
|
||||
@@ -307,10 +317,10 @@ impl Signature {
|
||||
/// NOTE: No checking goes on to ensure this is a real signature. Only use it if
|
||||
/// you are certain that the array actually is a signature. GIGO!
|
||||
pub fn from_slice(data: &[u8]) -> Option<Self> {
|
||||
if data.len() != 65 {
|
||||
if data.len() != SIGNATURE_SERIALIZED_SIZE {
|
||||
return None
|
||||
}
|
||||
let mut r = [0u8; 65];
|
||||
let mut r = [0u8; SIGNATURE_SERIALIZED_SIZE];
|
||||
r.copy_from_slice(data);
|
||||
Some(Signature(r))
|
||||
}
|
||||
@@ -473,7 +483,7 @@ impl Pair {
|
||||
pub fn verify_deprecated<M: AsRef<[u8]>>(sig: &Signature, message: M, pubkey: &Public) -> bool {
|
||||
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
|
||||
|
||||
let parse_signature_overflowing = |x: [u8; 65]| {
|
||||
let parse_signature_overflowing = |x: [u8; SIGNATURE_SERIALIZED_SIZE]| {
|
||||
let sig = libsecp256k1::Signature::parse_overflowing_slice(&x[..64]).ok()?;
|
||||
let rid = libsecp256k1::RecoveryId::parse(x[64]).ok()?;
|
||||
Some((sig, rid))
|
||||
@@ -726,7 +736,7 @@ mod test {
|
||||
let signature = pair.sign(&message[..]);
|
||||
let serialized_signature = serde_json::to_string(&signature).unwrap();
|
||||
// Signature is 65 bytes, so 130 chars + 2 quote chars
|
||||
assert_eq!(serialized_signature.len(), 132);
|
||||
assert_eq!(serialized_signature.len(), SIGNATURE_SERIALIZED_SIZE * 2 + 2);
|
||||
let signature = serde_json::from_str(&serialized_signature).unwrap();
|
||||
assert!(Pair::verify(&signature, &message[..], &pair.public()));
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ macro_rules! impl_non_endians {
|
||||
impl_non_endians!(
|
||||
[u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 10], [u8; 12],
|
||||
[u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], [u8; 48], [u8; 56],
|
||||
[u8; 64], [u8; 65], [u8; 80], [u8; 96], [u8; 112], [u8; 128], [u8; 144]
|
||||
[u8; 64], [u8; 65], [u8; 80], [u8; 96], [u8; 112], [u8; 128], [u8; 144], [u8; 177]
|
||||
);
|
||||
|
||||
/// Format into ASCII + # + hex, suitable for storage key preimages.
|
||||
|
||||
@@ -66,6 +66,7 @@ pub mod hash;
|
||||
#[cfg(feature = "std")]
|
||||
mod hasher;
|
||||
pub mod offchain;
|
||||
pub mod paired_crypto;
|
||||
pub mod sr25519;
|
||||
pub mod testing;
|
||||
#[cfg(feature = "std")]
|
||||
|
||||
@@ -0,0 +1,662 @@
|
||||
// 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.
|
||||
|
||||
//! API for using a pair of crypto schemes together.
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::crypto::Ss58Codec;
|
||||
use crate::crypto::{ByteArray, CryptoType, Derive, Public as PublicT, UncheckedFrom};
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use crate::crypto::{DeriveError, DeriveJunction, Pair as PairT, SecretStringError};
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(all(not(feature = "std"), feature = "serde"))]
|
||||
use sp_std::alloc::{format, string::String};
|
||||
|
||||
use sp_runtime_interface::pass_by::PassByInner;
|
||||
use sp_std::convert::TryFrom;
|
||||
|
||||
/// ECDSA and BLS12-377 paired crypto scheme
|
||||
#[cfg(feature = "bls-experimental")]
|
||||
pub mod ecdsa_n_bls377 {
|
||||
use crate::{bls377, crypto::CryptoTypeId, ecdsa};
|
||||
|
||||
/// An identifier used to match public keys against BLS12-377 keys
|
||||
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ecb7");
|
||||
|
||||
const PUBLIC_KEY_LEN: usize =
|
||||
ecdsa::PUBLIC_KEY_SERIALIZED_SIZE + bls377::PUBLIC_KEY_SERIALIZED_SIZE;
|
||||
const SIGNATURE_LEN: usize =
|
||||
ecdsa::SIGNATURE_SERIALIZED_SIZE + bls377::SIGNATURE_SERIALIZED_SIZE;
|
||||
|
||||
/// (ECDSA, BLS12-377) key-pair pair.
|
||||
#[cfg(feature = "full_crypto")]
|
||||
pub type Pair = super::Pair<ecdsa::Pair, bls377::Pair, PUBLIC_KEY_LEN, SIGNATURE_LEN>;
|
||||
/// (ECDSA, BLS12-377) public key pair.
|
||||
pub type Public = super::Public<PUBLIC_KEY_LEN>;
|
||||
/// (ECDSA, BLS12-377) signature pair.
|
||||
pub type Signature = super::Signature<SIGNATURE_LEN>;
|
||||
|
||||
impl super::CryptoType for Public {
|
||||
#[cfg(feature = "full_crypto")]
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
impl super::CryptoType for Signature {
|
||||
#[cfg(feature = "full_crypto")]
|
||||
type Pair = Pair;
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl super::CryptoType for Pair {
|
||||
type Pair = Pair;
|
||||
}
|
||||
}
|
||||
|
||||
/// Secure seed length.
|
||||
///
|
||||
/// Currently only supporting sub-schemes whose seed is a 32-bytes array.
|
||||
#[cfg(feature = "full_crypto")]
|
||||
const SECURE_SEED_LEN: usize = 32;
|
||||
|
||||
/// A secret seed.
|
||||
///
|
||||
/// It's not called a "secret key" because ring doesn't expose the secret keys
|
||||
/// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we
|
||||
/// will need it later (such as for HDKD).
|
||||
#[cfg(feature = "full_crypto")]
|
||||
type Seed = [u8; SECURE_SEED_LEN];
|
||||
|
||||
/// A public key.
|
||||
#[derive(Clone, Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Public<const LEFT_PLUS_RIGHT_LEN: usize>([u8; LEFT_PLUS_RIGHT_LEN]);
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> sp_std::hash::Hash for Public<LEFT_PLUS_RIGHT_LEN> {
|
||||
fn hash<H: sp_std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> ByteArray for Public<LEFT_PLUS_RIGHT_LEN> {
|
||||
const LEN: usize = LEFT_PLUS_RIGHT_LEN;
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> TryFrom<&[u8]> for Public<LEFT_PLUS_RIGHT_LEN> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
|
||||
if data.len() != LEFT_PLUS_RIGHT_LEN {
|
||||
return Err(())
|
||||
}
|
||||
let mut inner = [0u8; LEFT_PLUS_RIGHT_LEN];
|
||||
inner.copy_from_slice(data);
|
||||
Ok(Public(inner))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> AsRef<[u8; LEFT_PLUS_RIGHT_LEN]>
|
||||
for Public<LEFT_PLUS_RIGHT_LEN>
|
||||
{
|
||||
fn as_ref(&self) -> &[u8; LEFT_PLUS_RIGHT_LEN] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> AsRef<[u8]> for Public<LEFT_PLUS_RIGHT_LEN> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> AsMut<[u8]> for Public<LEFT_PLUS_RIGHT_LEN> {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> PassByInner for Public<LEFT_PLUS_RIGHT_LEN> {
|
||||
type Inner = [u8; LEFT_PLUS_RIGHT_LEN];
|
||||
|
||||
fn into_inner(self) -> Self::Inner {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn from_inner(inner: Self::Inner) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl<
|
||||
LeftPair: PairT,
|
||||
RightPair: PairT,
|
||||
const LEFT_PLUS_RIGHT_PUBLIC_LEN: usize,
|
||||
const SIGNATURE_LEN: usize,
|
||||
> From<Pair<LeftPair, RightPair, LEFT_PLUS_RIGHT_PUBLIC_LEN, SIGNATURE_LEN>>
|
||||
for Public<LEFT_PLUS_RIGHT_PUBLIC_LEN>
|
||||
where
|
||||
Pair<LeftPair, RightPair, LEFT_PLUS_RIGHT_PUBLIC_LEN, SIGNATURE_LEN>:
|
||||
PairT<Public = Public<LEFT_PLUS_RIGHT_PUBLIC_LEN>>,
|
||||
{
|
||||
fn from(x: Pair<LeftPair, RightPair, LEFT_PLUS_RIGHT_PUBLIC_LEN, SIGNATURE_LEN>) -> Self {
|
||||
x.public()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> UncheckedFrom<[u8; LEFT_PLUS_RIGHT_LEN]>
|
||||
for Public<LEFT_PLUS_RIGHT_LEN>
|
||||
{
|
||||
fn unchecked_from(data: [u8; LEFT_PLUS_RIGHT_LEN]) -> Self {
|
||||
Public(data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> std::fmt::Display for Public<LEFT_PLUS_RIGHT_LEN>
|
||||
where
|
||||
Public<LEFT_PLUS_RIGHT_LEN>: CryptoType,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", self.to_ss58check())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> sp_std::fmt::Debug for Public<LEFT_PLUS_RIGHT_LEN>
|
||||
where
|
||||
Public<LEFT_PLUS_RIGHT_LEN>: CryptoType,
|
||||
[u8; LEFT_PLUS_RIGHT_LEN]: crate::hexdisplay::AsBytesRef,
|
||||
{
|
||||
#[cfg(feature = "std")]
|
||||
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
let s = self.to_ss58check();
|
||||
write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8])
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> Serialize for Public<LEFT_PLUS_RIGHT_LEN>
|
||||
where
|
||||
Public<LEFT_PLUS_RIGHT_LEN>: CryptoType,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_ss58check())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, const LEFT_PLUS_RIGHT_LEN: usize> Deserialize<'de> for Public<LEFT_PLUS_RIGHT_LEN>
|
||||
where
|
||||
Public<LEFT_PLUS_RIGHT_LEN>: CryptoType,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Public::from_ss58check(&String::deserialize(deserializer)?)
|
||||
.map_err(|e| de::Error::custom(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> PublicT for Public<LEFT_PLUS_RIGHT_LEN> where
|
||||
Public<LEFT_PLUS_RIGHT_LEN>: CryptoType
|
||||
{
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> Derive for Public<LEFT_PLUS_RIGHT_LEN> {}
|
||||
|
||||
/// Trait characterizing a signature which could be used as individual component of an
|
||||
/// `paired_crypto:Signature` pair.
|
||||
pub trait SignatureBound: ByteArray {}
|
||||
|
||||
impl<T: ByteArray> SignatureBound for T {}
|
||||
|
||||
/// A pair of signatures of different types
|
||||
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq)]
|
||||
pub struct Signature<const LEFT_PLUS_RIGHT_LEN: usize>([u8; LEFT_PLUS_RIGHT_LEN]);
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> sp_std::hash::Hash for Signature<LEFT_PLUS_RIGHT_LEN> {
|
||||
fn hash<H: sp_std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> ByteArray for Signature<LEFT_PLUS_RIGHT_LEN> {
|
||||
const LEN: usize = LEFT_PLUS_RIGHT_LEN;
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> TryFrom<&[u8]> for Signature<LEFT_PLUS_RIGHT_LEN> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
|
||||
if data.len() != LEFT_PLUS_RIGHT_LEN {
|
||||
return Err(())
|
||||
}
|
||||
let mut inner = [0u8; LEFT_PLUS_RIGHT_LEN];
|
||||
inner.copy_from_slice(data);
|
||||
Ok(Signature(inner))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> AsMut<[u8]> for Signature<LEFT_PLUS_RIGHT_LEN> {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> AsRef<[u8; LEFT_PLUS_RIGHT_LEN]>
|
||||
for Signature<LEFT_PLUS_RIGHT_LEN>
|
||||
{
|
||||
fn as_ref(&self) -> &[u8; LEFT_PLUS_RIGHT_LEN] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> AsRef<[u8]> for Signature<LEFT_PLUS_RIGHT_LEN> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> Serialize for Signature<LEFT_PLUS_RIGHT_LEN> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&array_bytes::bytes2hex("", self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, const LEFT_PLUS_RIGHT_LEN: usize> Deserialize<'de> for Signature<LEFT_PLUS_RIGHT_LEN> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = array_bytes::hex2bytes(&String::deserialize(deserializer)?)
|
||||
.map_err(|e| de::Error::custom(format!("{:?}", e)))?;
|
||||
Signature::<LEFT_PLUS_RIGHT_LEN>::try_from(bytes.as_ref()).map_err(|e| {
|
||||
de::Error::custom(format!("Error converting deserialized data into signature: {:?}", e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> From<Signature<LEFT_PLUS_RIGHT_LEN>>
|
||||
for [u8; LEFT_PLUS_RIGHT_LEN]
|
||||
{
|
||||
fn from(signature: Signature<LEFT_PLUS_RIGHT_LEN>) -> [u8; LEFT_PLUS_RIGHT_LEN] {
|
||||
signature.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> sp_std::fmt::Debug for Signature<LEFT_PLUS_RIGHT_LEN>
|
||||
where
|
||||
[u8; LEFT_PLUS_RIGHT_LEN]: crate::hexdisplay::AsBytesRef,
|
||||
{
|
||||
#[cfg(feature = "std")]
|
||||
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEFT_PLUS_RIGHT_LEN: usize> UncheckedFrom<[u8; LEFT_PLUS_RIGHT_LEN]>
|
||||
for Signature<LEFT_PLUS_RIGHT_LEN>
|
||||
{
|
||||
fn unchecked_from(data: [u8; LEFT_PLUS_RIGHT_LEN]) -> Self {
|
||||
Signature(data)
|
||||
}
|
||||
}
|
||||
|
||||
/// A key pair.
|
||||
#[cfg(feature = "full_crypto")]
|
||||
#[derive(Clone)]
|
||||
pub struct Pair<
|
||||
LeftPair: PairT,
|
||||
RightPair: PairT,
|
||||
const PUBLIC_KEY_LEN: usize,
|
||||
const SIGNATURE_LEN: usize,
|
||||
> {
|
||||
left: LeftPair,
|
||||
right: RightPair,
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl<
|
||||
LeftPair: PairT,
|
||||
RightPair: PairT,
|
||||
const PUBLIC_KEY_LEN: usize,
|
||||
const SIGNATURE_LEN: usize,
|
||||
> PairT for Pair<LeftPair, RightPair, PUBLIC_KEY_LEN, SIGNATURE_LEN>
|
||||
where
|
||||
Pair<LeftPair, RightPair, PUBLIC_KEY_LEN, SIGNATURE_LEN>: CryptoType,
|
||||
LeftPair::Signature: SignatureBound,
|
||||
RightPair::Signature: SignatureBound,
|
||||
Public<PUBLIC_KEY_LEN>: CryptoType,
|
||||
LeftPair::Seed: From<Seed> + Into<Seed>,
|
||||
RightPair::Seed: From<Seed> + Into<Seed>,
|
||||
{
|
||||
type Seed = Seed;
|
||||
type Public = Public<PUBLIC_KEY_LEN>;
|
||||
type Signature = Signature<SIGNATURE_LEN>;
|
||||
|
||||
fn from_seed_slice(seed_slice: &[u8]) -> Result<Self, SecretStringError> {
|
||||
if seed_slice.len() != SECURE_SEED_LEN {
|
||||
return Err(SecretStringError::InvalidSeedLength)
|
||||
}
|
||||
let left = LeftPair::from_seed_slice(&seed_slice)?;
|
||||
let right = RightPair::from_seed_slice(&seed_slice)?;
|
||||
Ok(Pair { left, right })
|
||||
}
|
||||
|
||||
/// Derive a child key from a series of given junctions.
|
||||
///
|
||||
/// Note: if the `LeftPair` and `RightPair` crypto schemes differ in
|
||||
/// seed derivation, `derive` will drop the seed in the return.
|
||||
fn derive<Iter: Iterator<Item = DeriveJunction>>(
|
||||
&self,
|
||||
path: Iter,
|
||||
seed: Option<Self::Seed>,
|
||||
) -> Result<(Self, Option<Self::Seed>), DeriveError> {
|
||||
let path: Vec<_> = path.collect();
|
||||
|
||||
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 seed = match (left.1, right.1) {
|
||||
(Some(l), Some(r)) if l.as_ref() == r.as_ref() => Some(l.into()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Ok((Self { left: left.0, right: right.0 }, seed))
|
||||
}
|
||||
|
||||
fn public(&self) -> Self::Public {
|
||||
let mut raw = [0u8; PUBLIC_KEY_LEN];
|
||||
let left_pub = self.left.public();
|
||||
let right_pub = self.right.public();
|
||||
raw[..LeftPair::Public::LEN].copy_from_slice(left_pub.as_ref());
|
||||
raw[LeftPair::Public::LEN..].copy_from_slice(right_pub.as_ref());
|
||||
Self::Public::unchecked_from(raw)
|
||||
}
|
||||
|
||||
fn sign(&self, message: &[u8]) -> Self::Signature {
|
||||
let mut raw: [u8; SIGNATURE_LEN] = [0u8; SIGNATURE_LEN];
|
||||
raw[..LeftPair::Signature::LEN].copy_from_slice(self.left.sign(message).as_ref());
|
||||
raw[LeftPair::Signature::LEN..].copy_from_slice(self.right.sign(message).as_ref());
|
||||
Self::Signature::unchecked_from(raw)
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(sig: &Self::Signature, message: M, public: &Self::Public) -> bool {
|
||||
let Ok(left_pub) = public.0[..LeftPair::Public::LEN].try_into() else { return false };
|
||||
let Ok(left_sig) = sig.0[0..LeftPair::Signature::LEN].try_into() else { return false };
|
||||
if !LeftPair::verify(&left_sig, message.as_ref(), &left_pub) {
|
||||
return false
|
||||
}
|
||||
|
||||
let Ok(right_pub) = public.0[LeftPair::Public::LEN..PUBLIC_KEY_LEN].try_into() else {
|
||||
return false
|
||||
};
|
||||
let Ok(right_sig) = sig.0[LeftPair::Signature::LEN..].try_into() else { return false };
|
||||
RightPair::verify(&right_sig, message.as_ref(), &right_pub)
|
||||
}
|
||||
|
||||
/// Get the seed/secret key for each key and then concatenate them.
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
let mut raw = self.left.to_raw_vec();
|
||||
raw.extend(self.right.to_raw_vec());
|
||||
raw
|
||||
}
|
||||
}
|
||||
|
||||
// Test set exercising the (ECDSA, BLS12-377) implementation
|
||||
#[cfg(all(test, feature = "bls-experimental"))]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::crypto::DEV_PHRASE;
|
||||
use ecdsa_n_bls377::{Pair, Signature};
|
||||
|
||||
use crate::{bls377, ecdsa};
|
||||
#[test]
|
||||
|
||||
fn test_length_of_paired_ecdsa_and_bls377_public_key_and_signature_is_correct() {
|
||||
assert_eq!(
|
||||
<Pair as PairT>::Public::LEN,
|
||||
<ecdsa::Pair as PairT>::Public::LEN + <bls377::Pair as PairT>::Public::LEN
|
||||
);
|
||||
assert_eq!(
|
||||
<Pair as PairT>::Signature::LEN,
|
||||
<ecdsa::Pair as PairT>::Signature::LEN + <bls377::Pair as PairT>::Signature::LEN
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_phrase_should_be_used() {
|
||||
assert_eq!(
|
||||
Pair::from_string("//Alice///password", None).unwrap().public(),
|
||||
Pair::from_string(&format!("{}//Alice", DEV_PHRASE), Some("password"))
|
||||
.unwrap()
|
||||
.public(),
|
||||
);
|
||||
}
|
||||
|
||||
#[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
|
||||
// assert_eq!(pair.seed(), seed);
|
||||
let path = vec![DeriveJunction::Hard([0u8; 32])];
|
||||
let derived = pair.derive(path.into_iter(), None).ok().unwrap().0;
|
||||
assert_eq!(
|
||||
derived.to_raw_vec(),
|
||||
[
|
||||
array_bytes::hex2array_unchecked::<&str, SECURE_SEED_LEN>(
|
||||
"b8eefc4937200a8382d00050e050ced2d4ab72cc2ef1b061477afb51564fdd61"
|
||||
),
|
||||
array_bytes::hex2array_unchecked::<&str, SECURE_SEED_LEN>(
|
||||
"3a0626d095148813cd1642d38254f1cfff7eb8cc1a2fc83b2a135377c3554c12"
|
||||
)
|
||||
]
|
||||
.concat()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_should_work() {
|
||||
let seed_left_and_right: [u8; SECURE_SEED_LEN] = array_bytes::hex2array_unchecked(
|
||||
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
|
||||
);
|
||||
let pair = Pair::from_seed(&([seed_left_and_right].concat()[..].try_into().unwrap()));
|
||||
let public = pair.public();
|
||||
assert_eq!(
|
||||
public,
|
||||
Public::unchecked_from(
|
||||
array_bytes::hex2array_unchecked("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd917a84ca8ce4c37c93c95ecee6a3c0c9a7b9c225093cf2f12dc4f69cbfb847ef9424a18f5755d5a742247d386ff2aabb806bcf160eff31293ea9616976628f77266c8a8cc1d8753be04197bd6cdd8c5c87a148f782c4c1568d599b48833fd539001e580cff64bbc71850605433fcd051f3afc3b74819786f815ffb5272030a8d03e5df61e6183f8fd8ea85f26defa83400"),
|
||||
),
|
||||
);
|
||||
let message = b"";
|
||||
let signature =
|
||||
array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00d1e3013161991e142d8751017d4996209c2ff8a9ee160f373733eda3b4b785ba6edce9f45f87104bbe07aa6aa6eb2780aa705efb2c13d3b317d6409d159d23bdc7cdd5c2a832d1551cf49d811d49c901495e527dbd532e3a462335ce2686009104aba7bc11c5b22be78f3198d2727a0b"
|
||||
);
|
||||
let signature = Signature::unchecked_from(signature);
|
||||
assert!(pair.sign(&message[..]) == signature);
|
||||
assert!(Pair::verify(&signature, &message[..], &public));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_by_string_should_work() {
|
||||
let pair = Pair::from_string(
|
||||
"0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let public = pair.public();
|
||||
assert_eq!(
|
||||
public,
|
||||
Public::unchecked_from(
|
||||
array_bytes::hex2array_unchecked("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd916dc6be608fab3c6bd894a606be86db346cc170db85c733853a371f3db54ae1b12052c0888d472760c81b537572a26f00db865e5963aef8634f9917571c51b538b564b2a9ceda938c8b930969ee3b832448e08e33a79e9ddd28af419a3ce45300f5dbc768b067781f44f3fe05a19e6b07b1c4196151ec3f8ea37e4f89a8963030d2101e931276bb9ebe1f20102239d780"
|
||||
),
|
||||
),
|
||||
);
|
||||
let message = b"";
|
||||
let signature =
|
||||
array_bytes::hex2array_unchecked("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00bbb395bbdee1a35930912034f5fde3b36df2835a0536c865501b0675776a1d5931a3bea2e66eff73b2546c6af2061a8019223e4ebbbed661b2538e0f5823f2c708eb89c406beca8fcb53a5c13dbc7c0c42e4cf2be2942bba96ea29297915a06bd2b1b979c0e2ac8fd4ec684a6b5d110c"
|
||||
);
|
||||
let signature = Signature::unchecked_from(signature);
|
||||
assert!(pair.sign(&message[..]) == signature);
|
||||
assert!(Pair::verify(&signature, &message[..], &public));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_pair_should_work() {
|
||||
let (pair, _) = Pair::generate();
|
||||
let public = pair.public();
|
||||
let message = b"Something important";
|
||||
let signature = pair.sign(&message[..]);
|
||||
assert!(Pair::verify(&signature, &message[..], &public));
|
||||
assert!(!Pair::verify(&signature, b"Something else", &public));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seeded_pair_should_work() {
|
||||
let pair =
|
||||
Pair::from_seed(&(b"12345678901234567890123456789012".as_slice().try_into().unwrap()));
|
||||
let public = pair.public();
|
||||
assert_eq!(
|
||||
public,
|
||||
Public::unchecked_from(
|
||||
array_bytes::hex2array_unchecked("035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9754d2f2bbfa67df54d7e0e951979a18a1e0f45948857752cc2bac6bbb0b1d05e8e48bcc453920bf0c4bbd5993212480112a1fb433f04d74af0a8b700d93dc957ab3207f8d071e948f5aca1a7632c00bdf6d06be05b43e2e6216dccc8a5d55a0071cb2313cfd60b7e9114619cd17c06843b352f0b607a99122f6651df8f02e1ad3697bd208e62af047ddd7b942ba80080")
|
||||
),
|
||||
);
|
||||
let message =
|
||||
array_bytes::hex2bytes_unchecked("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000"
|
||||
);
|
||||
let signature = pair.sign(&message[..]);
|
||||
println!("Correct signature: {:?}", signature);
|
||||
assert!(Pair::verify(&signature, &message[..], &public));
|
||||
assert!(!Pair::verify(&signature, "Other message", &public));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_with_phrase_recovery_possible() {
|
||||
let (pair1, phrase, _) = Pair::generate_with_phrase(None);
|
||||
let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
|
||||
|
||||
assert_eq!(pair1.public(), pair2.public());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_with_password_phrase_recovery_possible() {
|
||||
let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password"));
|
||||
let (pair2, _) = Pair::from_phrase(&phrase, Some("password")).unwrap();
|
||||
|
||||
assert_eq!(pair1.public(), pair2.public());
|
||||
}
|
||||
|
||||
#[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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ss58check_roundtrip_works() {
|
||||
let pair =
|
||||
Pair::from_seed(&(b"12345678901234567890123456789012".as_slice().try_into().unwrap()));
|
||||
let public = pair.public();
|
||||
let s = public.to_ss58check();
|
||||
println!("Correct: {}", s);
|
||||
let cmp = Public::from_ss58check(&s).unwrap();
|
||||
assert_eq!(cmp, public);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_serialization_works() {
|
||||
let pair =
|
||||
Pair::from_seed(&(b"12345678901234567890123456789012".as_slice().try_into().unwrap()));
|
||||
let message = b"Something important";
|
||||
let signature = pair.sign(&message[..]);
|
||||
|
||||
let serialized_signature = serde_json::to_string(&signature).unwrap();
|
||||
println!("{:?} -- {:}", signature.0, serialized_signature);
|
||||
// Signature is 177 bytes, hexify * 2 + 2 quote charsy
|
||||
assert_eq!(serialized_signature.len(), 356);
|
||||
let signature = serde_json::from_str(&serialized_signature).unwrap();
|
||||
assert!(Pair::verify(&signature, &message[..], &pair.public()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_serialization_doesnt_panic() {
|
||||
fn deserialize_signature(text: &str) -> Result<Signature, serde_json::error::Error> {
|
||||
serde_json::from_str(text)
|
||||
}
|
||||
assert!(deserialize_signature("Not valid json.").is_err());
|
||||
assert!(deserialize_signature("\"Not an actual signature.\"").is_err());
|
||||
// Poorly-sized
|
||||
assert!(deserialize_signature("\"abc123\"").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_and_decode_public_key_works() {
|
||||
let pair =
|
||||
Pair::from_seed(&(b"12345678901234567890123456789012".as_slice().try_into().unwrap()));
|
||||
let public = pair.public();
|
||||
let encoded_public = public.encode();
|
||||
let decoded_public = Public::decode(&mut encoded_public.as_slice()).unwrap();
|
||||
assert_eq!(public, decoded_public)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_and_decode_signature_works() {
|
||||
let pair =
|
||||
Pair::from_seed(&(b"12345678901234567890123456789012".as_slice().try_into().unwrap()));
|
||||
let message = b"Something important";
|
||||
let signature = pair.sign(&message[..]);
|
||||
let encoded_signature = signature.encode();
|
||||
let decoded_signature = Signature::decode(&mut encoded_signature.as_slice()).unwrap();
|
||||
assert_eq!(signature, decoded_signature)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user