Skip to content

Commit

Permalink
crypto: add tz4 to aggregate PK,PKH types
Browse files Browse the repository at this point in the history
  • Loading branch information
emturner committed Jun 28, 2024
1 parent c2371c3 commit c19b326
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 2 deletions.
112 changes: 111 additions & 1 deletion crypto/src/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use crate::base58::{FromBase58Check, FromBase58CheckError};
use crate::hash::{Hash, HashTrait, HashType};
use crate::hash::{PublicKeyEd25519, PublicKeyP256, PublicKeySecp256k1};
use crate::hash::{PublicKeyBls, PublicKeyEd25519, PublicKeyP256, PublicKeySecp256k1};
use crate::signature::Signature;
use crate::{CryptoError, PublicKeySignatureVerifier};
use std::fmt::Display;
Expand All @@ -24,6 +24,8 @@ pub enum PublicKey {
Secp256k1(PublicKeySecp256k1),
/// Tz3 - public key
P256(PublicKeyP256),
/// Tz4 - public key
Bls(PublicKeyBls),
}

impl Display for PublicKey {
Expand All @@ -32,6 +34,7 @@ impl Display for PublicKey {
Self::Ed25519(tz1) => write!(f, "{}", tz1),
Self::Secp256k1(tz2) => write!(f, "{}", tz2),
Self::P256(tz3) => write!(f, "{}", tz3),
Self::Bls(tz4) => write!(f, "{}", tz4),
}
}
}
Expand All @@ -46,6 +49,8 @@ impl PublicKey {
PublicKey::Secp256k1(PublicKeySecp256k1::from_b58check(data)?)
} else if bytes.starts_with(HashType::PublicKeyP256.base58check_prefix()) {
PublicKey::P256(PublicKeyP256::from_b58check(data)?)
} else if bytes.starts_with(HashType::PublicKeyBls.base58check_prefix()) {
PublicKey::Bls(PublicKeyBls::from_b58check(data)?)
} else {
return Err(FromBase58CheckError::InvalidBase58);
};
Expand All @@ -58,6 +63,7 @@ impl PublicKey {
Self::Ed25519(tz1) => tz1.to_b58check(),
Self::Secp256k1(tz2) => tz2.to_b58check(),
Self::P256(tz3) => tz3.to_b58check(),
Self::Bls(tz4) => tz4.to_b58check(),
}
}
}
Expand All @@ -68,6 +74,7 @@ impl From<PublicKey> for Hash {
PublicKey::Ed25519(tz1) => tz1.into(),
PublicKey::Secp256k1(tz2) => tz2.into(),
PublicKey::P256(tz3) => tz3.into(),
PublicKey::Bls(tz4) => tz4.into(),
}
}
}
Expand Down Expand Up @@ -98,6 +105,12 @@ impl PublicKeySignatureVerifier for PublicKey {
secp256k1.verify_signature(&signature.try_into()?, msg)
}
PublicKey::P256(p256) => p256.verify_signature(&signature.try_into()?, msg),
#[cfg(feature = "bls")]
PublicKey::Bls(bls) => bls.verify_signature(&signature.try_into()?, msg),
#[cfg(not(feature = "bls"))]
PublicKey::Bls(_) => Err(CryptoError::Unsupported(
"bls feature disabled, tz4 signature verification not supported",
)),
}
}
}
Expand All @@ -110,13 +123,15 @@ impl crate::PublicKeyWithHash for PublicKey {
Self::Ed25519(pk) => Self::Hash::Ed25519(pk.pk_hash()),
Self::Secp256k1(pk) => Self::Hash::Secp256k1(pk.pk_hash()),
Self::P256(pk) => Self::Hash::P256(pk.pk_hash()),
Self::Bls(pk) => Self::Hash::Bls(pk.pk_hash()),
}
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::PublicKeyWithHash;

