diem_secure_storage/
crypto_kv_storage.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::{CryptoStorage, Error, KVStorage, PublicKeyResponse};
9use diem_crypto::{hash::CryptoHash, PrivateKey, SigningKey, Uniform};
10use diem_types::validator_config::{
11    ConsensusPrivateKey, ConsensusPublicKey, ConsensusSignature,
12};
13use rand::{rngs::OsRng, Rng, SeedableRng};
14use serde::ser::Serialize;
15
16/// CryptoKVStorage offers a CryptoStorage implementation by extending a key
17/// value store (KVStorage) to create and manage cryptographic keys. This is
18/// useful for providing a simple CryptoStorage implementation based upon an
19/// existing KVStorage engine (e.g. for test purposes).
20pub trait CryptoKVStorage: KVStorage {}
21
22impl<T: CryptoKVStorage> CryptoStorage for T {
23    fn create_key(&mut self, name: &str) -> Result<ConsensusPublicKey, Error> {
24        // Generate and store the new named key pair
25        let (private_key, public_key) = new_key_pair::<ConsensusPrivateKey>();
26        self.import_private_key(name, private_key)?;
27        Ok(public_key)
28    }
29
30    fn export_private_key(
31        &self, name: &str,
32    ) -> Result<ConsensusPrivateKey, Error> {
33        self.get(name).map(|v| v.value)
34    }
35
36    fn export_private_key_for_version(
37        &self, name: &str, version: ConsensusPublicKey,
38    ) -> Result<ConsensusPrivateKey, Error> {
39        let current_private_key = self.export_private_key(name)?;
40        if current_private_key.public_key().eq(&version) {
41            return Ok(current_private_key);
42        }
43
44        match self.export_private_key(&get_previous_version_name(name)) {
45            Ok(previous_private_key) => {
46                if previous_private_key.public_key().eq(&version) {
47                    Ok(previous_private_key)
48                } else {
49                    Err(Error::KeyVersionNotFound(
50                        name.into(),
51                        version.to_string(),
52                    ))
53                }
54            }
55            Err(Error::KeyNotSet(_)) => {
56                Err(Error::KeyVersionNotFound(name.into(), version.to_string()))
57            }
58            Err(e) => Err(e),
59        }
60    }
61
62    fn import_private_key(
63        &mut self, name: &str, key: ConsensusPrivateKey,
64    ) -> Result<(), Error> {
65        self.set(name, key)
66    }
67
68    fn get_public_key(&self, name: &str) -> Result<PublicKeyResponse, Error> {
69        let response = self.get(name)?;
70        let key: ConsensusPrivateKey = response.value;
71
72        Ok(PublicKeyResponse {
73            last_update: response.last_update,
74            public_key: key.public_key(),
75        })
76    }
77
78    fn get_public_key_previous_version(
79        &self, name: &str,
80    ) -> Result<ConsensusPublicKey, Error> {
81        match self.export_private_key(&get_previous_version_name(name)) {
82            Ok(previous_private_key) => Ok(previous_private_key.public_key()),
83            Err(Error::KeyNotSet(_)) => Err(Error::KeyVersionNotFound(
84                name.into(),
85                "previous version".into(),
86            )),
87            Err(e) => Err(e),
88        }
89    }
90
91    fn rotate_key(&mut self, name: &str) -> Result<ConsensusPublicKey, Error> {
92        let private_key: ConsensusPrivateKey = self.get(name)?.value;
93        let (new_private_key, new_public_key) =
94            new_key_pair::<ConsensusPrivateKey>();
95        self.set(&get_previous_version_name(name), private_key)?;
96        self.set(name, new_private_key)?;
97        Ok(new_public_key)
98    }
99
100    fn sign<U: CryptoHash + Serialize>(
101        &self, name: &str, message: &U,
102    ) -> Result<ConsensusSignature, Error> {
103        let private_key = self.export_private_key(name)?;
104        Ok(private_key.sign(message))
105    }
106
107    fn sign_using_version<U: CryptoHash + Serialize>(
108        &self, name: &str, version: ConsensusPublicKey, message: &U,
109    ) -> Result<ConsensusSignature, Error> {
110        let private_key = self.export_private_key_for_version(name, version)?;
111        Ok(private_key.sign(message))
112    }
113}
114
115fn new_key_pair<SK: SigningKey + Uniform>() -> (SK, SK::PublicKeyMaterial) {
116    let mut seed_rng = OsRng;
117    let mut rng = rand::rngs::StdRng::from_seed(seed_rng.gen());
118    let private_key = SK::generate(&mut rng);
119    let public_key = private_key.public_key();
120    (private_key, public_key)
121}
122
123/// Private helper method to get the name of the previous version of the given
124/// key pair, as held in secure cryptographic storage.
125fn get_previous_version_name(name: &str) -> String {
126    format!("{}_previous", name)
127}