Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/identity/keystore.rs
Line
Count
Source (jump to first uncovered line)
1
// Smoldot
2
// Copyright (C) 2019-2022  Parity Technologies (UK) Ltd.
3
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
15
// You should have received a copy of the GNU General Public License
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
//! Data structure containing cryptographic key pairs.
19
//!
20
//! The keystore is a shared data structure (i.e. all of its functions accept `&self` rather than
21
//! `&mut self`, making it possible to share it through an `Arc` for example) containing a list of
22
//! cryptographic key pairs (i.e. both the public and secret keys).
23
//!
24
//! Each key pair contained within the keystore is identified as a `(KeyNamespace, [u8; 32])`
25
//! tuple, where the `[u8; 32]` is the public key. See [`KeyNamespace`].
26
//!
27
//! A keystore is optionally associated with a directory of the file system into which it will
28
//! store secret keys permanently. Keys present in this directory are considered to be the content
29
//! of the keystore data structure.
30
//!
31
//! For caching reasons, adding and removing keys to the directory manually (for example through
32
//! the [`std::fs`] API) doesn't automatically propagate to the public API of the keystore.
33
//! Similarly, it is not intended to be possible to create two [`Keystore`] instances associated
34
//! to the same directory at the same time.
35
//!
36
//! > **Note**: The Substrate framework also has a keystore, however this keystore implementation
37
//! >           isn't compatible with the Substrate keystore implementation. In other words, this
38
//! >           keystore cannot load keys found in a directory that was previously associated with
39
//! >           a Substrate keystore. This was decided because the Substrate keystore made some
40
//! >           questionable decisions that it has to keep for backwards compatibility reasons.
41
//! >           This keystore, being newly-written, doesn't have to follow them.
42
43
#![cfg(feature = "std")]
44
#![cfg_attr(docsrs, doc(cfg(feature = "std")))]
45
46
use crate::{identity::seed_phrase, util::SipHasherBuild};
47
48
use async_lock::Mutex;
49
use rand_chacha::rand_core::{RngCore as _, SeedableRng as _};
50
use std::{borrow::Cow, fs, io, path, str};
51
52
/// Namespace of the key.
53
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
54
// TODO: document
55
pub enum KeyNamespace {
56
    Aura,
57
    AuthorityDiscovery,
58
    Babe,
59
    Grandpa,
60
    ImOnline,
61
    // TODO: there exists other variants in Substrate but it's unclear whether they're in use (see https://github.com/paritytech/substrate/blob/cafe12e7785bf92e5dc04780c10e7f8330a15a4c/primitives/core/src/crypto.rs)
62
}
63
64
impl KeyNamespace {
65
    /// Returns all existing variants of [`KeyNamespace`].
66
0
    pub fn all() -> impl ExactSizeIterator<Item = KeyNamespace> {
67
0
        [
68
0
            KeyNamespace::Aura,
69
0
            KeyNamespace::AuthorityDiscovery,
70
0
            KeyNamespace::Babe,
71
0
            KeyNamespace::Grandpa,
72
0
            KeyNamespace::ImOnline,
73
0
        ]
74
0
        .into_iter()
75
0
    }
Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB2_12KeyNamespace3all
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB2_12KeyNamespace3all
76
77
2
    fn from_string(str: &str) -> Option<Self> {
78
2
        match str {
79
2
            "aura" => 
Some(KeyNamespace::Aura)1
,
80
1
            "audi" => 
Some(KeyNamespace::AuthorityDiscovery)0
,
81
1
            "babe" => Some(KeyNamespace::Babe),
82
0
            "gran" => Some(KeyNamespace::Grandpa),
83
0
            "imon" => Some(KeyNamespace::ImOnline),
84
0
            _ => None,
85
        }
86
2
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB2_12KeyNamespace11from_string
Line
Count
Source
77
2
    fn from_string(str: &str) -> Option<Self> {
78
2
        match str {
79
2
            "aura" => 
Some(KeyNamespace::Aura)1
,
80
1
            "audi" => 
Some(KeyNamespace::AuthorityDiscovery)0
,
81
1
            "babe" => Some(KeyNamespace::Babe),
82
0
            "gran" => Some(KeyNamespace::Grandpa),
83
0
            "imon" => Some(KeyNamespace::ImOnline),
84
0
            _ => None,
85
        }
86
2
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB2_12KeyNamespace11from_string
87
88
4
    fn as_string(&self) -> &'static str {
89
4
        match self {
90
2
            KeyNamespace::Aura => "aura",
91
0
            KeyNamespace::AuthorityDiscovery => "audi",
92
2
            KeyNamespace::Babe => "babe",
93
0
            KeyNamespace::Grandpa => "gran",
94
0
            KeyNamespace::ImOnline => "imon",
95
        }
96
4
    }
_RNvMNtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB2_12KeyNamespace9as_string
Line
Count
Source
88
4
    fn as_string(&self) -> &'static str {
89
4
        match self {
90
2
            KeyNamespace::Aura => "aura",
91
0
            KeyNamespace::AuthorityDiscovery => "audi",
92
2
            KeyNamespace::Babe => "babe",
93
0
            KeyNamespace::Grandpa => "gran",
94
0
            KeyNamespace::ImOnline => "imon",
95
        }
96
4
    }
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB2_12KeyNamespace9as_string
97
}
98
99
/// Collection of key pairs.
100
///
101
/// This module doesn't give you access to the content of private keys, only to signing
102
/// capabilities.
103
pub struct Keystore {
104
    keys_directory: Option<path::PathBuf>,
105
    guarded: Mutex<Guarded>,
106
    /// Cached base signing context cloned when signing with `sr25519`.
107
    sr25519_signing_context: schnorrkel::context::SigningContext,
108
}
109
110
impl Keystore {
111
    /// Initializes a new keystore.
112
    ///
113
    /// Must be passed bytes of entropy that are used to avoid hash collision attacks and to
114
    /// generate private keys.
115
    ///
116
    /// An error is returned if the `keys_directory` couldn't be opened because, for example, of
117
    /// some missing permission or because it isn't a directory.
118
    /// If the `keys_directory` doesn't exist, it will be created using `fs::create_dir_all`.
119
4
    pub async fn new(
120
4
        keys_directory: Option<path::PathBuf>,
121
4
        randomness_seed: [u8; 32],
122
25
    ) -> Result<Self, io::Error> {
_RNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB4_8Keystore3new
Line
Count
Source
119
4
    pub async fn new(
120
4
        keys_directory: Option<path::PathBuf>,
121
4
        randomness_seed: [u8; 32],
122
4
    ) -> Result<Self, io::Error> {
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB4_8Keystore3new
123
25
        let mut gen_rng = rand_chacha::ChaCha20Rng::from_seed(randomness_seed);
124
25
125
25
        let mut keys = hashbrown::HashMap::with_capacity_and_hasher(32, {
126
25
            SipHasherBuild::new({
127
25
                let mut seed = [0; 16];
128
25
                gen_rng.fill_bytes(&mut seed);
129
25
                seed
130
25
            })
131
25
        });
132
133
        // Load the keys from the disk.
134
        // TODO: return some diagnostic about invalid files?
135
25
        if let Some(
keys_directory4
) = &keys_directory {
136
4
            if !keys_directory.try_exists()
?0
{
137
0
                fs::create_dir_all(keys_directory)?;
138
4
            }
139
140
4
            for 
entry2
in fs::read_dir(keys_directory)
?0
{
141
2
                let entry = entry
?0
;
142
2
                if entry.file_type()
?0
.is_dir() {
143
0
                    continue;
144
2
                }
145
146
                // Try to match the file name.
147
2
                let file_name = match entry.file_name().into_string() {
148
2
                    Ok(n) => n,
149
0
                    Err(_) => continue,
150
                };
151
152
2
                let mut parser =
153
2
                    nom::combinator::all_consuming::<_, _, (&str, nom::error::ErrorKind), _>(
154
2
                        nom::combinator::complete(nom::sequence::tuple((
155
2
                            nom::combinator::map_opt(
156
2
                                nom::bytes::streaming::take(4u32),
157
2
                                KeyNamespace::from_string,
158
2
                            ),
159
2
                            nom::bytes::streaming::tag("-"),
160
2
                            nom::combinator::map_opt(
161
2
                                nom::bytes::streaming::take(7u32),
162
2
                                |b| match b {
163
2
                                    "ed25519" => 
Some(PrivateKey::FileEd25519)1
,
164
1
                                    "sr25519" => Some(PrivateKey::FileSr25519),
165
0
                                    _ => None,
166
2
                                },
_RNCNCNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB8_8Keystore3new00Bc_
Line
Count
Source
162
2
                                |b| match b {
163
2
                                    "ed25519" => 
Some(PrivateKey::FileEd25519)1
,
164
1
                                    "sr25519" => Some(PrivateKey::FileSr25519),
165
0
                                    _ => None,
166
2
                                },
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new00Bc_
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new00CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new00CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new00CsibGXYHQB8Ea_25json_rpc_general_requests
167
2
                            ),
168
2
                            nom::bytes::streaming::tag("-"),
169
2
                            nom::combinator::map_opt(
170
128
                                nom::bytes::complete::take_while(|c: char| {
171
128
                                    c.is_ascii_digit() || 
('a'..='f').contains(&c)54
172
128
                                }),
_RNCNCNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB8_8Keystore3new0s_0Bc_
Line
Count
Source
170
128
                                nom::bytes::complete::take_while(|c: char| {
171
128
                                    c.is_ascii_digit() || 
('a'..='f').contains(&c)54
172
128
                                }),
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new0s_0Bc_
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new0s_0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new0s_0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new0s_0CsibGXYHQB8Ea_25json_rpc_general_requests
173
2
                                |k: &str| {
174
2
                                    if k.len() == 64 {
175
2
                                        Some(<[u8; 32]>::try_from(hex::decode(k).unwrap()).unwrap())
176
                                    } else {
177
0
                                        None
178
                                    }
179
2
                                },
_RNCNCNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB8_8Keystore3new0s0_0Bc_
Line
Count
Source
173
2
                                |k: &str| {
174
2
                                    if k.len() == 64 {
175
2
                                        Some(<[u8; 32]>::try_from(hex::decode(k).unwrap()).unwrap())
176
                                    } else {
177
0
                                        None
178
                                    }
179
2
                                },
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new0s0_0Bc_
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new0s0_0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new0s0_0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB8_8Keystore3new0s0_0CsibGXYHQB8Ea_25json_rpc_general_requests
180
2
                            ),
181
2
                        ))),
182
2
                    );
183
184
2
                let (namespace, _, algorithm, _, public_key) = match parser(&file_name) {
185
2
                    Ok((_, v)) => v,
186
0
                    Err(_) => continue,
187
                };
188
189
                // Make sure that the content of the file is valid and that it corresponds to
190
                // the public key advertised in the file name.
191
2
                match algorithm {
192
                    PrivateKey::FileEd25519 => {
193
1
                        match Self::load_ed25519_from_file(keys_directory.join(entry.path())).
await0
194
                        {
195
1
                            Ok(kp) => {
196
1
                                if ed25519_zebra::VerificationKey::from(&*kp).as_ref() != public_key
197
                                {
198
0
                                    continue;
199
1
                                }
200
                            }
201
0
                            Err(_) => continue,
202
                        }
203
                    }
204
                    PrivateKey::FileSr25519 => {
205
1
                        match Self::load_sr25519_from_file(keys_directory.join(entry.path())).
await0
206
                        {
207
1
                            Ok(kp) => {
208
1
                                if kp.public.to_bytes() != public_key {
209
0
                                    continue;
210
1
                                }
211
                            }
212
0
                            Err(err) => panic!("{err:?}"),
213
                        }
214
                    }
215
0
                    _ => unreachable!(),
216
                }
217
218
2
                keys.insert((namespace, public_key), algorithm);
219
            }
220
21
        }
221
222
25
        Ok(Keystore {
223
25
            keys_directory,
224
25
            guarded: Mutex::new(Guarded { gen_rng, keys }),
225
25
            sr25519_signing_context: schnorrkel::signing_context(b"substrate"),
226
25
        })
227
25
    }
_RNCNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB6_8Keystore3new0Ba_
Line
Count
Source
122
4
    ) -> Result<Self, io::Error> {
123
4
        let mut gen_rng = rand_chacha::ChaCha20Rng::from_seed(randomness_seed);
124
4
125
4
        let mut keys = hashbrown::HashMap::with_capacity_and_hasher(32, {
126
4
            SipHasherBuild::new({
127
4
                let mut seed = [0; 16];
128
4
                gen_rng.fill_bytes(&mut seed);
129
4
                seed
130
4
            })
131
4
        });
132
133
        // Load the keys from the disk.
134
        // TODO: return some diagnostic about invalid files?
135
4
        if let Some(keys_directory) = &keys_directory {
136
4
            if !keys_directory.try_exists()
?0
{
137
0
                fs::create_dir_all(keys_directory)?;
138
4
            }
139
140
4
            for 
entry2
in fs::read_dir(keys_directory)
?0
{
141
2
                let entry = entry
?0
;
142
2
                if entry.file_type()
?0
.is_dir() {
143
0
                    continue;
144
2
                }
145
146
                // Try to match the file name.
147
2
                let file_name = match entry.file_name().into_string() {
148
2
                    Ok(n) => n,
149
0
                    Err(_) => continue,
150
                };
151
152
2
                let mut parser =
153
2
                    nom::combinator::all_consuming::<_, _, (&str, nom::error::ErrorKind), _>(
154
2
                        nom::combinator::complete(nom::sequence::tuple((
155
2
                            nom::combinator::map_opt(
156
2
                                nom::bytes::streaming::take(4u32),
157
2
                                KeyNamespace::from_string,
158
2
                            ),
159
2
                            nom::bytes::streaming::tag("-"),
160
2
                            nom::combinator::map_opt(
161
2
                                nom::bytes::streaming::take(7u32),
162
2
                                |b| match b {
163
                                    "ed25519" => Some(PrivateKey::FileEd25519),
164
                                    "sr25519" => Some(PrivateKey::FileSr25519),
165
                                    _ => None,
166
2
                                },
167
2
                            ),
168
2
                            nom::bytes::streaming::tag("-"),
169
2
                            nom::combinator::map_opt(
170
2
                                nom::bytes::complete::take_while(|c: char| {
171
                                    c.is_ascii_digit() || ('a'..='f').contains(&c)
172
2
                                }),
173
2
                                |k: &str| {
174
                                    if k.len() == 64 {
175
                                        Some(<[u8; 32]>::try_from(hex::decode(k).unwrap()).unwrap())
176
                                    } else {
177
                                        None
178
                                    }
179
2
                                },
180
2
                            ),
181
2
                        ))),
182
2
                    );
183
184
2
                let (namespace, _, algorithm, _, public_key) = match parser(&file_name) {
185
2
                    Ok((_, v)) => v,
186
0
                    Err(_) => continue,
187
                };
188
189
                // Make sure that the content of the file is valid and that it corresponds to
190
                // the public key advertised in the file name.
191
2
                match algorithm {
192
                    PrivateKey::FileEd25519 => {
193
1
                        match Self::load_ed25519_from_file(keys_directory.join(entry.path())).
await0
194
                        {
195
1
                            Ok(kp) => {
196
1
                                if ed25519_zebra::VerificationKey::from(&*kp).as_ref() != public_key
197
                                {
198
0
                                    continue;
199
1
                                }
200
                            }
201
0
                            Err(_) => continue,
202
                        }
203
                    }
204
                    PrivateKey::FileSr25519 => {
205
1
                        match Self::load_sr25519_from_file(keys_directory.join(entry.path())).
await0
206
                        {
207
1
                            Ok(kp) => {
208
1
                                if kp.public.to_bytes() != public_key {
209
0
                                    continue;
210
1
                                }
211
                            }
212
0
                            Err(err) => panic!("{err:?}"),
213
                        }
214
                    }
215
0
                    _ => unreachable!(),
216
                }
217
218
2
                keys.insert((namespace, public_key), algorithm);
219
            }
220
0
        }
221
222
4
        Ok(Keystore {
223
4
            keys_directory,
224
4
            guarded: Mutex::new(Guarded { gen_rng, keys }),
225
4
            sr25519_signing_context: schnorrkel::signing_context(b"substrate"),
226
4
        })
227
4
    }
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore3new0Ba_
_RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore3new0CsiLzmwikkc22_14json_rpc_basic
Line
Count
Source
122
2
    ) -> Result<Self, io::Error> {
123
2
        let mut gen_rng = rand_chacha::ChaCha20Rng::from_seed(randomness_seed);
124
2
125
2
        let mut keys = hashbrown::HashMap::with_capacity_and_hasher(32, {
126
2
            SipHasherBuild::new({
127
2
                let mut seed = [0; 16];
128
2
                gen_rng.fill_bytes(&mut seed);
129
2
                seed
130
2
            })
131
2
        });
132
133
        // Load the keys from the disk.
134
        // TODO: return some diagnostic about invalid files?
135
2
        if let Some(
keys_directory0
) = &keys_directory {
136
0
            if !keys_directory.try_exists()? {
137
0
                fs::create_dir_all(keys_directory)?;
138
0
            }
139
140
0
            for entry in fs::read_dir(keys_directory)? {
141
0
                let entry = entry?;
142
0
                if entry.file_type()?.is_dir() {
143
0
                    continue;
144
0
                }
145
146
                // Try to match the file name.
147
0
                let file_name = match entry.file_name().into_string() {
148
0
                    Ok(n) => n,
149
0
                    Err(_) => continue,
150
                };
151
152
0
                let mut parser =
153
0
                    nom::combinator::all_consuming::<_, _, (&str, nom::error::ErrorKind), _>(
154
0
                        nom::combinator::complete(nom::sequence::tuple((
155
0
                            nom::combinator::map_opt(
156
0
                                nom::bytes::streaming::take(4u32),
157
0
                                KeyNamespace::from_string,
158
0
                            ),
159
0
                            nom::bytes::streaming::tag("-"),
160
0
                            nom::combinator::map_opt(
161
0
                                nom::bytes::streaming::take(7u32),
162
0
                                |b| match b {
163
                                    "ed25519" => Some(PrivateKey::FileEd25519),
164
                                    "sr25519" => Some(PrivateKey::FileSr25519),
165
                                    _ => None,
166
0
                                },
167
0
                            ),
168
0
                            nom::bytes::streaming::tag("-"),
169
0
                            nom::combinator::map_opt(
170
0
                                nom::bytes::complete::take_while(|c: char| {
171
                                    c.is_ascii_digit() || ('a'..='f').contains(&c)
172
0
                                }),
173
0
                                |k: &str| {
174
                                    if k.len() == 64 {
175
                                        Some(<[u8; 32]>::try_from(hex::decode(k).unwrap()).unwrap())
176
                                    } else {
177
                                        None
178
                                    }
179
0
                                },
180
0
                            ),
181
0
                        ))),
182
0
                    );
183
184
0
                let (namespace, _, algorithm, _, public_key) = match parser(&file_name) {
185
0
                    Ok((_, v)) => v,
186
0
                    Err(_) => continue,
187
                };
188
189
                // Make sure that the content of the file is valid and that it corresponds to
190
                // the public key advertised in the file name.
191
0
                match algorithm {
192
                    PrivateKey::FileEd25519 => {
193
0
                        match Self::load_ed25519_from_file(keys_directory.join(entry.path())).await
194
                        {
195
0
                            Ok(kp) => {
196
0
                                if ed25519_zebra::VerificationKey::from(&*kp).as_ref() != public_key
197
                                {
198
0
                                    continue;
199
0
                                }
200
                            }
201
0
                            Err(_) => continue,
202
                        }
203
                    }
204
                    PrivateKey::FileSr25519 => {
205
0
                        match Self::load_sr25519_from_file(keys_directory.join(entry.path())).await
206
                        {
207
0
                            Ok(kp) => {
208
0
                                if kp.public.to_bytes() != public_key {
209
0
                                    continue;
210
0
                                }
211
                            }
212
0
                            Err(err) => panic!("{err:?}"),
213
                        }
214
                    }
215
0
                    _ => unreachable!(),
216
                }
217
218
0
                keys.insert((namespace, public_key), algorithm);
219
            }
220
2
        }
221
222
2
        Ok(Keystore {
223
2
            keys_directory,
224
2
            guarded: Mutex::new(Guarded { gen_rng, keys }),
225
2
            sr25519_signing_context: schnorrkel::signing_context(b"substrate"),
226
2
        })
227
2
    }
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore3new0CscDgN54JpMGG_6author
_RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore3new0CsibGXYHQB8Ea_25json_rpc_general_requests
Line
Count
Source
122
19
    ) -> Result<Self, io::Error> {
123
19
        let mut gen_rng = rand_chacha::ChaCha20Rng::from_seed(randomness_seed);
124
19
125
19
        let mut keys = hashbrown::HashMap::with_capacity_and_hasher(32, {
126
19
            SipHasherBuild::new({
127
19
                let mut seed = [0; 16];
128
19
                gen_rng.fill_bytes(&mut seed);
129
19
                seed
130
19
            })
131
19
        });
132
133
        // Load the keys from the disk.
134
        // TODO: return some diagnostic about invalid files?
135
19
        if let Some(
keys_directory0
) = &keys_directory {
136
0
            if !keys_directory.try_exists()? {
137
0
                fs::create_dir_all(keys_directory)?;
138
0
            }
139
140
0
            for entry in fs::read_dir(keys_directory)? {
141
0
                let entry = entry?;
142
0
                if entry.file_type()?.is_dir() {
143
0
                    continue;
144
0
                }
145
146
                // Try to match the file name.
147
0
                let file_name = match entry.file_name().into_string() {
148
0
                    Ok(n) => n,
149
0
                    Err(_) => continue,
150
                };
151
152
0
                let mut parser =
153
0
                    nom::combinator::all_consuming::<_, _, (&str, nom::error::ErrorKind), _>(
154
0
                        nom::combinator::complete(nom::sequence::tuple((
155
0
                            nom::combinator::map_opt(
156
0
                                nom::bytes::streaming::take(4u32),
157
0
                                KeyNamespace::from_string,
158
0
                            ),
159
0
                            nom::bytes::streaming::tag("-"),
160
0
                            nom::combinator::map_opt(
161
0
                                nom::bytes::streaming::take(7u32),
162
0
                                |b| match b {
163
                                    "ed25519" => Some(PrivateKey::FileEd25519),
164
                                    "sr25519" => Some(PrivateKey::FileSr25519),
165
                                    _ => None,
166
0
                                },
167
0
                            ),
168
0
                            nom::bytes::streaming::tag("-"),
169
0
                            nom::combinator::map_opt(
170
0
                                nom::bytes::complete::take_while(|c: char| {
171
                                    c.is_ascii_digit() || ('a'..='f').contains(&c)
172
0
                                }),
173
0
                                |k: &str| {
174
                                    if k.len() == 64 {
175
                                        Some(<[u8; 32]>::try_from(hex::decode(k).unwrap()).unwrap())
176
                                    } else {
177
                                        None
178
                                    }
179
0
                                },
180
0
                            ),
181
0
                        ))),
182
0
                    );
183
184
0
                let (namespace, _, algorithm, _, public_key) = match parser(&file_name) {
185
0
                    Ok((_, v)) => v,
186
0
                    Err(_) => continue,
187
                };
188
189
                // Make sure that the content of the file is valid and that it corresponds to
190
                // the public key advertised in the file name.
191
0
                match algorithm {
192
                    PrivateKey::FileEd25519 => {
193
0
                        match Self::load_ed25519_from_file(keys_directory.join(entry.path())).await
194
                        {
195
0
                            Ok(kp) => {
196
0
                                if ed25519_zebra::VerificationKey::from(&*kp).as_ref() != public_key
197
                                {
198
0
                                    continue;
199
0
                                }
200
                            }
201
0
                            Err(_) => continue,
202
                        }
203
                    }
204
                    PrivateKey::FileSr25519 => {
205
0
                        match Self::load_sr25519_from_file(keys_directory.join(entry.path())).await
206
                        {
207
0
                            Ok(kp) => {
208
0
                                if kp.public.to_bytes() != public_key {
209
0
                                    continue;
210
0
                                }
211
                            }
212
0
                            Err(err) => panic!("{err:?}"),
213
                        }
214
                    }
215
0
                    _ => unreachable!(),
216
                }
217
218
0
                keys.insert((namespace, public_key), algorithm);
219
            }
220
19
        }
221
222
19
        Ok(Keystore {
223
19
            keys_directory,
224
19
            guarded: Mutex::new(Guarded { gen_rng, keys }),
225
19
            sr25519_signing_context: schnorrkel::signing_context(b"substrate"),
226
19
        })
227
19
    }