#[test]
fn tz1_b58check() {
Expand Down Expand Up @@ -157,6 +172,19 @@ mod test {
assert_eq!(tz3, &tz3_from_pk);
}

#[test]
fn tz4_b58check() {
let tz4 = "BLpk1yE462s3cPX5t2HhvGPg3HSEUgqLi9q9Knwx7mbN4VuhEsvBYFvEz5Eu9shR7vZRY1k5PCtV";

let public_key = PublicKey::from_b58check(tz4);

assert!(matches!(public_key, Ok(PublicKey::Bls(_))));

let tz4_from_pk = public_key.unwrap().to_b58check();

assert_eq!(tz4, &tz4_from_pk);
}

#[test]
fn tz1_encoding() {
let tz1 = "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK";
Expand Down Expand Up @@ -217,6 +245,29 @@ mod test {
assert_eq!(public_key, deserde_pk);
}

#[test]
fn tz4_encoding() {
let tz4 = "BLpk1yE462s3cPX5t2HhvGPg3HSEUgqLi9q9Knwx7mbN4VuhEsvBYFvEz5Eu9shR7vZRY1k5PCtV";

let public_key = PublicKey::from_b58check(tz4).expect("expected valid tz4 hash");
let expected_bytes = hex::decode("03b46a862ef09f7f994c3d3570464dd8ab39305161e27b144ef900fc57e92c9429878b81af9678478d63f2eb36b01b2418").unwrap();

let mut bin = Vec::new();
public_key
.bin_write(&mut bin)
.expect("serialization should work");

assert_eq!(expected_bytes, bin, "Unexpected serialisation");

let deserde_pk = NomReader::nom_read(bin.as_slice())
.expect("deserialization should work")
.1;

// Check tag encoding
assert_eq!(3_u8, bin[0]);
assert_eq!(public_key, deserde_pk);
}

#[test]
fn tz1_signature_signature_verification_succeeds() {
// sk: edsk3vifWnPCr8jXyhnt1YLa5KeNYTPfHENDq9gxqAA8ERkvEigYMe
Expand Down Expand Up @@ -304,4 +355,63 @@ mod test {
let result = tz3.verify_signature(&sig, &msg).unwrap();
assert!(!result);
}

#[cfg_attr(feature = "bls", test)]
#[cfg(feature = "bls")]
fn tz4_signature_signature_verification_succeeds() {
// sk: BLsk2wHXLW6gN9sbEN2rU84mmCSNZKn9KRKrw74LwHqEaLGwL3qQ31
let tz4 = PublicKey::from_b58check(
"BLpk1yE462s3cPX5t2HhvGPg3HSEUgqLi9q9Knwx7mbN4VuhEsvBYFvEz5Eu9shR7vZRY1k5PCtV",
)
.expect("decoding public key should work");
let sig = Signature::from_base58_check(
"BLsig9WknWnGmPcJw1q9oCBr53UyjAWxxYNS5wz5HBKmCcuxCfK1Hwhs92YDFocvxUhXfUosgcTuzAEuAAjKzjy7isNhU3o2e8snmZyo9E85oRudCpM1MNtkeAAYEkSXUPLKtRYa9yFwni"
).expect("signature decoding should work");
let msg = b"hello, bls";
let result = tz4.verify_signature(&sig, msg).unwrap();
assert!(result);
}

#[cfg_attr(feature = "bls", test)]
#[cfg(feature = "bls")]
fn tz4_signature_signature_verification_fails() {
// sk: BLsk2wHXLW6gN9sbEN2rU84mmCSNZKn9KRKrw74LwHqEaLGwL3qQ31
let tz4 = PublicKey::from_b58check(
"BLpk1yE462s3cPX5t2HhvGPg3HSEUgqLi9q9Knwx7mbN4VuhEsvBYFvEz5Eu9shR7vZRY1k5PCtV",
)
.expect("decoding public key should work");
let sig = Signature::from_base58_check(
"BLsig9WknWnGmPcJw1q9oCBr53UyjAWxxYNS5wz5HBKmCcuxCfK1Hwhs92YDFocvxUhXfUosgcTuzAEuAAjKzjy7isNhU3o2e8snmZyo9E85oRudCpM1MNtkeAAYEkSXUPLKtRYa9yFwni"
).expect("signature decoding should work");
let msg = b"not the correct message";
let result = tz4.verify_signature(&sig, msg).unwrap();
assert!(!result);
}

#[test]
fn pk_hash() {
let test_hash = |pk, pkh| {
let pk = PublicKey::from_b58check(pk).unwrap();
let hash = pk.pk_hash().to_b58check();

assert_eq!(hash, pkh);
};

test_hash(
"edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK",
"tz1QFD9WqLWZmmAuqnnTPPUjfauitYEWdshv",
);
test_hash(
"sppk7a2WEfU54QzcQZ2EMjihtcxLeRtNTVxHw4FW2e8W5kEJ8ZargSb",
"tz2DzfieD6mjjYYFqbGwotsW5ivfRogQE6c4",
);
test_hash(
"p2pk65p7HKSGvkMdeK5yckM2nmi59oGNw4ksqdcvwxxF3AV3hopkfGS",
"tz3SYfXSu1mJnKx37BcJvhHyRXM4gbHy8o8i",
);
test_hash(
"BLpk1yE462s3cPX5t2HhvGPg3HSEUgqLi9q9Knwx7mbN4VuhEsvBYFvEz5Eu9shR7vZRY1k5PCtV",
"tz4DWZXsrP3bdPaZ5B3M3iLVoRMAyxw9oKLH",
);
}
}
43 changes: 42 additions & 1 deletion crypto/src/public_key_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use tezos_data_encoding::encoding::HasEncoding;
use tezos_data_encoding::nom::NomReader;

use crate::base58::{FromBase58Check, FromBase58CheckError};
use crate::hash::{ContractTz1Hash, ContractTz2Hash, ContractTz3Hash, Hash, HashTrait, HashType};
use crate::hash::{
ContractTz1Hash, ContractTz2Hash, ContractTz3Hash, ContractTz4Hash, Hash, HashTrait, HashType,
};

/// Hash of Layer1 contract ids.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, HasEncoding, BinWriter, NomReader)]
Expand All @@ -21,6 +23,8 @@ pub enum PublicKeyHash {
Secp256k1(ContractTz2Hash),
/// Tz3-contract
P256(ContractTz3Hash),
/// Tz4-contract
Bls(ContractTz4Hash),
}

impl Display for PublicKeyHash {
Expand All @@ -29,6 +33,7 @@ impl Display for PublicKeyHash {
Self::Ed25519(tz1) => write!(f, "{}", tz1),
Self::Secp256k1(tz2) => write!(f, "{}", tz2),
Self::P256(tz3) => write!(f, "{}", tz3),
Self::Bls(tz4) => write!(f, "{}", tz4),
}
}
}
Expand All @@ -47,6 +52,9 @@ impl PublicKeyHash {
_ if bytes.starts_with(HashType::ContractTz3Hash.base58check_prefix()) => {
Ok(PublicKeyHash::P256(ContractTz3Hash::from_b58check(data)?))
}
_ if bytes.starts_with(HashType::ContractTz4Hash.base58check_prefix()) => {
Ok(PublicKeyHash::Bls(ContractTz4Hash::from_b58check(data)?))
}
_ => Err(FromBase58CheckError::InvalidBase58),
}
}
Expand All @@ -57,6 +65,7 @@ impl PublicKeyHash {
Self::Ed25519(tz1) => tz1.to_b58check(),
Self::Secp256k1(tz2) => tz2.to_b58check(),
Self::P256(tz3) => tz3.to_b58check(),
Self::Bls(tz4) => tz4.to_b58check(),
}
}
}
Expand All @@ -67,6 +76,7 @@ impl From<PublicKeyHash> for Hash {
PublicKeyHash::Ed25519(tz1) => tz1.into(),
PublicKeyHash::Secp256k1(tz2) => tz2.into(),
PublicKeyHash::P256(tz3) => tz3.into(),
PublicKeyHash::Bls(tz4) => tz4.into(),
}
}
}
Expand Down Expand Up @@ -122,6 +132,19 @@ mod test {
assert_eq!(tz3, &tz3_from_pkh);
}

