dusk_node_data/
bls.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use std::cmp::Ordering;
8use std::fmt::Debug;
9use std::fs;
10use std::path::PathBuf;
11
12use aes::Aes256;
13use base64::engine::general_purpose::STANDARD as BASE64_ENGINE;
14use base64::Engine;
15use block_modes::block_padding::Pkcs7;
16use block_modes::{BlockMode, BlockModeError, Cbc};
17use dusk_bytes::{DeserializableSlice, Serializable};
18use dusk_core::signatures::bls::{
19    PublicKey as BlsPublicKey, SecretKey as BlsSecretKey,
20};
21use rand::rngs::StdRng;
22use rand::SeedableRng;
23use serde::Serialize;
24use sha2::{Digest, Sha256};
25use tracing::warn;
26pub const PUBLIC_BLS_SIZE: usize = BlsPublicKey::SIZE;
27
28/// Extends BlsPublicKey by implementing a few traits
29///
30/// See also PublicKey::bytes(&self)
31#[derive(Default, Eq, PartialEq, Clone)]
32pub struct PublicKey {
33    inner: BlsPublicKey,
34    as_bytes: PublicKeyBytes,
35}
36
37impl TryFrom<[u8; 96]> for PublicKey {
38    type Error = dusk_bytes::Error;
39    fn try_from(bytes: [u8; 96]) -> Result<Self, Self::Error> {
40        let inner = BlsPublicKey::from_slice(&bytes)?;
41        let as_bytes = PublicKeyBytes(bytes);
42        Ok(Self { as_bytes, inner })
43    }
44}
45
46impl PublicKey {
47    pub fn new(inner: BlsPublicKey) -> Self {
48        let b = inner.to_bytes();
49        Self {
50            inner,
51            as_bytes: PublicKeyBytes(b),
52        }
53    }
54
55    /// from_sk_seed_u64 generates a sk from the specified seed and returns the
56    /// associated public key
57    pub fn from_sk_seed_u64(state: u64) -> Self {
58        let rng = &mut StdRng::seed_from_u64(state);
59        let sk = BlsSecretKey::random(rng);
60
61        Self::new(BlsPublicKey::from(&sk))
62    }
63
64    /// `bytes` returns a reference to the pk.to_bytes() initialized on
65    /// PublicKey::new call. NB: Frequent use of `to_bytes()` creates a
66    /// noticeable performance overhead.
67    pub fn bytes(&self) -> &PublicKeyBytes {
68        &self.as_bytes
69    }
70
71    pub fn inner(&self) -> &BlsPublicKey {
72        &self.inner
73    }
74
75    pub fn into_inner(self) -> BlsPublicKey {
76        self.inner
77    }
78
79    /// Truncated base58 representation of inner data
80    pub fn to_bs58(&self) -> String {
81        self.bytes().to_bs58()
82    }
83
84    /// Full base58 representation of inner data
85    pub fn to_base58(&self) -> String {
86        self.bytes().to_base58()
87    }
88}
89
90impl PartialOrd<PublicKey> for PublicKey {
91    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
92        Some(self.cmp(other))
93    }
94}
95
96impl Ord for PublicKey {
97    fn cmp(&self, other: &Self) -> Ordering {
98        self.as_bytes.inner().cmp(other.as_bytes.inner())
99    }
100}
101
102impl std::fmt::Debug for PublicKey {
103    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
104        let bs = self.to_base58();
105        f.debug_struct("PublicKey").field("bs58", &bs).finish()
106    }
107}
108/// A wrapper of 96-sized array
109#[derive(Clone, Copy, Eq, Hash, PartialEq, Serialize)]
110pub struct PublicKeyBytes(
111    #[serde(serialize_with = "crate::serialize_b58")] pub [u8; PUBLIC_BLS_SIZE],
112);
113
114impl Default for PublicKeyBytes {
115    fn default() -> Self {
116        PublicKeyBytes([0; 96])
117    }
118}
119
120impl PublicKeyBytes {
121    pub fn inner(&self) -> &[u8; 96] {
122        &self.0
123    }
124
125    /// Full base58 representation of inner data
126    pub fn to_base58(&self) -> String {
127        bs58::encode(&self.0).into_string()
128    }
129
130    /// Truncated base58 representation of inner data
131    pub fn to_bs58(&self) -> String {
132        let mut bs = self.to_base58();
133        bs.truncate(16);
134        bs
135    }
136}
137
138impl Debug for PublicKeyBytes {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        f.write_str(&self.to_bs58())
141    }
142}
143
144/// Loads consensus keys from an encrypted file.
145pub fn load_keys(
146    path: String,
147    pwd: String,
148) -> anyhow::Result<(BlsSecretKey, PublicKey)> {
149    let path_buf = PathBuf::from(path);
150    let (pk, sk) = read_from_file(path_buf, &pwd)?;
151
152    Ok((sk, PublicKey::new(pk)))
153}
154
155/// Fetches BLS public and secret keys from an encrypted consensus keys file.
156fn read_from_file(
157    path: PathBuf,
158    pwd: &str,
159) -> anyhow::Result<(BlsPublicKey, BlsSecretKey)> {
160    use serde::Deserialize;
161
162    /// Bls key pair helper structure
163    #[derive(Deserialize)]
164    struct BlsKeyPair {
165        secret_key_bls: String,
166        public_key_bls: String,
167    }
168
169    // attempt to load and decode wallet
170    let ciphertext = fs::read(&path).map_err(|e| {
171        anyhow::anyhow!(
172            "{} should be valid consensus keys file {e}",
173            path.display()
174        )
175    })?;
176
177    let mut hasher = Sha256::new();
178    hasher.update(pwd.as_bytes());
179    let hashed_pwd = hasher.finalize().to_vec();
180
181    let bytes = match decrypt(&ciphertext[..], &hashed_pwd) {
182        Ok(bytes) => bytes,
183        Err(_) => {
184            let bytes = decrypt(&ciphertext[..], &hashed_pwd).map_err(|e| {
185                anyhow::anyhow!("Invalid consensus keys password {e}")
186            })?;
187            warn!("Your consensus keys are in the old format");
188            warn!("Consider to export them using a new version of the wallet");
189            bytes
190        }
191    };
192
193    let keys: BlsKeyPair = serde_json::from_slice(&bytes)
194        .map_err(|e| anyhow::anyhow!("keys files should contain json {e}"))?;
195
196    let sk_bytes = BASE64_ENGINE
197        .decode(keys.secret_key_bls)
198        .map_err(|e| anyhow::anyhow!("sk should be base64 {e}"))?;
199
200    let sk = BlsSecretKey::from_slice(&sk_bytes)
201        .map_err(|e| anyhow::anyhow!("sk should be valid {e:?}"))?;
202
203    let pk = BlsPublicKey::from_slice(
204        &BASE64_ENGINE
205            .decode(keys.public_key_bls)
206            .map_err(|e| anyhow::anyhow!("pk should be base64 {e}"))?[..],
207    )
208    .map_err(|e| anyhow::anyhow!("pk should be valid {e:?}"))?;
209
210    Ok((pk, sk))
211}
212
213fn decrypt(data: &[u8], pwd: &[u8]) -> Result<Vec<u8>, BlockModeError> {
214    type Aes256Cbc = Cbc<Aes256, Pkcs7>;
215    let iv = &data[..16];
216    let enc = &data[16..];
217
218    let cipher = Aes256Cbc::new_from_slices(pwd, iv).expect("valid data");
219    cipher.decrypt_vec(enc)
220}
221
222/// Loads wallet files from $DUSK_WALLET_DIR and returns a vector of all loaded
223/// consensus keys.
224///
225/// It reads $DUSK_CONSENSUS_KEYS_PASS var to unlock wallet files.
226pub fn load_provisioners_keys(n: usize) -> Vec<(BlsSecretKey, PublicKey)> {
227    let mut keys = vec![];
228
229    let dir = std::env::var("DUSK_WALLET_DIR").unwrap();
230    let pwd = std::env::var("DUSK_CONSENSUS_KEYS_PASS").unwrap();
231
232    for i in 0..n {
233        let mut path = dir.clone();
234        path.push_str(&format!("node_{i}.keys"));
235        let path_buf = PathBuf::from(path);
236
237        let (pk, sk) = read_from_file(path_buf, &pwd).unwrap();
238
239        keys.push((sk, PublicKey::new(pk)));
240    }
241
242    keys
243}