228
229
    /// Inserts an Sr25519 private key in the keystore.
230
    ///
231
    /// Returns the corresponding public key.
232
    ///
233
    /// This is meant to be called with publicly-known private keys. Use
234
    /// [`Keystore::generate_sr25519`] if the private key is meant to actually be private.
235
    ///
236
    /// The key is not saved on disk.
237
    ///
238
    /// # Panic
239
    ///
240
    /// Panics if the key isn't a valid Sr25519 private key. This function is meant to be used
241
    /// with hard coded values which are known to be correct. Please do not call it with any
242
    /// sort of user input.
243
    ///
244
0
    pub fn insert_sr25519_memory(
245
0
        &mut self,
246
0
        namespaces: impl Iterator<Item = KeyNamespace>,
247
0
        private_key: &[u8; 64],
248
0
    ) -> [u8; 32] {
249
0
        // TODO: we can't wrap this private_key into a `Zeroizing` because of the call to `to_keypair()` below, needs fixing in schnorrkel
250
0
        let private_key = schnorrkel::SecretKey::from_bytes(&private_key[..]).unwrap();
251
0
        let keypair = zeroize::Zeroizing::new(private_key.to_keypair());
252
0
        let public_key = keypair.public.to_bytes();
253
254
0
        for namespace in namespaces {
255
0
            self.guarded.get_mut().keys.insert(
256
0
                (namespace, public_key),
257
0
                PrivateKey::MemorySr25519(keypair.clone()),
258
0
            );
259
0
        }
260
261
0
        public_key
262
0
    }
