Skip to main content

diem_types/transaction/
authenticator.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    account_address::AccountAddress,
6    transaction::{RawTransaction, RawTransactionWithData},
7};
8use anyhow::{ensure, Error, Result};
9use diem_crypto::{
10    ed25519::{Ed25519PublicKey, Ed25519Signature},
11    hash::CryptoHash,
12    multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature},
13    traits::Signature,
14    validatable::Validatable,
15    CryptoMaterialError, HashValue, ValidCryptoMaterial, ValidCryptoMaterialStringExt,
16};
17use diem_crypto_derive::{CryptoHasher, DeserializeKey, SerializeKey};
18#[cfg(any(test, feature = "fuzzing"))]
19use proptest_derive::Arbitrary;
20use rand::{rngs::OsRng, Rng};
21use serde::{Deserialize, Serialize};
22use std::{convert::TryFrom, fmt, str::FromStr};
23use thiserror::Error;
24
25/// Maximum number of signatures supported in `TransactionAuthenticator`,
26/// across all `AccountAuthenticator`s included.
27pub const MAX_NUM_OF_SIGS: usize = 32;
28
29/// An error enum for issues related to transaction or account authentication.
30#[derive(Clone, Debug, PartialEq, Eq, Error)]
31#[error("{:?}", self)]
32pub enum AuthenticationError {
33    /// The number of signatures exceeds the maximum supported.
34    MaxSignaturesExceeded,
35}
36
37/// Each transaction submitted to the Diem blockchain contains a `TransactionAuthenticator`. During
38/// transaction execution, the executor will check if every `AccountAuthenticator`'s signature on
39/// the transaction hash is well-formed and whether the sha3 hash of the
40/// `AccountAuthenticator`'s `AuthenticationKeyPreimage` matches the `AuthenticationKey` stored
41/// under the participating signer's account address.
42
43#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
44pub enum TransactionAuthenticator {
45    /// Single signature
46    Ed25519 {
47        public_key: Validatable<Ed25519PublicKey>,
48        signature: Ed25519Signature,
49    },
50    /// K-of-N multisignature
51    MultiEd25519 {
52        public_key: MultiEd25519PublicKey,
53        signature: MultiEd25519Signature,
54    },
55    /// Multi-agent transaction.
56    MultiAgent {
57        sender: AccountAuthenticator,
58        secondary_signer_addresses: Vec<AccountAddress>,
59        secondary_signers: Vec<AccountAuthenticator>,
60    },
61}
62
63impl TransactionAuthenticator {
64    /// Create a single-signature ed25519 authenticator
65    pub fn ed25519(public_key: Ed25519PublicKey, signature: Ed25519Signature) -> Self {
66        Self::Ed25519 {
67            public_key: Validatable::new_valid(public_key),
68            signature,
69        }
70    }
71
72    /// Create a multisignature ed25519 authenticator
73    pub fn multi_ed25519(
74        public_key: MultiEd25519PublicKey,
75        signature: MultiEd25519Signature,
76    ) -> Self {
77        Self::MultiEd25519 {
78            public_key,
79            signature,
80        }
81    }
82
83    /// Create a multi-agent authenticator
84    pub fn multi_agent(
85        sender: AccountAuthenticator,
86        secondary_signer_addresses: Vec<AccountAddress>,
87        secondary_signers: Vec<AccountAuthenticator>,
88    ) -> Self {
89        Self::MultiAgent {
90            sender,
91            secondary_signer_addresses,
92            secondary_signers,
93        }
94    }
95
96    /// Return Ok if all AccountAuthenticator's public keys match their signatures, Err otherwise
97    pub fn verify(&self, raw_txn: &RawTransaction) -> Result<()> {
98        let num_sigs: usize = self.sender().number_of_signatures()
99            + self
100                .secondary_signers()
101                .iter()
102                .map(|auth| auth.number_of_signatures())
103                .sum::<usize>();
104        if num_sigs > MAX_NUM_OF_SIGS {
105            return Err(Error::new(AuthenticationError::MaxSignaturesExceeded));
106        }
107        match self {
108            Self::Ed25519 {
109                public_key,
110                signature,
111            } => signature.verify(raw_txn, public_key.validate()?),
112            Self::MultiEd25519 {
113                public_key,
114                signature,
115            } => signature.verify(raw_txn, public_key),
116            Self::MultiAgent {
117                sender,
118                secondary_signer_addresses,
119                secondary_signers,
120            } => {
121                let message = RawTransactionWithData::new_multi_agent(
122                    raw_txn.clone(),
123                    secondary_signer_addresses.clone(),
124                );
125                sender.verify(&message)?;
126                for signer in secondary_signers {
127                    signer.verify(&message)?;
128                }
129                Ok(())
130            }
131        }
132    }
133
134    pub fn sender(&self) -> AccountAuthenticator {
135        match self {
136            Self::Ed25519 {
137                public_key,
138                signature,
139            } => AccountAuthenticator::Ed25519 {
140                public_key: public_key.clone(),
141                signature: signature.clone(),
142            },
143            Self::MultiEd25519 {
144                public_key,
145                signature,
146            } => AccountAuthenticator::multi_ed25519(public_key.clone(), signature.clone()),
147            Self::MultiAgent { sender, .. } => sender.clone(),
148        }
149    }
150
151    pub fn secondary_signer_addreses(&self) -> Vec<AccountAddress> {
152        match self {
153            Self::Ed25519 { .. }
154            | Self::MultiEd25519 {
155                public_key: _,
156                signature: _,
157            } => vec![],
158            Self::MultiAgent {
159                sender: _,
160                secondary_signer_addresses,
161                ..
162            } => secondary_signer_addresses.to_vec(),
163        }
164    }
165
166    pub fn secondary_signers(&self) -> Vec<AccountAuthenticator> {
167        match self {
168            Self::Ed25519 { .. }
169            | Self::MultiEd25519 {
170                public_key: _,
171                signature: _,
172            } => vec![],
173            Self::MultiAgent {
174                sender: _,
175                secondary_signer_addresses: _,
176                secondary_signers,
177            } => secondary_signers.to_vec(),
178        }
179    }
180}
181
182impl fmt::Display for TransactionAuthenticator {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        match self {
185            Self::Ed25519 {
186                public_key: _,
187                signature: _,
188            } => {
189                write!(
190                    f,
191                    "TransactionAuthenticator[scheme: Ed25519, sender: {}]",
192                    self.sender()
193                )
194            }
195            Self::MultiEd25519 {
196                public_key: _,
197                signature: _,
198            } => {
199                write!(
200                    f,
201                    "TransactionAuthenticator[scheme: MultiEd25519, sender: {}]",
202                    self.sender()
203                )
204            }
205            Self::MultiAgent {
206                sender,
207                secondary_signer_addresses,
208                secondary_signers,
209            } => {
210                let mut sec_addrs: String = "".to_string();
211                for sec_addr in secondary_signer_addresses {
212                    sec_addrs = format!("{}\n\t\t\t{:#?},", sec_addrs, sec_addr);
213                }
214                let mut sec_signers: String = "".to_string();
215                for sec_signer in secondary_signers {
216                    sec_signers = format!("{}\n\t\t\t{:#?},", sec_signers, sec_signer);
217                }
218                write!(
219                    f,
220                    "TransactionAuthenticator[\n\
221                        \tscheme: MultiAgent, \n\
222                        \tsender: {}\n\
223                        \tsecondary signer addresses: {}\n\
224                        \tsecondary signers: {}]",
225                    sender, sec_addrs, sec_signers,
226                )
227            }
228        }
229    }
230}
231
232/// An `AccountAuthenticator` is an an abstraction of a signature scheme. It must know:
233/// (1) How to check its signature against a message and public key
234/// (2) How to convert its public key into an `AuthenticationKeyPreimage` structured as
235/// (public_key | signaure_scheme_id).
236/// Each on-chain `DiemAccount` must store an `AuthenticationKey` (computed via a sha3 hash of an
237/// `AuthenticationKeyPreimage`).
238
239// TODO: in the future, can tie these to the AccountAuthenticator enum directly with https://github.com/rust-lang/rust/issues/60553
240#[derive(Debug)]
241#[repr(u8)]
242pub enum Scheme {
243    Ed25519 = 0,
244    MultiEd25519 = 1,
245    // ... add more schemes here
246}
247
248impl fmt::Display for Scheme {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        let display = match self {
251            Scheme::Ed25519 => "Ed25519",
252            Scheme::MultiEd25519 => "MultiEd25519",
253        };
254        write!(f, "Scheme::{}", display)
255    }
256}
257
258#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
259pub enum AccountAuthenticator {
260    /// Single signature
261    Ed25519 {
262        public_key: Validatable<Ed25519PublicKey>,
263        signature: Ed25519Signature,
264    },
265    /// K-of-N multisignature
266    MultiEd25519 {
267        public_key: MultiEd25519PublicKey,
268        signature: MultiEd25519Signature,
269    },
270    // ... add more schemes here
271}
272
273impl AccountAuthenticator {
274    /// Unique identifier for the signature scheme
275    pub fn scheme(&self) -> Scheme {
276        match self {
277            Self::Ed25519 { .. } => Scheme::Ed25519,
278            Self::MultiEd25519 { .. } => Scheme::MultiEd25519,
279        }
280    }
281
282    /// Create a single-signature ed25519 authenticator
283    pub fn ed25519(public_key: Ed25519PublicKey, signature: Ed25519Signature) -> Self {
284        Self::Ed25519 {
285            public_key: Validatable::new_valid(public_key),
286            signature,
287        }
288    }
289
290    /// Create a multisignature ed25519 authenticator
291    pub fn multi_ed25519(
292        public_key: MultiEd25519PublicKey,
293        signature: MultiEd25519Signature,
294    ) -> Self {
295        Self::MultiEd25519 {
296            public_key,
297            signature,
298        }
299    }
300
301    /// Return Ok if the authenticator's public key matches its signature, Err otherwise
302    pub fn verify<T: Serialize + CryptoHash>(&self, message: &T) -> Result<()> {
303        match self {
304            Self::Ed25519 {
305                public_key,
306                signature,
307            } => signature.verify(message, public_key.validate()?),
308            Self::MultiEd25519 {
309                public_key,
310                signature,
311            } => signature.verify(message, public_key),
312        }
313    }
314
315    /// Return the raw bytes of `self.public_key`
316    pub fn public_key_bytes(&self) -> Vec<u8> {
317        match self {
318            Self::Ed25519 { public_key, .. } => public_key.unvalidated().to_bytes().to_vec(),
319            Self::MultiEd25519 { public_key, .. } => public_key.to_bytes().to_vec(),
320        }
321    }
322
323    /// Return the raw bytes of `self.signature`
324    pub fn signature_bytes(&self) -> Vec<u8> {
325        match self {
326            Self::Ed25519 { signature, .. } => signature.to_bytes().to_vec(),
327            Self::MultiEd25519 { signature, .. } => signature.to_bytes().to_vec(),
328        }
329    }
330
331    /// Return an authentication key preimage derived from `self`'s public key and scheme id
332    pub fn authentication_key_preimage(&self) -> AuthenticationKeyPreimage {
333        AuthenticationKeyPreimage::new(self.public_key_bytes(), self.scheme())
334    }
335
336    /// Return an authentication key derived from `self`'s public key and scheme id
337    pub fn authentication_key(&self) -> AuthenticationKey {
338        AuthenticationKey::from_preimage(&self.authentication_key_preimage())
339    }
340
341    /// Return the number of signatures included in this account authenticator.
342    pub fn number_of_signatures(&self) -> usize {
343        match self {
344            Self::Ed25519 { .. } => 1,
345            Self::MultiEd25519 { signature, .. } => signature.signatures().len(),
346        }
347    }
348}
349
350/// A struct that represents an account authentication key. An account's address is the last 16
351/// bytes of authentication key used to create it
352#[derive(
353    Clone,
354    Copy,
355    CryptoHasher,
356    Debug,
357    DeserializeKey,
358    Eq,
359    Hash,
360    Ord,
361    PartialEq,
362    PartialOrd,
363    SerializeKey,
364)]
365#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
366pub struct AuthenticationKey([u8; AuthenticationKey::LENGTH]);
367
368impl AuthenticationKey {
369    /// Create an authentication key from `bytes`
370    pub const fn new(bytes: [u8; Self::LENGTH]) -> Self {
371        Self(bytes)
372    }
373
374    /// Return an authentication key that is impossible (in expectation) to sign for--useful for
375    /// intentionally relinquishing control of an account.
376    pub const fn zero() -> Self {
377        Self([0; 32])
378    }
379
380    /// The number of bytes in an authentication key.
381    pub const LENGTH: usize = 32;
382
383    /// Create an authentication key from a preimage by taking its sha3 hash
384    pub fn from_preimage(preimage: &AuthenticationKeyPreimage) -> AuthenticationKey {
385        AuthenticationKey::new(*HashValue::sha3_256_of(&preimage.0).as_ref())
386    }
387
388    /// Create an authentication key from an Ed25519 public key
389    pub fn ed25519(public_key: &Ed25519PublicKey) -> AuthenticationKey {
390        Self::from_preimage(&AuthenticationKeyPreimage::ed25519(public_key))
391    }
392
393    /// Create an authentication key from a MultiEd25519 public key
394    pub fn multi_ed25519(public_key: &MultiEd25519PublicKey) -> Self {
395        Self::from_preimage(&AuthenticationKeyPreimage::multi_ed25519(public_key))
396    }
397
398    /// Return an address derived from the last `AccountAddress::LENGTH` bytes of this
399    /// authentication key.
400    pub fn derived_address(&self) -> AccountAddress {
401        // keep only last 16 bytes
402        let mut array = [0u8; AccountAddress::LENGTH];
403        array.copy_from_slice(&self.0[Self::LENGTH - AccountAddress::LENGTH..]);
404        AccountAddress::new(array)
405    }
406
407    /// Return the first AccountAddress::LENGTH bytes of this authentication key
408    pub fn prefix(&self) -> [u8; AccountAddress::LENGTH] {
409        let mut array = [0u8; AccountAddress::LENGTH];
410        array.copy_from_slice(&self.0[..AccountAddress::LENGTH]);
411        array
412    }
413
414    /// Construct a vector from this authentication key
415    pub fn to_vec(&self) -> Vec<u8> {
416        self.0.to_vec()
417    }
418
419    /// Create a random authentication key. For testing only
420    pub fn random() -> Self {
421        let mut rng = OsRng;
422        let buf: [u8; Self::LENGTH] = rng.gen();
423        AuthenticationKey::new(buf)
424    }
425}
426
427impl ValidCryptoMaterial for AuthenticationKey {
428    fn to_bytes(&self) -> Vec<u8> {
429        self.to_vec()
430    }
431}
432
433/// A value that can be hashed to produce an authentication key
434pub struct AuthenticationKeyPreimage(Vec<u8>);
435
436impl AuthenticationKeyPreimage {
437    /// Return bytes for (public_key | scheme_id)
438    fn new(mut public_key_bytes: Vec<u8>, scheme: Scheme) -> Self {
439        public_key_bytes.push(scheme as u8);
440        Self(public_key_bytes)
441    }
442
443    /// Construct a preimage from an Ed25519 public key
444    pub fn ed25519(public_key: &Ed25519PublicKey) -> AuthenticationKeyPreimage {
445        Self::new(public_key.to_bytes().to_vec(), Scheme::Ed25519)
446    }
447
448    /// Construct a preimage from a MultiEd25519 public key
449    pub fn multi_ed25519(public_key: &MultiEd25519PublicKey) -> AuthenticationKeyPreimage {
450        Self::new(public_key.to_bytes(), Scheme::MultiEd25519)
451    }
452
453    /// Construct a vector from this authentication key
454    pub fn into_vec(self) -> Vec<u8> {
455        self.0
456    }
457}
458
459impl fmt::Display for AccountAuthenticator {
460    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461        write!(
462            f,
463            "AccountAuthenticator[scheme id: {:?}, public key: {}, signature: {}]",
464            self.scheme(),
465            hex::encode(&self.public_key_bytes()),
466            hex::encode(&self.signature_bytes())
467        )
468    }
469}
470
471impl TryFrom<&[u8]> for AuthenticationKey {
472    type Error = CryptoMaterialError;
473
474    fn try_from(bytes: &[u8]) -> std::result::Result<AuthenticationKey, CryptoMaterialError> {
475        if bytes.len() != Self::LENGTH {
476            return Err(CryptoMaterialError::WrongLengthError);
477        }
478        let mut addr = [0u8; Self::LENGTH];
479        addr.copy_from_slice(bytes);
480        Ok(AuthenticationKey(addr))
481    }
482}
483
484impl TryFrom<Vec<u8>> for AuthenticationKey {
485    type Error = CryptoMaterialError;
486
487    fn try_from(bytes: Vec<u8>) -> std::result::Result<AuthenticationKey, CryptoMaterialError> {
488        AuthenticationKey::try_from(&bytes[..])
489    }
490}
491
492impl FromStr for AuthenticationKey {
493    type Err = Error;
494
495    fn from_str(s: &str) -> Result<Self> {
496        ensure!(
497            !s.is_empty(),
498            "authentication key string should not be empty.",
499        );
500        let bytes_out = ::hex::decode(s)?;
501        let key = AuthenticationKey::try_from(bytes_out.as_slice())?;
502        Ok(key)
503    }
504}
505
506impl AsRef<[u8]> for AuthenticationKey {
507    fn as_ref(&self) -> &[u8] {
508        &self.0
509    }
510}
511
512impl fmt::LowerHex for AuthenticationKey {
513    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514        write!(f, "{}", hex::encode(&self.0))
515    }
516}
517
518impl fmt::Display for AuthenticationKey {
519    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
520        // Forward to the LowerHex impl with a "0x" prepended (the # flag).
521        write!(f, "{:#x}", self)
522    }
523}
524
525#[cfg(test)]
526mod tests {
527    use crate::transaction::authenticator::AuthenticationKey;
528    use std::str::FromStr;
529
530    #[test]
531    fn test_from_str_should_not_panic_by_given_empty_string() {
532        assert!(AuthenticationKey::from_str("").is_err());
533    }
534}