forked from tezedge/tezedge
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #73 from trilitech/emturner@ukowkonlwkpl
crypto: add PublicKey(Hash) types from SDK
- Loading branch information
Showing
4 changed files
with
480 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
// Copyright (c) SimpleStaking, Viable Systems and Tezedge Contributors | ||
// SPDX-CopyrightText: 2023 TriliTech <[email protected]> | ||
// SPDX-CopyrightText: 2023-2024 TriliTech <[email protected]> | ||
// SPDX-CopyrightText: 2023 Nomadic Labs <[email protected]> | ||
// | ||
// SPDX-License-Identifier: MIT | ||
#![forbid(unsafe_code)] | ||
#![cfg_attr(feature = "fuzzing", feature(no_coverage))] | ||
|
@@ -14,6 +15,8 @@ pub mod base58; | |
pub mod bls; | ||
#[macro_use] | ||
pub mod hash; | ||
pub mod public_key; | ||
pub mod public_key_hash; | ||
pub mod signature; | ||
|
||
#[derive(Debug, Error)] | ||
|
@@ -38,6 +41,8 @@ pub enum CryptoError { | |
AlgorithmError(String), | ||
#[error("Ed25519 error: {0}")] | ||
Ed25519(ed25519_dalek::SignatureError), | ||
#[error("Incorrect signature type: {0}")] | ||
SignatureType(#[from] signature::TryFromSignatureError), | ||
} | ||
|
||
/// Public key that support hashing. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
// SPDX-FileCopyrightText: 2023 Marigold <[email protected]> | ||
// SPDX-FileCopyrightText: 2024 Trilitech <[email protected]> | ||
// | ||
// SPDX-License-Identifier: MIT | ||
|
||
//! Public Key of Layer1. | ||
|
||
use crate::base58::{FromBase58Check, FromBase58CheckError}; | ||
use crate::hash::{Hash, HashTrait, HashType}; | ||
use crate::hash::{PublicKeyEd25519, PublicKeyP256, PublicKeySecp256k1}; | ||
use crate::signature::Signature; | ||
use crate::{CryptoError, PublicKeySignatureVerifier}; | ||
use std::fmt::Display; | ||
use tezos_data_encoding::enc::BinWriter; | ||
use tezos_data_encoding::encoding::HasEncoding; | ||
use tezos_data_encoding::nom::NomReader; | ||
|
||
/// Public Key of Layer1. | ||
#[derive(Debug, Clone, PartialEq, Eq, HasEncoding, BinWriter, NomReader)] | ||
pub enum PublicKey { | ||
/// Tz1 - public key | ||
Ed25519(PublicKeyEd25519), | ||
/// Tz2 - public key | ||
Secp256k1(PublicKeySecp256k1), | ||
/// Tz3 - public key | ||
P256(PublicKeyP256), | ||
} | ||
|
||
impl Display for PublicKey { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Self::Ed25519(tz1) => write!(f, "{}", tz1), | ||
Self::Secp256k1(tz2) => write!(f, "{}", tz2), | ||
Self::P256(tz3) => write!(f, "{}", tz3), | ||
} | ||
} | ||
} | ||
|
||
impl PublicKey { | ||
/// Conversion from base58-encoding string (with prefix). | ||
pub fn from_b58check(data: &str) -> Result<Self, FromBase58CheckError> { | ||
let bytes = data.from_base58check()?; | ||
let public_key = if bytes.starts_with(HashType::PublicKeyEd25519.base58check_prefix()) { | ||
PublicKey::Ed25519(PublicKeyEd25519::from_b58check(data)?) | ||
} else if bytes.starts_with(HashType::PublicKeySecp256k1.base58check_prefix()) { | ||
PublicKey::Secp256k1(PublicKeySecp256k1::from_b58check(data)?) | ||
} else if bytes.starts_with(HashType::PublicKeyP256.base58check_prefix()) { | ||
PublicKey::P256(PublicKeyP256::from_b58check(data)?) | ||
} else { | ||
return Err(FromBase58CheckError::InvalidBase58); | ||
}; | ||
Ok(public_key) | ||
} | ||
|
||
/// Conversion to base58-encoding string (with prefix). | ||
pub fn to_b58check(&self) -> String { | ||
match self { | ||
Self::Ed25519(tz1) => tz1.to_b58check(), | ||
Self::Secp256k1(tz2) => tz2.to_b58check(), | ||
Self::P256(tz3) => tz3.to_b58check(), | ||
} | ||
} | ||
} | ||
|
||
impl From<PublicKey> for Hash { | ||
fn from(pkh: PublicKey) -> Self { | ||
match pkh { | ||
PublicKey::Ed25519(tz1) => tz1.into(), | ||
PublicKey::Secp256k1(tz2) => tz2.into(), | ||
PublicKey::P256(tz3) => tz3.into(), | ||
} | ||
} | ||
} | ||
|
||
impl TryFrom<&str> for PublicKey { | ||
type Error = FromBase58CheckError; | ||
|
||
fn try_from(value: &str) -> Result<Self, Self::Error> { | ||
Self::from_b58check(value) | ||
} | ||
} | ||
|
||
impl PublicKeySignatureVerifier for PublicKey { | ||
type Signature = Signature; | ||
type Error = CryptoError; | ||
|
||
// TODO: This can be made more effecient by avoiding a clone of the internal buffer. | ||
// | ||
fn verify_signature( | ||
&self, | ||
signature: &Self::Signature, | ||
msg: &[u8], | ||
) -> Result<bool, Self::Error> { | ||
let signature = signature.clone(); | ||
match self { | ||
PublicKey::Ed25519(ed25519) => ed25519.verify_signature(&signature.try_into()?, msg), | ||
PublicKey::Secp256k1(secp256k1) => { | ||
secp256k1.verify_signature(&signature.try_into()?, msg) | ||
} | ||
PublicKey::P256(p256) => p256.verify_signature(&signature.try_into()?, msg), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn tz1_b58check() { | ||
let tz1 = "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK"; | ||
|
||
let pkh = PublicKey::from_b58check(tz1); | ||
|
||
assert!(matches!(pkh, Ok(PublicKey::Ed25519(_)))); | ||
|
||
let tz1_from_pkh = pkh.unwrap().to_b58check(); | ||
|
||
assert_eq!(tz1, &tz1_from_pkh); | ||
} | ||
|
||
#[test] | ||
fn tz2_b58check() { | ||
let tz2 = "sppk7Zik17H7AxECMggqD1FyXUQdrGRFtz9X7aR8W2BhaJoWwSnPEGA"; | ||
|
||
let public_key = PublicKey::from_b58check(tz2); | ||
|
||
assert!(matches!(public_key, Ok(PublicKey::Secp256k1(_)))); | ||
|
||
let tz2_from_pk = public_key.unwrap().to_b58check(); | ||
|
||
assert_eq!(tz2, &tz2_from_pk); | ||
} | ||
|
||
#[test] | ||
fn tz3_b58check() { | ||
let tz3 = "p2pk67VpBjWwoPULwXCpayec6rFxaAKv8VjJ8cVMHmLDCYARu31zx5Z"; | ||
|
||
let public_key = PublicKey::from_b58check(tz3); | ||
|
||
assert!(matches!(public_key, Ok(PublicKey::P256(_)))); | ||
|
||
let tz3_from_pk = public_key.unwrap().to_b58check(); | ||
|
||
assert_eq!(tz3, &tz3_from_pk); | ||
} | ||
|
||
// #[test] | ||
// fn tz1_encoding() { | ||
// let tz1 = "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK"; | ||
|
||
// let public_key = PublicKey::from_b58check(tz1).expect("expected valid tz1 hash"); | ||
|
||
// let mut bin = Vec::new(); | ||
// public_key | ||
// .bin_write(&mut bin) | ||
// .expect("serialization should work"); | ||
|
||
// let deserde_pk = NomReader::nom_read(bin.as_slice()) | ||
// .expect("deserialization should work") | ||
// .1; | ||
|
||
// // Check tag encoding | ||
// assert_eq!(0_u8, bin[0]); | ||
// assert_eq!(public_key, deserde_pk); | ||
// } | ||
|
||
// #[test] | ||
// fn tz2_encoding() { | ||
// let tz2 = "sppk7Zik17H7AxECMggqD1FyXUQdrGRFtz9X7aR8W2BhaJoWwSnPEGA"; | ||
|
||
// let public_key = PublicKey::from_b58check(tz2).expect("expected valid tz2 hash"); | ||
|
||
// let mut bin = Vec::new(); | ||
// public_key | ||
// .bin_write(&mut bin) | ||
// .expect("serialization should work"); | ||
|
||
// let deserde_pk = NomReader::nom_read(bin.as_slice()) | ||
// .expect("deserialization should work") | ||
// .1; | ||
|
||
// // Check tag encoding | ||
// assert_eq!(1_u8, bin[0]); | ||
// assert_eq!(public_key, deserde_pk); | ||
// } | ||
|
||
// #[test] | ||
// fn tz3_encoding() { | ||
// let tz3 = "p2pk67VpBjWwoPULwXCpayec6rFxaAKv8VjJ8cVMHmLDCYARu31zx5Z"; | ||
|
||
// let public_key = PublicKey::from_b58check(tz3).expect("expected valid tz3 hash"); | ||
|
||
// let mut bin = Vec::new(); | ||
// public_key | ||
// .bin_write(&mut bin) | ||
// .expect("serialization should work"); | ||
|
||
// let deserde_pk = NomReader::nom_read(bin.as_slice()) | ||
// .expect("deserialization should work") | ||
// .1; | ||
|
||
// // Check tag encoding | ||
// assert_eq!(2_u8, bin[0]); | ||
// assert_eq!(public_key, deserde_pk); | ||
// } | ||
|
||
#[test] | ||
fn tz1_signature_signature_verification_succeeds() { | ||
// sk: edsk3vifWnPCr8jXyhnt1YLa5KeNYTPfHENDq9gxqAA8ERkvEigYMe | ||
let tz1 = | ||
PublicKey::from_b58check("edpkurrsBe7UjF59ciHHmBRnS76WHx3YNL9m7owYta6ticPrdP9DG4") | ||
.expect("public key decoding should work"); | ||
let sig: Signature = Signature::from_base58_check( | ||
"edsigtoeXp3xFtGugwCTDSDuifQ9Ka81X4gXFoxRQ6Xao2Ryc3yioptrKMfNy5c9pHhbA9Xn3sYZdx2SPiCGTFXjjXx9xKCPDoq" | ||
).expect("signature decoding should work"); | ||
|
||
let msg = b"hello, world"; | ||
|
||
let result = tz1 | ||
.verify_signature(&sig, msg) | ||
.expect("signature should be correct"); | ||
assert!(result); | ||
} | ||
|
||
#[test] | ||
fn tz1_signature_signature_verification_fails() { | ||
let tz1 = | ||
PublicKey::from_b58check("edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK") | ||
.expect("public key decoding should work"); | ||
let sig = Signature::from_base58_check( | ||
"sigdGBG68q2vskMuac4AzyNb1xCJTfuU8MiMbQtmZLUCYydYrtTd5Lessn1EFLTDJzjXoYxRasZxXbx6tHnirbEJtikcMHt3" | ||
).expect("signature decoding should work"); | ||
let msg = hex::decode("bcbb7b77cb0712e4cd02160308cfd53e8dde8a7980c4ff28b62deb12304913c2") | ||
.expect("payload decoding should work"); | ||
|
||
let result = tz1.verify_signature(&sig, &msg); | ||
assert!(result.is_err()); | ||
} | ||
|
||
#[test] | ||
fn tz2_signature_signature_verification_succeeds() { | ||
let tz2 = | ||
PublicKey::from_b58check("sppk7cwkTzCPptCSxSTvGNg4uqVcuTbyWooLnJp4yxJNH5DReUGxYvs") | ||
.expect("public key decoding should work"); | ||
let sig = Signature::from_base58_check("sigrJ2jqanLupARzKGvzWgL1Lv6NGUqDovHKQg9MX4PtNtHXgcvG6131MRVzujJEXfvgbuRtfdGbXTFaYJJjuUVLNNZTf5q1").expect("signature decoding should work"); | ||
let msg = hex::decode("5538e2cc90c9b053a12e2d2f3a985aff1809eac59501db4d644e4bb381b06b4b") | ||
.expect("payload decoding should work"); | ||
|
||
let result = tz2.verify_signature(&sig, &msg).unwrap(); | ||
assert!(result); | ||
} | ||
|
||
#[test] | ||
fn tz2_signature_signature_verification_fails() { | ||
let tz2 = "sppk7Zik17H7AxECMggqD1FyXUQdrGRFtz9X7aR8W2BhaJoWwSnPEGA"; | ||
let tz2 = PublicKey::from_b58check(tz2).expect("parsing should world"); | ||
let sig = Signature::from_base58_check("sigrJ2jqanLupARzKGvzWgL1Lv6NGUqDovHKQg9MX4PtNtHXgcvG6131MRVzujJEXfvgbuRtfdGbXTFaYJJjuUVLNNZTf5q1").expect("signature decoding should work"); | ||
let msg = hex::decode("5538e2cc90c9b053a12e2d2f3a985aff1809eac59501db4d644e4bb381b06b4b") | ||
.expect("payload decoding should work"); | ||
|
||
let result = tz2.verify_signature(&sig, &msg).unwrap(); | ||
assert!(!result); | ||
} | ||
|
||
#[test] | ||
fn tz3_signature_signature_verification_succeeds() { | ||
let tz3 = | ||
PublicKey::from_b58check("p2pk67Cwb5Ke6oSmqeUbJxURXMe3coVnH9tqPiB2xD84CYhHbBKs4oM") | ||
.expect("decoding public key should work"); | ||
let sig = Signature::from_base58_check( | ||
"sigNCaj9CnmD94eZH9C7aPPqBbVCJF72fYmCFAXqEbWfqE633WNFWYQJFnDUFgRUQXR8fQ5tKSfJeTe6UAi75eTzzQf7AEc1" | ||
).expect("signature decoding should work"); | ||
let msg = hex::decode("5538e2cc90c9b053a12e2d2f3a985aff1809eac59501db4d644e4bb381b06b4b") | ||
.expect("payload decoding should work"); | ||
|
||
let result = tz3.verify_signature(&sig, &msg).unwrap(); | ||
assert!(result); | ||
} | ||
|
||
#[test] | ||
fn tz3_signature_signature_verification_fails() { | ||
let tz3 = | ||
PublicKey::from_b58check("p2pk67VpBjWwoPULwXCpayec6rFxaAKv8VjJ8cVMHmLDCYARu31zx5Z") | ||
.expect("decoding public key should work"); | ||
let sig = Signature::from_base58_check( | ||
"sigNCaj9CnmD94eZH9C7aPPqBbVCJF72fYmCFAXqEbWfqE633WNFWYQJFnDUFgRUQXR8fQ5tKSfJeTe6UAi75eTzzQf7AEc1" | ||
).expect("signature decoding should work"); | ||
let msg = hex::decode("5538e2cc90c9b053a12e2d2f3a985aff1809eac59501db4d644e4bb381b06b4b") | ||
.expect("payload decoding should work"); | ||
|
||
let result = tz3.verify_signature(&sig, &msg).unwrap(); | ||
assert!(!result); | ||
} | ||
} |
Oops, something went wrong.