Unexecuted instantiation: _RINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_8Keystore21insert_sr25519_memorypEB9_
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore21insert_sr25519_memorypEB9_
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore21insert_sr25519_memoryINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_12KeyNamespaceKj5_EECsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore21insert_sr25519_memoryINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_12KeyNamespaceKj5_EECscDgN54JpMGG_6author
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore21insert_sr25519_memoryINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_12KeyNamespaceKj5_EECsibGXYHQB8Ea_25json_rpc_general_requests
263
264
    /// Generates a new Ed25519 key and inserts it in the keystore.
265
    ///
266
    /// If `save` is `true`, the generated key is saved in the file system. This function returns
267
    /// an error only if `save` is `true` and the key couldn't be written to the file system.
268
    /// The value of `save` is silently ignored if no path was provided to [`Keystore::new`].
269
    ///
270
    /// Returns the corresponding public key.
271
1
    pub async fn generate_ed25519(
272
1
        &self,
273
1
        namespace: KeyNamespace,
274
1
        save: bool,
275
1
    ) -> Result<[u8; 32], io::Error> {
_RNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB4_8Keystore16generate_ed25519
Line
Count
Source
271
1
    pub async fn generate_ed25519(
272
1
        &self,
273
1
        namespace: KeyNamespace,
274
1
        save: bool,
275
1
    ) -> Result<[u8; 32], io::Error> {
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB4_8Keystore16generate_ed25519
276
1
        let mut guarded = self.guarded.lock().
await0
;
277
278
        // Note: it is in principle possible to generate some entropy from the PRNG, then unlock
279
        // the mutex while the private key is being generated. This reduces the time during which
280
        // the mutex is locked, but in practice generating a key is a rare enough event that this
281
        // is not worth the effort.
282
1
        let private_key =
283
1
            zeroize::Zeroizing::new(ed25519_zebra::SigningKey::new(&mut guarded.gen_rng));
284
1
        let public_key: [u8; 32] = ed25519_zebra::VerificationKey::from(&*private_key).into();
285
286
1
        let save_path = if save {
287
1
            self.path_of_key_ed25519(namespace, &public_key)
288
        } else {
289
0
            None
290
        };
291
292
1
        if let Some(save_path) = save_path {
293
1
            Self::write_to_file_ed25519(&save_path, &private_key).
await0
?0
;
294
1
            guarded
295
1
                .keys
296
1
                .insert((namespace, public_key), PrivateKey::FileEd25519);
297
0
        } else {
298
0
            guarded.keys.insert(
299
0
                (namespace, public_key),
300
0
                PrivateKey::MemoryEd25519(private_key),
301
0
            );
302
0
        }
303
304
1
        Ok(public_key)
305
1
    }
_RNCNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB6_8Keystore16generate_ed255190Ba_
Line
Count
Source
275
1
    ) -> Result<[u8; 32], io::Error> {
276
1
        let mut guarded = self.guarded.lock().
await0
;
277
278
        // Note: it is in principle possible to generate some entropy from the PRNG, then unlock
279
        // the mutex while the private key is being generated. This reduces the time during which
280
        // the mutex is locked, but in practice generating a key is a rare enough event that this
281
        // is not worth the effort.
282
1
        let private_key =
283
1
            zeroize::Zeroizing::new(ed25519_zebra::SigningKey::new(&mut guarded.gen_rng));
284
1
        let public_key: [u8; 32] = ed25519_zebra::VerificationKey::from(&*private_key).into();
285
286
1
        let save_path = if save {
287
1
            self.path_of_key_ed25519(namespace, &public_key)
288
        } else {
289
0
            None
290
        };
291
292
1
        if let Some(save_path) = save_path {
293
1
            Self::write_to_file_ed25519(&save_path, &private_key).
await0
?0
;
294
1
            guarded
295
1
                .keys
296
1
                .insert((namespace, public_key), PrivateKey::FileEd25519);
297
0
        } else {
298
0
            guarded.keys.insert(
299
0
                (namespace, public_key),
300
0
                PrivateKey::MemoryEd25519(private_key),
301
0
            );
302
0
        }
303
304
1
        Ok(public_key)
305
1
    }
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore16generate_ed255190Ba_
306
307
    /// Returns the list of all keys known to this keystore.
