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    impl InMemorySigner {
84        /// Creates a new [`InMemorySigner`] seeded with `prng_seed`.
85        /// If `prng_seed` is `None`, an `OsRng` will be used.
86        #[cfg(with_getrandom)]
87        pub fn new(prng_seed: Option<u64>) -> Self {
88            InMemorySigner(Arc::new(RwLock::new(InMemSignerInner::new(prng_seed))))
89        }
90
91        /// Creates a new [`InMemorySigner`].
92        #[cfg(not(with_getrandom))]
93        pub fn new() -> Self {
94            InMemorySigner(Arc::new(RwLock::new(InMemSignerInner::new())))
95        }
96
97        /// Generates a new key pair from Signer's RNG. Use with care.
98        #[cfg(with_getrandom)]
99        pub fn generate_new(&mut self) -> AccountPublicKey {
100            let mut inner = self.0.write().unwrap();
101            let secret = AccountSecretKey::generate_from(&mut inner.rng_state.prng);
102            if inner.rng_state.testing_seed.is_some() {
103                // Generate a new testing seed for the case when we need to store the PRNG state.
104                // It provides a "forward-secrecy" property for the testing seed.
105                // We do not do that for the case when `testing_seed` is `None`, because
106                // we default to the usage of OsRng in that case.
107                inner.rng_state.testing_seed = Some(inner.rng_state.prng.next_u64());
108            }
109            let public = secret.public();
110            let owner = AccountOwner::from(public);
111            inner.keys.insert(owner, secret);
112            public
113        }
114
115        /// Returns the public key corresponding to the given `owner`.
116        pub fn keys(&self) -> Vec<(AccountOwner, Vec<u8>)> {
117            let inner = self.0.read().unwrap();
118            inner.keys()
119        }
120    }
121
122    /// In-memory signer.
123    struct InMemSignerInner {
124        keys: BTreeMap<AccountOwner, AccountSecretKey>,
125        #[cfg(with_getrandom)]
126        rng_state: RngState,
127    }
128
129    #[cfg(with_getrandom)]
130    struct RngState {
131        prng: Box<dyn CryptoRng>,
132        #[cfg(with_getrandom)]
133        testing_seed: Option<u64>,
134    }
135
136    #[cfg(with_getrandom)]
137    impl RngState {
138        fn new(prng_seed: Option<u64>) -> Self {
139            let prng: Box<dyn CryptoRng> = prng_seed.into();
140            RngState {
141                prng,
142                #[cfg(with_getrandom)]
143                testing_seed: prng_seed,
144            }
145        }
146    }
147
148    impl InMemSignerInner {
149        /// Creates a new `InMemSignerInner` seeded with `prng_seed`.
150        /// If `prng_seed` is `None`, an `OsRng` will be used.
151        #[cfg(with_getrandom)]
152        pub fn new(prng_seed: Option<u64>) -> Self {
153            InMemSignerInner {
154                keys: BTreeMap::new(),
155                rng_state: RngState::new(prng_seed),
156            }
157        }
158
159        /// Creates a new `InMemSignerInner`.
160        #[cfg(not(with_getrandom))]
161        pub fn new() -> Self {
162            InMemSignerInner {
163                keys: BTreeMap::new(),
164            }
165        }
166
167        pub fn keys(&self) -> Vec<(AccountOwner, Vec<u8>)> {
168            self.keys
169                .iter()
170                .map(|(owner, secret)| {
171                    (
172                        *owner,
173                        serde_json::to_vec(secret).expect("serialization should not fail"),
174                    )
175                })
176                .collect()
177        }
178    }
179
180    impl Signer for InMemorySigner {
181        type Error = Error;
182
183        /// Creates a signature for the given `value` using the provided `owner`.
184        async fn sign(
185            &self,
186            owner: &AccountOwner,
187            value: &CryptoHash,
188        ) -> Result<AccountSignature, Error> {
189            let inner = self.0.read().unwrap();
190            if let Some(secret) = inner.keys.get(owner) {
191                let signature = secret.sign_prehash(*value);
192                Ok(signature)
193            } else {
194                Err(Error::NoSuchOwner)
195            }
196        }
197
198        /// Returns whether the given `owner` is a known signer.
199        async fn contains_key(&self, owner: &AccountOwner) -> Result<bool, Error> {
200            Ok(self.0.read().unwrap().keys.contains_key(owner))
201        }
202    }
203
204    impl FromIterator<(AccountOwner, AccountSecretKey)> for InMemorySigner {
205        fn from_iter<T>(input: T) -> Self
206        where
207            T: IntoIterator<Item = (AccountOwner, AccountSecretKey)>,
208        {
209            InMemorySigner(Arc::new(RwLock::new(InMemSignerInner {
210                keys: BTreeMap::from_iter(input),
211                #[cfg(with_getrandom)]
212                rng_state: RngState::new(None),
213            })))
214        }
215    }
216
217    impl Default for InMemSignerInner {
218        fn default() -> Self {
219            #[cfg(with_getrandom)]
220            let signer = InMemSignerInner::new(None);
221            #[cfg(not(with_getrandom))]
222            let signer = InMemSignerInner::new();
223            signer
224        }
225    }
226
227    impl Serialize for InMemorySigner {
228        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229        where
230            S: serde::Serializer,
231        {
232            let inner = self.0.read().unwrap();
233            InMemSignerInner::serialize(&*inner, serializer)
234        }
235    }
236
237    impl<'de> Deserialize<'de> for InMemorySigner {
238        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
239        where
240            D: serde::Deserializer<'de>,
241        {
242            let inner = InMemSignerInner::deserialize(deserializer)?;
243            Ok(InMemorySigner(Arc::new(RwLock::new(inner))))
244        }
245    }
246
247    impl Serialize for InMemSignerInner {
248        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
249        where
250            S: serde::Serializer,
251        {
252            #[derive(Serialize, Debug)]
253            struct Inner<'a> {
254                keys: &'a Vec<(AccountOwner, Vec<u8>)>,
255                #[cfg(with_getrandom)]
256                prng_seed: Option<u64>,
257            }
258
259            #[cfg(with_getrandom)]
260            let prng_seed = self.rng_state.testing_seed;
261
262            let inner = Inner {
263                keys: &self.keys(),
264                #[cfg(with_getrandom)]
265                prng_seed,
266            };
267
268            Inner::serialize(&inner, serializer)
269        }
270    }
271
272    impl<'de> Deserialize<'de> for InMemSignerInner {
273        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
274        where
275            D: serde::Deserializer<'de>,
276        {
277            #[derive(Deserialize)]
278            struct Inner {
279                keys: Vec<(AccountOwner, Vec<u8>)>,
280                #[cfg(with_getrandom)]
281                prng_seed: Option<u64>,
282            }
283
284            let inner = Inner::deserialize(deserializer)?;
285
286            let keys = inner
287                .keys
288                .into_iter()
289                .map(|(owner, secret)| {
290                    let secret =
291                        serde_json::from_slice(&secret).map_err(serde::de::Error::custom)?;
292                    Ok((owner, secret))
293                })
294                .collect::<Result<BTreeMap<_, _>, _>>()?;
295
296            let signer = InMemSignerInner {
297                keys,
298                #[cfg(with_getrandom)]
299                rng_state: RngState::new(inner.prng_seed),
300            };
301            Ok(signer)
302        }
303    }
304}