Skip to main content

linera_base/crypto/
signer.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5An interface for cryptographic signers that can be used by the Linera client to sign blocks.
6*/
7
8use std::error::Error as StdError;
9
10pub use in_mem::InMemorySigner;
11
12use super::CryptoHash;
13use crate::{crypto::AccountSignature, identifiers::AccountOwner};
14
15cfg_if::cfg_if! {
16    if #[cfg(web)] {
17        #[doc(hidden)]
18        pub trait TaskSendable {}
19        impl<T> TaskSendable for T {}
20    } else {
21        #[doc(hidden)]
22        pub trait TaskSendable: Send + Sync {}
23        impl<T: Send + Sync> TaskSendable for T {}
24    }
25}
26
27/// Errors that can be returned from signers.
28pub trait Error: StdError + TaskSendable {}
29impl<T: StdError + TaskSendable> Error for T {}
30
31impl StdError for Box<dyn Error + '_> {
32    fn source(&self) -> Option<&(dyn StdError + 'static)> {
33        (**self).source()
34    }
35}
36
37/// A trait for signing keys.
38#[cfg_attr(not(web), trait_variant::make(Send))]
39pub trait Signer {
40    /// The type of errors arising from operations on this `Signer`.
41    type Error: Error;
42
43    /// Creates a signature for the given `value` using the provided `owner`.
44    // DEV: We sign `CryptoHash` type, rather than `&[u8]` to make sure we don't sign
45    // things accidentally. See [`CryptoHash::new`] for how the type's name is included
46    // in the resulting hash, providing the canonicity of the hashing process.
47    async fn sign(
48        &self,
49        owner: &AccountOwner,
50        value: &CryptoHash,
51    ) -> Result<AccountSignature, Self::Error>;
52
53    /// Returns whether the given `owner` is a known signer.
54    async fn contains_key(&self, owner: &AccountOwner) -> Result<bool, Self::Error>;
55}
56
57/// In-memory implementation of the [`Signer`] trait.
58mod in_mem {
59    use std::{
60        collections::BTreeMap,
61        sync::{Arc, RwLock},
62    };
63
64    use serde::{Deserialize, Serialize};
65
66    #[cfg(with_getrandom)]
67    use crate::crypto::{AccountPublicKey, CryptoRng};
68    use crate::{
69        crypto::{AccountSecretKey, AccountSignature, CryptoHash, Signer},
70        identifiers::AccountOwner,
71    };
72
73    #[derive(Debug, thiserror::Error)]
74    pub enum Error {
75        #[error("no key found for the given owner")]
76        NoSuchOwner,
77    }
78
79    /// In-memory signer.
80    #[derive(Clone)]
81    pub struct InMemorySigner(Arc<RwLock<InMemSignerInner>>);
82
83    #[cfg(not(with_getrandom))]
84    impl Default for InMemorySigner {
85        fn default() -> Self {
86            Self::new()
87        }
88    }
89
90    impl InMemorySigner {
91        /// Creates a new [`InMemorySigner`] seeded with `prng_seed`.
92        /// If `prng_seed` is `None`, an `OsRng` will be used.
93        #[cfg(with_getrandom)]
94        pub fn new(prng_seed: Option<u64>) -> Self {
95            InMemorySigner(Arc::new(RwLock::new(InMemSignerInner::new(prng_seed))))
96        }
97
98        /// Creates a new [`InMemorySigner`].
99        #[cfg(not(with_getrandom))]
100        pub fn new() -> Self {
101            InMemorySigner(Arc::new(RwLock::new(InMemSignerInner::new())))
102        }
103
104        /// Generates a new key pair from Signer's RNG. Use with care.
105        #[cfg(with_getrandom)]
106        pub fn generate_new(&mut self) -> AccountPublicKey {
107            let mut inner = self.0.write().unwrap();
108            let secret = AccountSecretKey::generate_from(&mut inner.rng_state.prng);
109            if inner.rng_state.testing_seed.is_some() {
110                // Generate a new testing seed for the case when we need to store the PRNG state.
111                // It provides a "forward-secrecy" property for the testing seed.
112                // We do not do that for the case when `testing_seed` is `None`, because
113                // we default to the usage of OsRng in that case.
114                inner.rng_state.testing_seed = Some(inner.rng_state.prng.next_u64());
115            }
116            let public = secret.public();
117            let owner = AccountOwner::from(public);
118            inner.keys.insert(owner, secret);
119            public
120        }
121
122        /// Returns the public key corresponding to the given `owner`.
123        pub fn keys(&self) -> Vec<(AccountOwner, Vec<u8>)> {
124            let inner = self.0.read().unwrap();
125            inner.keys()
126        }
127
128        /// Removes the key for `owner`, returning whether one was present. Useful to
129        /// simulate a key becoming unavailable (e.g. an autosigner key that is rotated or
130        /// withdrawn after a block was already staged by it).
131        pub fn forget_key(&self, owner: &AccountOwner) -> bool {
132            self.0.write().unwrap().keys.remove(owner).is_some()
133        }
134    }
135
136    #[derive(Debug, Deserialize, Serialize)]
137    struct Inner {
138        keys: Vec<(AccountOwner, String)>,
139        #[cfg(with_getrandom)]
140        prng_seed: Option<u64>,
141    }
142
143    /// In-memory signer.
144    struct InMemSignerInner {
145        keys: BTreeMap<AccountOwner, AccountSecretKey>,
146        #[cfg(with_getrandom)]
147        rng_state: RngState,
148    }
149
150    #[cfg(with_getrandom)]
151    struct RngState {
152        prng: Box<dyn CryptoRng>,
153        #[cfg(with_getrandom)]
154        testing_seed: Option<u64>,
155    }
156
157    #[cfg(with_getrandom)]
158    impl RngState {
159        fn new(prng_seed: Option<u64>) -> Self {
160            let prng: Box<dyn CryptoRng> = prng_seed.into();
161            RngState {
162                prng,
163                #[cfg(with_getrandom)]
164                testing_seed: prng_seed,
165            }
166        }
167    }
168
169    impl InMemSignerInner {
170        /// Creates a new `InMemSignerInner` seeded with `prng_seed`.
171        /// If `prng_seed` is `None`, an `OsRng` will be used.
172        #[cfg(with_getrandom)]
173        pub fn new(prng_seed: Option<u64>) -> Self {
174            InMemSignerInner {
175                keys: BTreeMap::new(),
176                rng_state: RngState::new(prng_seed),
177            }
178        }
179
180        /// Creates a new `InMemSignerInner`.
181        #[cfg(not(with_getrandom))]
182        pub fn new() -> Self {
183            InMemSignerInner {
184                keys: BTreeMap::new(),
185            }
186        }
187
188        pub fn keys(&self) -> Vec<(AccountOwner, Vec<u8>)> {
189            self.keys
190                .iter()
191                .map(|(owner, secret)| {
192                    let bytes = serde_json::to_vec(secret).expect("serialization should not fail");
193                    (*owner, bytes)
194                })
195                .collect()
196        }
197    }
198
199    impl Signer for InMemorySigner {
200        type Error = Error;
201
202        /// Creates a signature for the given `value` using the provided `owner`.
203        async fn sign(
204            &self,
205            owner: &AccountOwner,
206            value: &CryptoHash,
207        ) -> Result<AccountSignature, Error> {
208            let inner = self.0.read().unwrap();
209            if let Some(secret) = inner.keys.get(owner) {
210                let signature = secret.sign_prehash(*value);
211                Ok(signature)
212            } else {
213                Err(Error::NoSuchOwner)
214            }
215        }
216
217        /// Returns whether the given `owner` is a known signer.
218        async fn contains_key(&self, owner: &AccountOwner) -> Result<bool, Error> {
219            Ok(self.0.read().unwrap().keys.contains_key(owner))
220        }
221    }
222
223    impl FromIterator<(AccountOwner, AccountSecretKey)> for InMemorySigner {
224        fn from_iter<T>(input: T) -> Self
225        where
226            T: IntoIterator<Item = (AccountOwner, AccountSecretKey)>,
227        {
228            InMemorySigner(Arc::new(RwLock::new(InMemSignerInner {
229                keys: BTreeMap::from_iter(input),
230                #[cfg(with_getrandom)]
231                rng_state: RngState::new(None),
232            })))
233        }
234    }
235
236    impl Default for InMemSignerInner {
237        fn default() -> Self {
238            #[cfg(with_getrandom)]
239            let signer = InMemSignerInner::new(None);
240            #[cfg(not(with_getrandom))]
241            let signer = InMemSignerInner::new();
242            signer
243        }
244    }
245
246    impl Serialize for InMemorySigner {
247        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
248        where
249            S: serde::Serializer,
250        {
251            let inner = self.0.read().unwrap();
252            InMemSignerInner::serialize(&*inner, serializer)
253        }
254    }
255
256    impl<'de> Deserialize<'de> for InMemorySigner {
257        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
258        where
259            D: serde::Deserializer<'de>,
260        {
261            let inner = InMemSignerInner::deserialize(deserializer)?;
262            Ok(InMemorySigner(Arc::new(RwLock::new(inner))))
263        }
264    }
265
266    impl Serialize for InMemSignerInner {
267        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
268        where
269            S: serde::Serializer,
270        {
271            #[cfg(with_getrandom)]
272            let prng_seed = self.rng_state.testing_seed;
273
274            let keys_as_strings = self
275                .keys()
276                .into_iter()
277                .map(|(owner, bytes)| (owner, hex::encode(bytes)))
278                .collect::<Vec<_>>();
279
280            let inner = Inner {
281                keys: keys_as_strings,
282                #[cfg(with_getrandom)]
283                prng_seed,
284            };
285
286            Inner::serialize(&inner, serializer)
287        }
288    }
289
290    impl<'de> Deserialize<'de> for InMemSignerInner {
291        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292        where
293            D: serde::Deserializer<'de>,
294        {
295            let inner = Inner::deserialize(deserializer)?;
296
297            let keys = inner
298                .keys
299                .into_iter()
300                .map(|(owner, secret_hex)| {
301                    let secret_bytes =
302                        hex::decode(&secret_hex).map_err(serde::de::Error::custom)?;
303                    let secret =
304                        serde_json::from_slice(&secret_bytes).map_err(serde::de::Error::custom)?;
305                    Ok((owner, secret))
306                })
307                .collect::<Result<BTreeMap<_, _>, _>>()?;
308
309            let signer = InMemSignerInner {
310                keys,
311                #[cfg(with_getrandom)]
312                rng_state: RngState::new(inner.prng_seed),
313            };
314            Ok(signer)
315        }
316    }
317}