308
    ///
309
    /// > **Note**: Keep in mind that this function is racy, as keys can be added and removed
310
    /// >           in parallel of this function being called.
311
2
    pub async fn keys(&self) -> impl Iterator<Item = (KeyNamespace, [u8; 32])> {
_RNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB4_8Keystore4keys
Line
Count
Source
311
2
    pub async fn keys(&self) -> impl Iterator<Item = (KeyNamespace, [u8; 32])> {
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB4_8Keystore4keys
312
2
        let guarded = self.guarded.lock().
await0
;
313
2
        guarded.keys.keys().cloned().collect::<Vec<_>>().into_iter()
314
2
    }
_RNCNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB6_8Keystore4keys0Ba_
Line
Count
Source
311
2
    pub async fn keys(&self) -> impl Iterator<Item = (KeyNamespace, [u8; 32])> {
312
2
        let guarded = self.guarded.lock().
await0
;
313
2
        guarded.keys.keys().cloned().collect::<Vec<_>>().into_iter()
314
2
    }
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore4keys0Ba_
315
316
    /// Generates a new Sr25519 key and inserts it in the keystore.
317
    ///
318
    /// If `save` is `true`, the generated key is saved in the file system. This function returns
319
    /// an error only if `save` is `true` and the key couldn't be written to the file system.
320
    /// The value of `save` is silently ignored if no path was provided to [`Keystore::new`].
321
    ///
322
    /// Returns the corresponding public key.
323
1
    pub async fn generate_sr25519(
324
1
        &self,
325
1
        namespace: KeyNamespace,
326
1
        save: bool,
327
1
    ) -> Result<[u8; 32], io::Error> {
_RNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB4_8Keystore16generate_sr25519
Line
Count
Source
323
1
    pub async fn generate_sr25519(
324
1
        &self,
325
1
        namespace: KeyNamespace,
326
1
        save: bool,
327
1
    ) -> Result<[u8; 32], io::Error> {
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB4_8Keystore16generate_sr25519
328
1
        let mut guarded = self.guarded.lock().
await0
;
329
330
        // Note: it is in principle possible to generate some entropy from the PRNG, then unlock
331
        // the mutex while the private key is being generated. This reduces the time during which
332
        // the mutex is locked, but in practice generating a key is a rare enough event that this
333
        // is not worth the effort.
334
1
        let mini_secret = zeroize::Zeroizing::new(schnorrkel::MiniSecretKey::generate_with(
335
1
            &mut guarded.gen_rng,
336
1
        ));
337
1
        let keypair = zeroize::Zeroizing::new(
338
1
            mini_secret.expand_to_keypair(schnorrkel::ExpansionMode::Ed25519),
339
1
        );
340
1
        let public_key = keypair.public.to_bytes();
341
342
1
        let save_path = if save {
343
1
            self.path_of_key_sr25519(namespace, &public_key)
344
        } else {
345
0
            None
346
        };
347
348
1
        if let Some(save_path) = save_path {
349
1
            Self::write_to_file_sr25519(&save_path, &mini_secret).
await0
?0
;
350
1
            guarded
351
1
                .keys
352
1
                .insert((namespace, public_key), PrivateKey::FileSr25519);
353
0
        } else {
354
0
            guarded
355
0
                .keys
356
0
                .insert((namespace, public_key), PrivateKey::MemorySr25519(keypair));
357
0
        }
358
359
1
        Ok(public_key)
360
1
    }
_RNCNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB6_8Keystore16generate_sr255190Ba_
Line
Count
Source
327
1
    ) -> Result<[u8; 32], io::Error> {
328
1
        let mut guarded = self.guarded.lock().
await0
;
329
330
        // Note: it is in principle possible to generate some entropy from the PRNG, then unlock
331
        // the mutex while the private key is being generated. This reduces the time during which
332
        // the mutex is locked, but in practice generating a key is a rare enough event that this
333
        // is not worth the effort.
334
1
        let mini_secret = zeroize::Zeroizing::new(schnorrkel::MiniSecretKey::generate_with(
335
1
            &mut guarded.gen_rng,
336
1
        ));
337
1
        let keypair = zeroize::Zeroizing::new(
338
1
            mini_secret.expand_to_keypair(schnorrkel::ExpansionMode::Ed25519),
339
1
        );
340
1
        let public_key = keypair.public.to_bytes();
341
342
1
        let save_path = if save {
343
1
            self.path_of_key_sr25519(namespace, &public_key)
344
        } else {
345
0
            None
346
        };
347
348
1
        if let Some(save_path) = save_path {
349
1
            Self::write_to_file_sr25519(&save_path, &mini_secret).
await0
?0
;
350
1
            guarded
351
1
                .keys
352
1
                .insert((namespace, public_key), PrivateKey::FileSr25519);
353
0
        } else {
354
0
            guarded
355
0
                .keys
356
0
                .insert((namespace, public_key), PrivateKey::MemorySr25519(keypair));
357
0
        }
358
359
1
        Ok(public_key)
360
1
    }
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore16generate_sr255190Ba_
361
362
    /// Signs the given payload using the private key associated to the public key passed as
363
    /// parameter.
364
    ///
365
    /// An error is returned if the key-namespace combination is not in the keystore, or if the
366
    /// key couldn't be loaded from disk. In the case when a key couldn't be loaded from disk, it
367
    /// is automatically removed from the keystore.
368
2
    pub async fn sign(
369
2
        &self,
370
2
        key_namespace: KeyNamespace,
371
2
        public_key: &[u8; 32],
372
2
        payload: &[u8],
373
2
    ) -> Result<[u8; 64], SignError> {
_RNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB4_8Keystore4sign
Line
Count
Source
368
2
    pub async fn sign(
369
2
        &self,
370
2
        key_namespace: KeyNamespace,
371
2
        public_key: &[u8; 32],
372
2
        payload: &[u8],
373
2
    ) -> Result<[u8; 64], SignError> {
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB4_8Keystore4sign
374
2
        let mut guarded = self.guarded.lock().
await0
;
375
2
        let key = guarded
376
2
            .keys
377
2
            .get(&(key_namespace, *public_key))
378
2
            .ok_or(SignError::UnknownPublicKey)
?0
;
379
380
2
        match key {
381
0
            PrivateKey::MemoryEd25519(key) => Ok(key.sign(payload).into()),
382
            PrivateKey::FileEd25519 => {
383
1
                match Self::load_ed25519_from_file(
384
1
                    self.path_of_key_ed25519(key_namespace, public_key).unwrap(),
385
1
                )
386
0
                .await
387
                {
388
1
                    Ok(key) => {
389
1
                        drop(guarded);
390
1
                        Ok(key.sign(payload).into())
391
                    }
392
0
                    Err(err) => {
393
0
                        guarded.keys.remove(&(key_namespace, *public_key));
394
0
                        Err(err.into())
395
                    }
396
                }
397
            }
398
0
            PrivateKey::MemorySr25519(key) => Ok(key
399
0
                .sign(self.sr25519_signing_context.bytes(payload))
400
0
                .to_bytes()),
401
            PrivateKey::FileSr25519 => {
402
1
                match Self::load_sr25519_from_file(
403
1
                    self.path_of_key_sr25519(key_namespace, public_key).unwrap(),
404
1
                )
405
0
                .await
406
                {
407
1
                    Ok(key) => {
408
1
                        drop(guarded);
409
1
                        Ok(key
410
1
                            .sign(self.sr25519_signing_context.bytes(payload))
411
1
                            .to_bytes())
412
                    }
413
0
                    Err(err) => {
414
0
                        guarded.keys.remove(&(key_namespace, *public_key));
415
0
                        Err(err.into())
416
                    }
417
                }
418
            }
419
        }
420
2
    }
_RNCNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB6_8Keystore4sign0Ba_
Line
Count
Source
373
2
    ) -> Result<[u8; 64], SignError> {
374
2
        let mut guarded = self.guarded.lock().
await0
;
375
2
        let key = guarded
376
2
            .keys
377
2
            .get(&(key_namespace, *public_key))
378
2
            .ok_or(SignError::UnknownPublicKey)
?0
;
379
380
2
        match key {
381
0
            PrivateKey::MemoryEd25519(key) => Ok(key.sign(payload).into()),
382
            PrivateKey::FileEd25519 => {
383
1
                match Self::load_ed25519_from_file(
384
1
                    self.path_of_key_ed25519(key_namespace, public_key).unwrap(),
385
1
                )
386
0
                .await
387
                {
388
1
                    Ok(key) => {
389
1
                        drop(guarded);
390
1
                        Ok(key.sign(payload).into())
391
                    }
392
0
                    Err(err) => {
393
0
                        guarded.keys.remove(&(key_namespace, *public_key));
394
0
                        Err(err.into())
395
                    }
396
                }
397
            }
398
0
            PrivateKey::MemorySr25519(key) => Ok(key
399
0
                .sign(self.sr25519_signing_context.bytes(payload))
400
0
                .to_bytes()),
401
            PrivateKey::FileSr25519 => {
402
1
                match Self::load_sr25519_from_file(
403
1
                    self.path_of_key_sr25519(key_namespace, public_key).unwrap(),
404
1
                )
405
0
                .await
406
                {
407
1
                    Ok(key) => {
408
1
                        drop(guarded);
409
1
                        Ok(key
410
1
                            .sign(self.sr25519_signing_context.bytes(payload))
411
1
                            .to_bytes())
412
                    }
413
0
                    Err(err) => {
414
0
                        guarded.keys.remove(&(key_namespace, *public_key));
415
0
                        Err(err.into())
416
                    }
417
                }
418
            }
419
        }
420
2
    }
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore4sign0Ba_
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore4sign0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore4sign0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB6_8Keystore4sign0CsibGXYHQB8Ea_25json_rpc_general_requests
421
422
    // TODO: doc
423
    ///
424
    /// Note that the labels must be `'static` due to requirements from the underlying library.
425
    // TODO: unclear why this can't be an async function; getting lifetime errors
426
0
    pub fn sign_sr25519_vrf<'a>(
427
0
        &'a self,
428
0
        key_namespace: KeyNamespace,
429
0
        public_key: &'a [u8; 32],
430
0
        label: &'static [u8],
431
0
        transcript_items: impl Iterator<Item = (&'static [u8], either::Either<&'a [u8], u64>)> + 'a,
432
0
    ) -> impl core::future::Future<Output = Result<VrfSignature, SignVrfError>> + 'a {
