diem_types/transaction/
authenticator.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::account_address::AccountAddress;
9use anyhow::{ensure, Error, Result};
10use diem_crypto::{
11    bls::{
12        BLSPublicKey, BLSPublicKeyUnchecked, BLSSignature,
13        BLSSignatureUnchecked,
14    },
15    ed25519::{Ed25519PublicKey, Ed25519Signature},
16    hash::CryptoHash,
17    multi_bls::MultiBLSSignature,
18    multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature},
19    traits::Signature,
20    CryptoMaterialError, HashValue, ValidCryptoMaterial,
21    ValidCryptoMaterialStringExt,
22};
23use diem_crypto_derive::{CryptoHasher, DeserializeKey, SerializeKey};
24#[cfg(any(test, feature = "fuzzing"))]
25use proptest_derive::Arbitrary;
26use rand::{rngs::OsRng, Rng};
27use serde::{Deserialize, Serialize};
28use std::{convert::TryFrom, fmt, str::FromStr};
29
30/// A `TransactionAuthenticator` is an abstraction of a signature scheme. It
31/// must know: (1) How to check its signature against a message and public key
32/// (2) How to convert its public key into an `AuthenticationKeyPreimage`
33/// structured as (public_key | signaure_scheme_id).
34/// Each on-chain `DiemAccount` must store an `AuthenticationKey` (computed via
35/// a sha3 hash of an `AuthenticationKeyPreimage`).
36/// Each transaction submitted to the Diem blockchain contains a
37/// `TransactionAuthenticator`. During transaction execution, the executor will
38/// check if the `TransactionAuthenticator`'s signature on the transaction hash
39/// is well-formed (1) and whether the sha3 hash of the
40/// `TransactionAuthenticator`'s `AuthenticationKeyPreimage` matches the
41/// `AuthenticationKey` stored under the transaction's sender account address
42/// (2).
43
44// TODO: in the future, can tie these to the TransactionAuthenticator enum directly with https://github.com/rust-lang/rust/issues/60553
45#[derive(Debug)]
46#[repr(u8)]
47pub enum Scheme {
48    Ed25519 = 0,
49    MultiEd25519 = 1,
50    BLS = 2,
51    MultiBLS = 3,
52    // ... add more schemes here
53}
54
55impl fmt::Display for Scheme {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        let display = match self {
58            Scheme::Ed25519 => "Ed25519",
59            Scheme::MultiEd25519 => "MultiEd25519",
60            Scheme::BLS => "bls",
61            Scheme::MultiBLS => "multi_bls",
62        };
63        write!(f, "Scheme::{}", display)
64    }
65}
66
67#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
68pub enum TransactionAuthenticator {
69    /// Single signature
70    Ed25519 {
71        public_key: Ed25519PublicKey,
72        signature: Ed25519Signature,
73    },
74    /// K-of-N multisignature
75    MultiEd25519 {
76        public_key: MultiEd25519PublicKey,
77        signature: MultiEd25519Signature,
78    },
79    /// BLS signature
80    BLS {
81        public_key: BLSPublicKey,
82        signature: BLSSignature,
83    },
84    MultiBLS {
85        signature: MultiBLSSignature,
86    }, // ... add more schemes here
87}
88
89#[derive(Deserialize)]
90pub enum TransactionAuthenticatorUnchecked {
91    Ed25519 {
92        public_key: Ed25519PublicKey,
93        signature: Ed25519Signature,
94    },
95    MultiEd25519 {
96        public_key: MultiEd25519PublicKey,
97        signature: MultiEd25519Signature,
98    },
99    BLS {
100        public_key: BLSPublicKeyUnchecked,
101        signature: BLSSignatureUnchecked,
102    },
103    MultiBLS {
104        signature: MultiBLSSignature,
105    },
106}
107
108impl From<TransactionAuthenticatorUnchecked> for TransactionAuthenticator {
109    fn from(t: TransactionAuthenticatorUnchecked) -> Self {
110        match t {
111            TransactionAuthenticatorUnchecked::BLS {
112                public_key,
113                signature,
114            } => Self::BLS {
115                public_key: public_key.into(),
116                signature: signature.into(),
117            },
118            TransactionAuthenticatorUnchecked::MultiBLS { signature } => {
119                Self::MultiBLS { signature }
120            }
121            TransactionAuthenticatorUnchecked::Ed25519 {
122                public_key,
123                signature,
124            } => Self::Ed25519 {
125                public_key,
126                signature,
127            },
128            TransactionAuthenticatorUnchecked::MultiEd25519 {
129                public_key,
130                signature,
131            } => Self::MultiEd25519 {
132                public_key,
133                signature,
134            },
135        }
136    }
137}
138
139impl TransactionAuthenticator {
140    /// Unique identifier for the signature scheme
141    pub fn scheme(&self) -> Scheme {
142        match self {
143            Self::Ed25519 { .. } => Scheme::Ed25519,
144            Self::MultiEd25519 { .. } => Scheme::MultiEd25519,
145            Self::BLS { .. } => Scheme::BLS,
146            Self::MultiBLS { .. } => Scheme::MultiBLS,
147        }
148    }
149
150    /// Create a single-signature ed25519 authenticator
151    pub fn ed25519(
152        public_key: Ed25519PublicKey, signature: Ed25519Signature,
153    ) -> Self {
154        Self::Ed25519 {
155            public_key,
156            signature,
157        }
158    }
159
160    /// Create a multisignature ed25519 authenticator
161    pub fn multi_ed25519(
162        public_key: MultiEd25519PublicKey, signature: MultiEd25519Signature,
163    ) -> Self {
164        Self::MultiEd25519 {
165            public_key,
166            signature,
167        }
168    }
169
170    pub fn bls(public_key: BLSPublicKey, signature: BLSSignature) -> Self {
171        Self::BLS {
172            public_key,
173            signature,
174        }
175    }
176
177    pub fn multi_bls(signature: MultiBLSSignature) -> Self {
178        Self::MultiBLS { signature }
179    }
180
181    /// Return Ok if the authenticator's public key matches its signature, Err
182    /// otherwise
183    pub fn verify<T: Serialize + CryptoHash>(&self, message: &T) -> Result<()> {
184        match self {
185            Self::Ed25519 {
186                public_key,
187                signature,
188            } => signature.verify(message, public_key),
189            Self::MultiEd25519 {
190                public_key,
191                signature,
192            } => signature.verify(message, public_key),
193            Self::BLS {
194                public_key,
195                signature,
196            } => signature.verify(message, public_key),
197            Self::MultiBLS { .. } => {
198                // we will verify this case in pos state
199                Ok(())
200            }
201        }
202    }
203
204    /// Return the raw bytes of `self.public_key`
205    pub fn public_key_bytes(&self) -> Vec<u8> {
206        match self {
207            Self::Ed25519 { public_key, .. } => public_key.to_bytes().to_vec(),
208            Self::MultiEd25519 { public_key, .. } => {
209                public_key.to_bytes().to_vec()
210            }
211            Self::BLS { public_key, .. } => public_key.to_bytes().to_vec(),
212            Self::MultiBLS { .. } => todo!(),
213        }
214    }
215
216    /// Return the raw bytes of `self.signature`
217    pub fn signature_bytes(&self) -> Vec<u8> {
218        match self {
219            Self::Ed25519 { signature, .. } => signature.to_bytes().to_vec(),
220            Self::MultiEd25519 { signature, .. } => {
221                signature.to_bytes().to_vec()
222            }
223            Self::BLS { signature, .. } => signature.to_bytes().to_vec(),
224            Self::MultiBLS { .. } => todo!(),
225        }
226    }
227
228    /// Return an authentication key preimage derived from `self`'s public key
229    /// and scheme id
230    pub fn authentication_key_preimage(&self) -> AuthenticationKeyPreimage {
231        AuthenticationKeyPreimage::new(self.public_key_bytes(), self.scheme())
232    }
233
234    /// Return an authentication key derived from `self`'s public key and scheme
235    /// id
236    pub fn authentication_key(&self) -> AuthenticationKey {
237        AuthenticationKey::from_preimage(&self.authentication_key_preimage())
238    }
239}
240
241/// A struct that represents an account authentication key. An account's address
242/// is the last 16 bytes of authentication key used to create it
243#[derive(
244    Clone,
245    Copy,
246    CryptoHasher,
247    Debug,
248    DeserializeKey,
249    Eq,
250    Hash,
251    Ord,
252    PartialEq,
253    PartialOrd,
254    SerializeKey,
255)]
256#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
257pub struct AuthenticationKey([u8; AuthenticationKey::LENGTH]);
258
259impl AuthenticationKey {
260    /// The number of bytes in an authentication key.
261    pub const LENGTH: usize = 32;
262
263    /// Create an authentication key from `bytes`
264    pub const fn new(bytes: [u8; Self::LENGTH]) -> Self { Self(bytes) }
265
266    /// Create an authentication key from a preimage by taking its sha3 hash
267    pub fn from_preimage(
268        preimage: &AuthenticationKeyPreimage,
269    ) -> AuthenticationKey {
270        AuthenticationKey::new(*HashValue::sha3_256_of(&preimage.0).as_ref())
271    }
272
273    /// Create an authentication key from an Ed25519 public key
274    pub fn ed25519(public_key: &Ed25519PublicKey) -> AuthenticationKey {
275        Self::from_preimage(&AuthenticationKeyPreimage::ed25519(public_key))
276    }
277
278    /// Create an authentication key from a MultiEd25519 public key
279    pub fn multi_ed25519(public_key: &MultiEd25519PublicKey) -> Self {
280        Self::from_preimage(&AuthenticationKeyPreimage::multi_ed25519(
281            public_key,
282        ))
283    }
284
285    /// Return an address derived from the last `AccountAddress::LENGTH` bytes
286    /// of this authentication key.
287    pub fn derived_address(&self) -> AccountAddress {
288        // keep only last 16 bytes
289        let mut array = [0u8; AccountAddress::LENGTH];
290        array.copy_from_slice(&self.0[Self::LENGTH - AccountAddress::LENGTH..]);
291        AccountAddress::new(array)
292    }
293
294    /// Return the first AccountAddress::LENGTH bytes of this authentication key
295    pub fn prefix(&self) -> [u8; AccountAddress::LENGTH] {
296        let mut array = [0u8; AccountAddress::LENGTH];
297        array.copy_from_slice(&self.0[..AccountAddress::LENGTH]);
298        array
299    }
300
301    /// Construct a vector from this authentication key
302    pub fn to_vec(&self) -> Vec<u8> { self.0.to_vec() }
303
304    /// Create a random authentication key. For testing only
305    pub fn random() -> Self {
306        let mut rng = OsRng;
307        let buf: [u8; Self::LENGTH] = rng.gen();
308        AuthenticationKey::new(buf)
309    }
310}
311
312impl ValidCryptoMaterial for AuthenticationKey {
313    fn to_bytes(&self) -> Vec<u8> { self.to_vec() }
314}
315
316/// A value that can be hashed to produce an authentication key
317pub struct AuthenticationKeyPreimage(Vec<u8>);
318
319impl AuthenticationKeyPreimage {
320    /// Return bytes for (public_key | scheme_id)
321    fn new(mut public_key_bytes: Vec<u8>, scheme: Scheme) -> Self {
322        public_key_bytes.push(scheme as u8);
323        Self(public_key_bytes)
324    }
325
326    /// Construct a preimage from an Ed25519 public key
327    pub fn ed25519(public_key: &Ed25519PublicKey) -> AuthenticationKeyPreimage {
328        Self::new(public_key.to_bytes().to_vec(), Scheme::Ed25519)
329    }
330
331    /// Construct a preimage from a MultiEd25519 public key
332    pub fn multi_ed25519(
333        public_key: &MultiEd25519PublicKey,
334    ) -> AuthenticationKeyPreimage {
335        Self::new(public_key.to_bytes(), Scheme::MultiEd25519)
336    }
337
338    /// Construct a vector from this authentication key
339    pub fn into_vec(self) -> Vec<u8> { self.0 }
340}
341
342impl fmt::Display for TransactionAuthenticator {
343    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344        write!(
345            f,
346            "TransactionAuthenticator[scheme id: {:?}, public key: {}, signature: {}]",
347            self.scheme(),
348            hex::encode(&self.public_key_bytes()),
349            hex::encode(&self.signature_bytes())
350        )
351    }
352}
353
354impl TryFrom<&[u8]> for AuthenticationKey {
355    type Error = CryptoMaterialError;
356
357    fn try_from(
358        bytes: &[u8],
359    ) -> std::result::Result<AuthenticationKey, CryptoMaterialError> {
360        if bytes.len() != Self::LENGTH {
361            return Err(CryptoMaterialError::WrongLengthError);
362        }
363        let mut addr = [0u8; Self::LENGTH];
364        addr.copy_from_slice(bytes);
365        Ok(AuthenticationKey(addr))
366    }
367}
368
369impl TryFrom<Vec<u8>> for AuthenticationKey {
370    type Error = CryptoMaterialError;
371
372    fn try_from(
373        bytes: Vec<u8>,
374    ) -> std::result::Result<AuthenticationKey, CryptoMaterialError> {
375        AuthenticationKey::try_from(&bytes[..])
376    }
377}
378
379impl FromStr for AuthenticationKey {
380    type Err = Error;
381
382    fn from_str(s: &str) -> Result<Self> {
383        ensure!(
384            !s.is_empty(),
385            "authentication key string should not be empty.",
386        );
387        let bytes_out = ::hex::decode(s)?;
388        let key = AuthenticationKey::try_from(bytes_out.as_slice())?;
389        Ok(key)
390    }
391}
392
393impl AsRef<[u8]> for AuthenticationKey {
394    fn as_ref(&self) -> &[u8] { &self.0 }
395}
396
397impl fmt::LowerHex for AuthenticationKey {
398    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399        write!(f, "{}", hex::encode(&self.0))
400    }
401}
402
403impl fmt::Display for AuthenticationKey {
404    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
405        // Forward to the LowerHex impl with a "0x" prepended (the # flag).
406        write!(f, "{:#x}", self)
407    }
408}
409
410#[cfg(test)]
411mod tests {
412    use crate::transaction::authenticator::AuthenticationKey;
413    use std::str::FromStr;
414
415    #[test]
416    fn test_from_str_should_not_panic_by_given_empty_string() {
417        assert!(AuthenticationKey::from_str("").is_err());
418    }
419}