#[test]
fn tz4_b58check() {
let tz4 = "tz4DWZXsrP3bdPaZ5B3M3iLVoRMAyxw9oKLH";

let pkh = PublicKeyHash::from_b58check(tz4);

assert!(matches!(pkh, Ok(PublicKeyHash::Bls(_))));

let tz4_from_pkh = pkh.unwrap().to_b58check();

assert_eq!(tz4, &tz4_from_pkh);
}

#[test]
fn tz1_encoding() {
let tz1 = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx";
Expand Down Expand Up @@ -175,4 +198,22 @@ mod test {
assert_eq!(2_u8, bin[0]);
assert_eq!(pkh, deserde_pkh);
}

#[test]
fn tz4_encoding() {
let tz4 = "tz4DWZXsrP3bdPaZ5B3M3iLVoRMAyxw9oKLH";

let pkh = PublicKeyHash::from_b58check(tz4).expect("expected valid tz4 hash");

let mut bin = Vec::new();
pkh.bin_write(&mut bin).expect("serialization should work");

let deserde_pkh = NomReader::nom_read(bin.as_slice())
.expect("deserialization should work")
.1;

// Check tag encoding
assert_eq!(3_u8, bin[0]);
assert_eq!(pkh, deserde_pkh);
}
}

0 comments on commit c19b326

Please sign in to comment.