433
0
        async move {
434
0
            let mut guarded = self.guarded.lock().await;
435
0
            let key = guarded
436
0
                .keys
437
0
                .get(&(key_namespace, *public_key))
438
0
                .ok_or(SignVrfError::Sign(SignError::UnknownPublicKey))?;
439
440
0
            match key {
441
                PrivateKey::MemoryEd25519(_) | PrivateKey::FileEd25519 => {
442
0
                    Err(SignVrfError::WrongKeyAlgorithm)
443
                }
444
                PrivateKey::MemorySr25519(_) | PrivateKey::FileSr25519 => {
445
0
                    let key = match key {
446
0
                        PrivateKey::MemorySr25519(key) => Cow::Borrowed(key),
447
                        PrivateKey::FileSr25519 => {
448
0
                            match Self::load_sr25519_from_file(
449
0
                                self.path_of_key_sr25519(key_namespace, public_key).unwrap(),
450
0
                            )
451
0
                            .await
452
                            {
453
0
                                Ok(key) => {
454
0
                                    drop(guarded);
455
0
                                    Cow::Owned(key)
456
                                }
457
0
                                Err(err) => {
458
0
                                    guarded.keys.remove(&(key_namespace, *public_key));
459
0
                                    return Err(err.into());
460
                                }
461
                            }
462
                        }
463
0
                        _ => unreachable!(),
464
                    };
465
466
0
                    let mut transcript = merlin::Transcript::new(label);
467
0
                    for (label, value) in transcript_items {
468
0
                        match value {
469
0
                            either::Left(bytes) => {
470
0
                                transcript.append_message(label, bytes);
471
0
                            }
472
0
                            either::Right(value) => {
473
0
                                transcript.append_u64(label, value);
474
0
                            }
475
                        }
476
                    }
477
478
0
                    let (_in_out, proof, _) = key.vrf_sign(transcript);
479
0
                    Ok(VrfSignature {
480
0
                        // TODO: should probably output the `_in_out` as well
481
0
                        proof: proof.to_bytes(),
482
0
                    })
483
                }
484
            }
485
0
        }
