tezos_smart_rollup_encoding/
public_key.rs

1// SPDX-FileCopyrightText: 2023 Marigold <contact@marigold.dev>
2//
3// SPDX-License-Identifier: MIT
4
5//! Public Key of Layer1.
6
7use std::fmt::Display;
8use tezos_crypto_rs::hash::{
9    PublicKeyEd25519, PublicKeyP256, PublicKeySecp256k1, Signature,
10};
11use tezos_crypto_rs::{CryptoError, PublicKeySignatureVerifier};
12use tezos_data_encoding::enc::BinWriter;
13use tezos_data_encoding::encoding::HasEncoding;
14use tezos_data_encoding::nom::NomReader;
15
16use crypto::base58::{FromBase58Check, FromBase58CheckError};
17use crypto::hash::{Hash, HashTrait, HashType};
18
19/// Public Key of Layer1.
20#[derive(Debug, Clone, PartialEq, Eq, HasEncoding, BinWriter, NomReader)]
21pub enum PublicKey {
22    /// Tz1 - public key
23    Ed25519(PublicKeyEd25519),
24    /// Tz2 - public key
25    Secp256k1(PublicKeySecp256k1),
26    /// Tz3 - public key
27    P256(PublicKeyP256),
28}
29
30impl Display for PublicKey {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            Self::Ed25519(tz1) => write!(f, "{}", tz1),
34            Self::Secp256k1(tz2) => write!(f, "{}", tz2),
35            Self::P256(tz3) => write!(f, "{}", tz3),
36        }
37    }
38}
39
40impl PublicKey {
41    /// Conversion from base58-encoding string (with prefix).
42    pub fn from_b58check(data: &str) -> Result<Self, FromBase58CheckError> {
43        let bytes = data.from_base58check()?;
44        let public_key = if bytes
45            .starts_with(HashType::PublicKeyEd25519.base58check_prefix())
46        {
47            PublicKey::Ed25519(PublicKeyEd25519::from_b58check(data)?)
48        } else if bytes.starts_with(HashType::PublicKeySecp256k1.base58check_prefix()) {
49            PublicKey::Secp256k1(PublicKeySecp256k1::from_b58check(data)?)
50        } else if bytes.starts_with(HashType::PublicKeyP256.base58check_prefix()) {
51            PublicKey::P256(PublicKeyP256::from_b58check(data)?)
52        } else {
53            return Err(FromBase58CheckError::InvalidBase58);
54        };
55        Ok(public_key)
56    }
57
58    /// Conversion to base58-encoding string (with prefix).
59    pub fn to_b58check(&self) -> String {
60        match self {
61            Self::Ed25519(tz1) => tz1.to_b58check(),
62            Self::Secp256k1(tz2) => tz2.to_b58check(),
63            Self::P256(tz3) => tz3.to_b58check(),
64        }
65    }
66}
67
68impl From<PublicKey> for Hash {
69    fn from(pkh: PublicKey) -> Self {
70        match pkh {
71            PublicKey::Ed25519(tz1) => tz1.into(),
72            PublicKey::Secp256k1(tz2) => tz2.into(),
73            PublicKey::P256(tz3) => tz3.into(),
74        }
75    }
76}
77
78impl TryFrom<&str> for PublicKey {
79    type Error = FromBase58CheckError;
80
81    fn try_from(value: &str) -> Result<Self, Self::Error> {
82        Self::from_b58check(value)
83    }
84}
85
86impl PublicKeySignatureVerifier for PublicKey {
87    type Signature = Signature;
88    type Error = CryptoError;
89
90    fn verify_signature(
91        &self,
92        signature: &Self::Signature,
93        msg: &[u8],
94    ) -> Result<bool, Self::Error> {
95        match self {
96            PublicKey::Ed25519(ed25519) => ed25519.verify_signature(signature, msg),
97            PublicKey::Secp256k1(secp256k1) => secp256k1.verify_signature(signature, msg),
98            PublicKey::P256(p256) => p256.verify_signature(signature, msg),
99        }
100    }
101}
102
103#[cfg(test)]
104mod test {
105    use super::*;
106
107    #[test]
108    fn tz1_b58check() {
109        let tz1 = "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK";
110
111        let pkh = PublicKey::from_b58check(tz1);
112
113        assert!(matches!(pkh, Ok(PublicKey::Ed25519(_))));
114
115        let tz1_from_pkh = pkh.unwrap().to_b58check();
116
117        assert_eq!(tz1, &tz1_from_pkh);
118    }
119
120    #[test]
121    fn tz2_b58check() {
122        let tz2 = "sppk7Zik17H7AxECMggqD1FyXUQdrGRFtz9X7aR8W2BhaJoWwSnPEGA";
123
124        let public_key = PublicKey::from_b58check(tz2);
125
126        assert!(matches!(public_key, Ok(PublicKey::Secp256k1(_))));
127
128        let tz2_from_pk = public_key.unwrap().to_b58check();
129
130        assert_eq!(tz2, &tz2_from_pk);
131    }
132
133    #[test]
134    fn tz3_b58check() {
135        let tz3 = "p2pk67VpBjWwoPULwXCpayec6rFxaAKv8VjJ8cVMHmLDCYARu31zx5Z";
136
137        let public_key = PublicKey::from_b58check(tz3);
138
139        assert!(matches!(public_key, Ok(PublicKey::P256(_))));
140
141        let tz3_from_pk = public_key.unwrap().to_b58check();
142
143        assert_eq!(tz3, &tz3_from_pk);
144    }
145
146    #[test]
147    fn tz1_encoding() {
148        let tz1 = "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK";
149
150        let public_key = PublicKey::from_b58check(tz1).expect("expected valid tz1 hash");
151
152        let mut bin = Vec::new();
153        public_key
154            .bin_write(&mut bin)
155            .expect("serialization should work");
156
157        let deserde_pk = NomReader::nom_read(bin.as_slice())
158            .expect("deserialization should work")
159            .1;
160
161        // Check tag encoding
162        assert_eq!(0_u8, bin[0]);
163        assert_eq!(public_key, deserde_pk);
164    }
165
166    #[test]
167    fn tz2_encoding() {
168        let tz2 = "sppk7Zik17H7AxECMggqD1FyXUQdrGRFtz9X7aR8W2BhaJoWwSnPEGA";
169
170        let public_key = PublicKey::from_b58check(tz2).expect("expected valid tz2 hash");
171
172        let mut bin = Vec::new();
173        public_key
174            .bin_write(&mut bin)
175            .expect("serialization should work");
176
177        let deserde_pk = NomReader::nom_read(bin.as_slice())
178            .expect("deserialization should work")
179            .1;
180
181        // Check tag encoding
182        assert_eq!(1_u8, bin[0]);
183        assert_eq!(public_key, deserde_pk);
184    }
185
186    #[test]
187    fn tz3_encoding() {
188        let tz3 = "p2pk67VpBjWwoPULwXCpayec6rFxaAKv8VjJ8cVMHmLDCYARu31zx5Z";
189
190        let public_key = PublicKey::from_b58check(tz3).expect("expected valid tz3 hash");
191
192        let mut bin = Vec::new();
193        public_key
194            .bin_write(&mut bin)
195            .expect("serialization should work");
196
197        let deserde_pk = NomReader::nom_read(bin.as_slice())
198            .expect("deserialization should work")
199            .1;
200
201        // Check tag encoding
202        assert_eq!(2_u8, bin[0]);
203        assert_eq!(public_key, deserde_pk);
204    }
205
206    #[test]
207    fn tz1_signature_signature_verification_succeeds() {
208        let tz1 = PublicKey::from_b58check(
209            "edpkvWR5truf7AMF3PZVCXx7ieQLCW4MpNDzM3VwPfmFWVbBZwswBw",
210        )
211        .expect("public key decoding should work");
212        let sig: Signature = Signature::from_base58_check(
213            "sigdGBG68q2vskMuac4AzyNb1xCJTfuU8MiMbQtmZLUCYydYrtTd5Lessn1EFLTDJzjXoYxRasZxXbx6tHnirbEJtikcMHt3"
214        ).expect("signature decoding should work");
215        let msg = hex::decode(
216            "bcbb7b77cb0712e4cd02160308cfd53e8dde8a7980c4ff28b62deb12304913c2",
217        )
218        .expect("payload decoding should work");
219
220        let result = tz1
221            .verify_signature(&sig, &msg)
222            .expect("signature should be correct");
223        assert!(result);
224    }
225
226    #[test]
227    fn tz1_signature_signature_verification_fails() {
228        let tz1 = PublicKey::from_b58check(
229            "edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK",
230        )
231        .expect("public key decoding should work");
232        let sig = Signature::from_base58_check(
233            "sigdGBG68q2vskMuac4AzyNb1xCJTfuU8MiMbQtmZLUCYydYrtTd5Lessn1EFLTDJzjXoYxRasZxXbx6tHnirbEJtikcMHt3"
234        ).expect("signature decoding should work");
235        let msg = hex::decode(
236            "bcbb7b77cb0712e4cd02160308cfd53e8dde8a7980c4ff28b62deb12304913c2",
237        )
238        .expect("payload decoding should work");
239
240        let result = tz1.verify_signature(&sig, &msg);
241        assert!(result.is_err());
242    }
243
244    #[test]
245    fn tz2_signature_signature_verification_succeeds() {
246        let tz2 = PublicKey::from_b58check(
247            "sppk7cwkTzCPptCSxSTvGNg4uqVcuTbyWooLnJp4yxJNH5DReUGxYvs",
248        )
249        .expect("public key decoding should work");
250        let sig = Signature::from_base58_check("sigrJ2jqanLupARzKGvzWgL1Lv6NGUqDovHKQg9MX4PtNtHXgcvG6131MRVzujJEXfvgbuRtfdGbXTFaYJJjuUVLNNZTf5q1").expect("signature decoding should work");
251        let msg = hex::decode(
252            "5538e2cc90c9b053a12e2d2f3a985aff1809eac59501db4d644e4bb381b06b4b",
253        )
254        .expect("payload decoding should work");
255
256        let result = tz2.verify_signature(&sig, &msg).unwrap();
257        assert!(result);
258    }
259
260    #[test]
261    fn tz2_signature_signature_verification_fails() {
262        let tz2 = "sppk7Zik17H7AxECMggqD1FyXUQdrGRFtz9X7aR8W2BhaJoWwSnPEGA";
263        let tz2 = PublicKey::from_b58check(tz2).expect("parsing should world");
264        let sig = Signature::from_base58_check("sigrJ2jqanLupARzKGvzWgL1Lv6NGUqDovHKQg9MX4PtNtHXgcvG6131MRVzujJEXfvgbuRtfdGbXTFaYJJjuUVLNNZTf5q1").expect("signature decoding should work");
265        let msg = hex::decode(
266            "5538e2cc90c9b053a12e2d2f3a985aff1809eac59501db4d644e4bb381b06b4b",
267        )
268        .expect("payload decoding should work");
269
270        let result = tz2.verify_signature(&sig, &msg).unwrap();
271        assert!(!result);
272    }
273
274    #[test]
275    fn tz3_signature_signature_verification_succeeds() {
276        let tz3 = PublicKey::from_b58check(
277            "p2pk67Cwb5Ke6oSmqeUbJxURXMe3coVnH9tqPiB2xD84CYhHbBKs4oM",
278        )
279        .expect("decoding public key should work");
280        let sig = Signature::from_base58_check(
281            "sigNCaj9CnmD94eZH9C7aPPqBbVCJF72fYmCFAXqEbWfqE633WNFWYQJFnDUFgRUQXR8fQ5tKSfJeTe6UAi75eTzzQf7AEc1"
282        ).expect("signature decoding should work");
283        let msg = hex::decode(
284            "5538e2cc90c9b053a12e2d2f3a985aff1809eac59501db4d644e4bb381b06b4b",
285        )
286        .expect("payload decoding should work");
287
288        let result = tz3.verify_signature(&sig, &msg).unwrap();
289        assert!(result);
290    }
291
292    #[test]
293    fn tz3_signature_signature_verification_fails() {
294        let tz3 = PublicKey::from_b58check(
295            "p2pk67VpBjWwoPULwXCpayec6rFxaAKv8VjJ8cVMHmLDCYARu31zx5Z",
296        )
297        .expect("decoding public key should work");
298        let sig = Signature::from_base58_check(
299            "sigNCaj9CnmD94eZH9C7aPPqBbVCJF72fYmCFAXqEbWfqE633WNFWYQJFnDUFgRUQXR8fQ5tKSfJeTe6UAi75eTzzQf7AEc1"
300        ).expect("signature decoding should work");
301        let msg = hex::decode(
302            "5538e2cc90c9b053a12e2d2f3a985aff1809eac59501db4d644e4bb381b06b4b",
303        )
304        .expect("payload decoding should work");
305
306        let result = tz3.verify_signature(&sig, &msg).unwrap();
307        assert!(!result);
308    }
309}