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