Unexecuted instantiation: _RNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB7_8Keystore16sign_sr25519_vrfpE0Bb_
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore16sign_sr25519_vrfpE0Bb_
486
0
    }
Unexecuted instantiation: _RINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_8Keystore16sign_sr25519_vrfpEB9_
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore16sign_sr25519_vrfpEB9_
487
488
2
    async fn load_ed25519_from_file(
489
2
        path: impl AsRef<path::Path>,
490
2
    ) -> Result<zeroize::Zeroizing<ed25519_zebra::SigningKey>, KeyLoadError> {
_RINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufEB9_
Line
Count
Source
488
2
    async fn load_ed25519_from_file(
489
2
        path: impl AsRef<path::Path>,
490
2
    ) -> Result<zeroize::Zeroizing<ed25519_zebra::SigningKey>, KeyLoadError> {
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore22load_ed25519_from_filepEB9_
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufECsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufECscDgN54JpMGG_6author
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufECsibGXYHQB8Ea_25json_rpc_general_requests
491
        // TODO: read asynchronously?
492
2
        let bytes = fs::read(path).map_err(KeyLoadError::Io)
?0
;
493
2
        let phrase =
494
2
            str::from_utf8(&bytes).map_err(|err| 
KeyLoadError::BadFormat(err.to_string())0
)
?0
;
Unexecuted instantiation: _RNCNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE00Bd_
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_filepE00Bd_
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE00CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE00CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE00CsibGXYHQB8Ea_25json_rpc_general_requests
495
2
        let mut private_key = seed_phrase::decode_ed25519_private_key(phrase)
496
2
            .map_err(|err| 
KeyLoadError::BadFormat(err.to_string())0
)
?0
;
Unexecuted instantiation: _RNCNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0s_0Bd_
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_filepE0s_0Bd_
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0s_0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0s_0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0s_0CsibGXYHQB8Ea_25json_rpc_general_requests
497
2
        let zebra_key = zeroize::Zeroizing::new(ed25519_zebra::SigningKey::from(*private_key));
498
2
        zeroize::Zeroize::zeroize(&mut *private_key);
499
2
        Ok(zebra_key)
500
2
    }
_RNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB7_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0Bb_
Line
Count
Source
490
2
    ) -> Result<zeroize::Zeroizing<ed25519_zebra::SigningKey>, KeyLoadError> {
491
        // TODO: read asynchronously?
492
2
        let bytes = fs::read(path).map_err(KeyLoadError::Io)
?0
;
493
2
        let phrase =
494
2
            str::from_utf8(&bytes).map_err(|err| KeyLoadError::BadFormat(err.to_string()))
?0
;
495
2
        let mut private_key = seed_phrase::decode_ed25519_private_key(phrase)
496
2
            .map_err(|err| KeyLoadError::BadFormat(err.to_string()))
?0
;
497
2
        let zebra_key = zeroize::Zeroizing::new(ed25519_zebra::SigningKey::from(*private_key));
498
2
        zeroize::Zeroize::zeroize(&mut *private_key);
499
2
        Ok(zebra_key)
500
2
    }
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore22load_ed25519_from_filepE0Bb_
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore22load_ed25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0CsibGXYHQB8Ea_25json_rpc_general_requests
501
502
2
    async fn load_sr25519_from_file(
503
2
        path: impl AsRef<path::Path>,
504
2
    ) -> Result<zeroize::Zeroizing<schnorrkel::Keypair>, KeyLoadError> {
_RINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufEB9_
Line
Count
Source
502
2
    async fn load_sr25519_from_file(
503
2
        path: impl AsRef<path::Path>,
504
2
    ) -> Result<zeroize::Zeroizing<schnorrkel::Keypair>, KeyLoadError> {
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore22load_sr25519_from_filepEB9_
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufECsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufECscDgN54JpMGG_6author
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufECsibGXYHQB8Ea_25json_rpc_general_requests
505
        // TODO: read asynchronously?
506
2
        let bytes = fs::read(path).map_err(KeyLoadError::Io)
?0
;
507
2
        let phrase =
508
2
            str::from_utf8(&bytes).map_err(|err| 
KeyLoadError::BadFormat(err.to_string())0
)
?0
;
Unexecuted instantiation: _RNCNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE00Bd_
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_filepE00Bd_
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE00CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE00CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE00CsibGXYHQB8Ea_25json_rpc_general_requests
509
2
        let mut private_key = seed_phrase::decode_sr25519_private_key(phrase)
510
2
            .map_err(|err| 
KeyLoadError::BadFormat(err.to_string())0
)
?0
;
Unexecuted instantiation: _RNCNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0s_0Bd_
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_filepE0s_0Bd_
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0s_0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0s_0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB9_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0s_0CsibGXYHQB8Ea_25json_rpc_general_requests
511
        // `from_bytes` only panics if the key is of the wrong length, which we know can't
512
        // happen here.
513
2
        let schnorrkel_key = zeroize::Zeroizing::new(
514
2
            schnorrkel::SecretKey::from_bytes(&*private_key)
515
2
                .unwrap()
516
2
                .into(),
517
2
        );
518
2
        zeroize::Zeroize::zeroize(&mut *private_key);
519
2
        Ok(schnorrkel_key)
520
2
    }
_RNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB7_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0Bb_
Line
Count
Source
504
2
    ) -> Result<zeroize::Zeroizing<schnorrkel::Keypair>, KeyLoadError> {
505
        // TODO: read asynchronously?
506
2
        let bytes = fs::read(path).map_err(KeyLoadError::Io)
?0
;
507
2
        let phrase =
508
2
            str::from_utf8(&bytes).map_err(|err| KeyLoadError::BadFormat(err.to_string()))
?0
;
509
2
        let mut private_key = seed_phrase::decode_sr25519_private_key(phrase)
510
2
            .map_err(|err| KeyLoadError::BadFormat(err.to_string()))
?0
;
511
        // `from_bytes` only panics if the key is of the wrong length, which we know can't
512
        // happen here.
513
2
        let schnorrkel_key = zeroize::Zeroizing::new(
514
2
            schnorrkel::SecretKey::from_bytes(&*private_key)
515
2
                .unwrap()
516
2
                .into(),
517
2
        );
518
2
        zeroize::Zeroize::zeroize(&mut *private_key);
519
2
        Ok(schnorrkel_key)
520
2
    }
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore22load_sr25519_from_filepE0Bb_
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0CsiLzmwikkc22_14json_rpc_basic
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0CscDgN54JpMGG_6author
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore22load_sr25519_from_fileNtNtCsbpXXxgr6u8g_3std4path7PathBufE0CsibGXYHQB8Ea_25json_rpc_general_requests
521
522
1
    async fn write_to_file_ed25519(
523
1
        path: impl AsRef<path::Path>,
524
1
        key: &ed25519_zebra::SigningKey,
525
1
    ) -> Result<(), io::Error> {
_RINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_8Keystore21write_to_file_ed25519RNtNtCsbpXXxgr6u8g_3std4path7PathBufEB9_
Line
Count
Source
522
1
    async fn write_to_file_ed25519(
523
1
        path: impl AsRef<path::Path>,
524
1
        key: &ed25519_zebra::SigningKey,
525
1
    ) -> Result<(), io::Error> {
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore21write_to_file_ed25519pEB9_
526
1
        let mut phrase = zeroize::Zeroizing::new(vec![0; key.as_ref().len() * 2]);
527
1
        hex::encode_to_slice(key.as_ref(), &mut phrase).unwrap();
528
1
        Self::write_to_file(path, &phrase).
await0
529
1
    }
_RNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB7_8Keystore21write_to_file_ed25519RNtNtCsbpXXxgr6u8g_3std4path7PathBufE0Bb_
Line
Count
Source
525
1
    ) -> Result<(), io::Error> {
526
1
        let mut phrase = zeroize::Zeroizing::new(vec![0; key.as_ref().len() * 2]);
527
1
        hex::encode_to_slice(key.as_ref(), &mut phrase).unwrap();
528
1
        Self::write_to_file(path, &phrase).
await0
529
1
    }
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore21write_to_file_ed25519pE0Bb_
530
531
1
    async fn write_to_file_sr25519(
532
1
        path: impl AsRef<path::Path>,
533
1
        key: &schnorrkel::MiniSecretKey,
534
1
    ) -> Result<(), io::Error> {
_RINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_8Keystore21write_to_file_sr25519RNtNtCsbpXXxgr6u8g_3std4path7PathBufEB9_
Line
Count
Source
531
1
    async fn write_to_file_sr25519(
532
1
        path: impl AsRef<path::Path>,
533
1
        key: &schnorrkel::MiniSecretKey,
534
1
    ) -> Result<(), io::Error> {
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore21write_to_file_sr25519pEB9_
535
1
        // TODO: `to_bytes` isn't zeroize-friendly
536
1
        let bytes = key.to_bytes();
537
1
        let mut phrase = zeroize::Zeroizing::new(vec![0; bytes.len() * 2]);
538
1
        hex::encode_to_slice(bytes, &mut phrase).unwrap();
539
1
        Self::write_to_file(path, &phrase).
await0
540
1
    }
_RNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB7_8Keystore21write_to_file_sr25519RNtNtCsbpXXxgr6u8g_3std4path7PathBufE0Bb_
Line
Count
Source
534
1
    ) -> Result<(), io::Error> {
535
1
        // TODO: `to_bytes` isn't zeroize-friendly
536
1
        let bytes = key.to_bytes();
537
1
        let mut phrase = zeroize::Zeroizing::new(vec![0; bytes.len() * 2]);
538
1
        hex::encode_to_slice(bytes, &mut phrase).unwrap();
539
1
        Self::write_to_file(path, &phrase).
await0
540
1
    }
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore21write_to_file_sr25519pE0Bb_
541
542
2
    async fn write_to_file(
543
2
        path: impl AsRef<path::Path>,
544
2
        key_phrase: &[u8],
545
2
    ) -> Result<(), io::Error> {
_RINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_8Keystore13write_to_fileRNtNtCsbpXXxgr6u8g_3std4path7PathBufEB9_
Line
Count
Source
542
2
    async fn write_to_file(
543
2
        path: impl AsRef<path::Path>,
544
2
        key_phrase: &[u8],
545
2
    ) -> Result<(), io::Error> {
Unexecuted instantiation: _RINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_8Keystore13write_to_filepEB9_
546
2
        let mut file = fs::File::create(path)
?0
;
547
        // TODO: proper security flags on Windows?
548
        #[cfg(target_family = "unix")]
549
2
        file.set_permissions(std::os::unix::fs::PermissionsExt::from_mode(0o400))
?0
;
550
2
        io::Write::write_all(&mut file, b"0x")
?0
;
551
2
        io::Write::write_all(&mut file, key_phrase)
?0
;
552
2
        io::Write::flush(&mut file)
?0
; // This call is generally useless, but doesn't hurt.
553
2
        file.sync_all()
?0
;
554
2
        Ok(())
555
2
    }
_RNCINvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB7_8Keystore13write_to_fileRNtNtCsbpXXxgr6u8g_3std4path7PathBufE0Bb_
Line
Count
Source
545
2
    ) -> Result<(), io::Error> {
546
2
        let mut file = fs::File::create(path)
?0
;
547
        // TODO: proper security flags on Windows?
548
        #[cfg(target_family = "unix")]
549
2
        file.set_permissions(std::os::unix::fs::PermissionsExt::from_mode(0o400))
?0
;
550
2
        io::Write::write_all(&mut file, b"0x")
?0
;
551
2
        io::Write::write_all(&mut file, key_phrase)
?0
;
552
2
        io::Write::flush(&mut file)
?0
; // This call is generally useless, but doesn't hurt.
553
2
        file.sync_all()
?0
;
554
2
        Ok(())
555
2
    }
