forked from slowli/secret-tree
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ed25519.rs
112 lines (100 loc) · 3.54 KB
/
ed25519.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright 2019 Alex Ostrovski
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http:https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Example how to store a `SecretTree` seed in the passphrase-encrypted form and use it
//! to derive heterogeneous keys.
use ed25519::Keypair;
use pwbox::{
rcrypto::{RustCrypto, Scrypt},
Eraser, Suite,
};
use rand6::thread_rng;
use secret_tree::{Name, SecretTree};
use std::fmt;
struct Keys {
consensus_keys: Keypair,
service_keys: Keypair,
other_secrets: Vec<u128>,
}
impl Keys {
pub fn new(tree: &SecretTree) -> Self {
let consensus = tree.child(Name::new("consensus"));
let service = tree.child(Name::new("service"));
let other = tree.child(Name::new("other"));
Keys {
consensus_keys: Keypair::generate(&mut consensus.rng()),
service_keys: Keypair::generate(&mut service.rng()),
other_secrets: (0..5)
.map(|i| {
let mut buffer = [0_u128];
other.index(i).fill(&mut buffer);
buffer[0]
})
.collect(),
}
}
}
impl fmt::Display for Keys {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut debug_struct = f.debug_struct("Keys");
debug_struct.field(
"consensus",
&hex::encode(self.consensus_keys.public.as_bytes()),
);
debug_struct.field("service", &hex::encode(self.service_keys.public.as_bytes()));
for (i, secret) in self.other_secrets.iter().enumerate() {
debug_struct.field(&format!("other/{}", i), secret);
}
debug_struct.finish()
}
}
fn main() {
// Generate a RNG tree randomly.
let mut rng = thread_rng();
let tree = SecretTree::new(&mut rng);
let keys = Keys::new(&tree);
println!("Original keys: {:#}\n", keys);
let public_keys = (keys.consensus_keys.public, keys.service_keys.public);
// Assume that we have securely persisted the RNG tree (e.g., with passphrase encryption).
let passphrase = "correct horse battery staple";
let secured_store = RustCrypto::build_box(&mut rng)
.kdf(if cfg!(debug_assertions) {
// Ultra-light parameters to get the test run fast in the debug mode.
Scrypt::custom(6, 16)
} else {
Scrypt::default()
})
.seal(passphrase, tree.seed())
.unwrap();
drop(tree);
let mut eraser = Eraser::new();
eraser.add_suite::<RustCrypto>();
let secured_store = eraser.erase(&secured_store).unwrap();
println!(
"Passphrase-encrypted RNG tree (TOML):\n{}",
toml::to_string(&secured_store).unwrap()
);
// ...Then, we can restore all keys by deserializing the RNG tree.
let seed = eraser
.restore(&secured_store)
.unwrap()
.open(passphrase)
.unwrap();
let tree = SecretTree::from_seed(&seed).unwrap();
let keys = Keys::new(&tree);
assert_eq!(
public_keys,
(keys.consensus_keys.public, keys.service_keys.public)
);
println!("Restored keys: {:#}", keys);
}