Unexecuted instantiation: _RNCINvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB7_8Keystore13write_to_filepE0Bb_
556
557
2
    fn path_of_key_ed25519(
558
2
        &self,
559
2
        key_namespace: KeyNamespace,
560
2
        public_key: &[u8; 32],
561
2
    ) -> Option<path::PathBuf> {
562
2
        self.path_of_key(key_namespace, "ed25519", public_key)
563
2
    }
_RNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB4_8Keystore19path_of_key_ed25519
Line
Count
Source
557
2
    fn path_of_key_ed25519(
558
2
        &self,
559
2
        key_namespace: KeyNamespace,
560
2
        public_key: &[u8; 32],
561
2
    ) -> Option<path::PathBuf> {
562
2
        self.path_of_key(key_namespace, "ed25519", public_key)
563
2
    }
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB4_8Keystore19path_of_key_ed25519
564
565
2
    fn path_of_key_sr25519(
566
2
        &self,
567
2
        key_namespace: KeyNamespace,
568
2
        public_key: &[u8; 32],
569
2
    ) -> Option<path::PathBuf> {
570
2
        self.path_of_key(key_namespace, "sr25519", public_key)
571
2
    }
_RNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB4_8Keystore19path_of_key_sr25519
Line
Count
Source
565
2
    fn path_of_key_sr25519(
566
2
        &self,
567
2
        key_namespace: KeyNamespace,
568
2
        public_key: &[u8; 32],
569
2
    ) -> Option<path::PathBuf> {
570
2
        self.path_of_key(key_namespace, "sr25519", public_key)
571
2
    }
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB4_8Keystore19path_of_key_sr25519
572
573
4
    fn path_of_key(
574
4
        &self,
575
4
        key_namespace: KeyNamespace,
576
4
        key_algorithm: &str,
577
4
        public_key: &[u8; 32],
578
4
    ) -> Option<path::PathBuf> {
579
4
        let keys_directory = match &self.keys_directory {
580
4
            Some(k) => k,
581
0
            None => return None,
582
        };
583
584
        // We don't use the same pathing scheme as Substrate, for two reasons:
585
        // - The fact that Substrate hex-encodes the namespace is completely unnecessary and
586
        // confusing.
587
        // - Substrate doesn't indicate whether the key is ed25519 or sr25519, because the
588
        // algorithm to use is provided when signing or verifying. This is weird and in my opinion
589
        // not a good practice.
590
591
4
        let mut file_name = String::with_capacity(256); // 256 is more than enough.
592
4
        file_name.push_str(key_namespace.as_string());
593
4
        file_name.push('-');
594
4
        file_name.push_str(key_algorithm);
595
4
        file_name.push('-');
596
4
        file_name.push_str(&hex::encode(public_key));
597
4
598
4
        let mut path =
599
4
            path::PathBuf::with_capacity(keys_directory.as_os_str().len() + file_name.len() + 16);
600
4
        path.push(keys_directory);
601
4
        path.push(file_name);
602
4
        Some(path)
603
4
    }
_RNvMs_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB4_8Keystore11path_of_key
Line
Count
Source
573
4
    fn path_of_key(
574
4
        &self,
575
4
        key_namespace: KeyNamespace,
576
4
        key_algorithm: &str,
577
4
        public_key: &[u8; 32],
578
4
    ) -> Option<path::PathBuf> {
579
4
        let keys_directory = match &self.keys_directory {
580
4
            Some(k) => k,
581
0
            None => return None,
582
        };
583
584
        // We don't use the same pathing scheme as Substrate, for two reasons:
585
        // - The fact that Substrate hex-encodes the namespace is completely unnecessary and
586
        // confusing.
587
        // - Substrate doesn't indicate whether the key is ed25519 or sr25519, because the
588
        // algorithm to use is provided when signing or verifying. This is weird and in my opinion
589
        // not a good practice.
590
591
4
        let mut file_name = String::with_capacity(256); // 256 is more than enough.
592
4
        file_name.push_str(key_namespace.as_string());
593
4
        file_name.push('-');
594
4
        file_name.push_str(key_algorithm);
595
4
        file_name.push('-');
596
4
        file_name.push_str(&hex::encode(public_key));
597
4
598
4
        let mut path =
599
4
            path::PathBuf::with_capacity(keys_directory.as_os_str().len() + file_name.len() + 16);
600
4
        path.push(keys_directory);
601
4
        path.push(file_name);
602
4
        Some(path)
603
4
    }
Unexecuted instantiation: _RNvMs_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB4_8Keystore11path_of_key
604
}
605
606
struct Guarded {
607
    gen_rng: rand_chacha::ChaCha20Rng,
608
    keys: hashbrown::HashMap<(KeyNamespace, [u8; 32]), PrivateKey, SipHasherBuild>,
609
}
610
611
pub struct VrfSignature {
612
    pub proof: [u8; 64],
613
}
614
615
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXsa_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_9SignErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXsa_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_9SignErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
616
pub enum SignError {
617
    /// The given `(namespace, public key)` combination is unknown to this keystore.
618
    UnknownPublicKey,
619
620
    /// Error while accessing the file containing the secret key.
621
    /// Typically indicates the content of the file has been modified by something else than
622
    /// the keystore.
623
    #[display(fmt = "Error loading the secret key; {_0}")]
624
    KeyLoad(KeyLoadError),
625
}
626
627
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXsc_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_12KeyLoadErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXsc_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_12KeyLoadErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
628
pub enum KeyLoadError {
629
    /// Error reported by the operating system.
630
    #[display(fmt = "{_0}")]
631
    Io(io::Error),
632
    /// Content of the file is invalid. Contains a human-readable error message as a string.
633
    /// Because the format of the content of the file is an implementation detail, no detail is
634
    /// provided.
635
    #[display(fmt = "{_0}")]
636
    BadFormat(String),
637
}
638
639
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXse_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_12SignVrfErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXse_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_12SignVrfErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
640
pub enum SignVrfError {
641
    #[display(fmt = "{_0}")]
642
    Sign(SignError),
643
    WrongKeyAlgorithm,
644
}
645
646
enum PrivateKey {
647
    MemoryEd25519(zeroize::Zeroizing<ed25519_zebra::SigningKey>),
648
    MemorySr25519(zeroize::Zeroizing<schnorrkel::Keypair>),
649
    FileEd25519,
650
    FileSr25519,
651
}
652
653
impl From<KeyLoadError> for SignError {
654
0
    fn from(err: KeyLoadError) -> SignError {
655
0
        SignError::KeyLoad(err)
656
0
    }
Unexecuted instantiation: _RNvXs0_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_9SignErrorINtNtCsaYZPK01V26L_4core7convert4FromNtB5_12KeyLoadErrorE4from
Unexecuted instantiation: _RNvXs0_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_9SignErrorINtNtCsaYZPK01V26L_4core7convert4FromNtB5_12KeyLoadErrorE4from
657
}
658
659
impl From<KeyLoadError> for SignVrfError {
660
0
    fn from(err: KeyLoadError) -> SignVrfError {
661
0
        SignVrfError::Sign(SignError::KeyLoad(err))
662
0
    }
Unexecuted instantiation: _RNvXs1_NtNtCsN16ciHI6Qf_7smoldot8identity8keystoreNtB5_12SignVrfErrorINtNtCsaYZPK01V26L_4core7convert4FromNtB5_12KeyLoadErrorE4from
Unexecuted instantiation: _RNvXs1_NtNtCseuYC0Zibziv_7smoldot8identity8keystoreNtB5_12SignVrfErrorINtNtCsaYZPK01V26L_4core7convert4FromNtB5_12KeyLoadErrorE4from
663
}
664
665
#[cfg(test)]
666
mod tests {
667
    use super::{KeyNamespace, Keystore};
668
669
    #[test]
670
1
    fn disk_storage_works_ed25519() {
671
1
        futures_executor::block_on(async move {
672
1
            let path = tempfile::tempdir().unwrap();
673
674
1
            let keystore1 = Keystore::new(Some(path.path().to_owned()), rand::random())
675
0
                .await
676
1
                .unwrap();
677
1
            let public_key = keystore1
678
1
                .generate_ed25519(KeyNamespace::Babe, true)
679
0
                .await
680
1
                .unwrap();
681
1
            drop(keystore1);
682
683
1
            let keystore2 = Keystore::new(Some(path.path().to_owned()), rand::random())
684
0
                .await
685
1
                .unwrap();
686
1
            assert_eq!(
687
1
                keystore2.keys().
await0
.next(),
688
1
                Some((KeyNamespace::Babe, public_key))
689
            );
690
691
1
            let signature = keystore2
692
1
                .sign(KeyNamespace::Babe, &public_key, b"hello world")
693
0
                .await
694
1
                .unwrap();
695
1
696
1
            assert!(ed25519_zebra::VerificationKey::try_from(public_key)
697
1
                .unwrap()
698
1
                .verify(&ed25519_zebra::Signature::from(signature), b"hello world")
699
1
                .is_ok());
700
1
        });
701
1
    }
702
703
    #[test]
704
1
    fn disk_storage_works_sr25519() {
705
1
        futures_executor::block_on(async move {
706
1
            let path = tempfile::tempdir().unwrap();
707
708
1
            let keystore1 = Keystore::new(Some(path.path().to_owned()), rand::random())
709
0
                .await
710
1
                .unwrap();
711
1
            let public_key = keystore1
712
1
                .generate_sr25519(KeyNamespace::Aura, true)
713
0
                .await
714
1
                .unwrap();
715
1
            drop(keystore1);
716
717
1
            let keystore2 = Keystore::new(Some(path.path().to_owned()), rand::random())
718
0
                .await
719
1
                .unwrap();
720
1
            assert_eq!(
721
1
                keystore2.keys().
await0
.next(),
722
1
                Some((KeyNamespace::Aura, public_key))
723
            );
724
725
1
            let signature = keystore2
726
1
                .sign(KeyNamespace::Aura, &public_key, b"hello world")
727
0
                .await
728
1
                .unwrap();
729
1
730
1
            assert!(schnorrkel::PublicKey::from_bytes(&public_key)
731
1
                .unwrap()
732
1
                .verify_simple(
733
1
                    b"substrate",
734
1
                    b"hello world",
735
1
                    &schnorrkel::Signature::from_bytes(&signature).unwrap()
736
1
                )
737
1
                .is_ok());
738
1
        });
739
1
    }
740
}