autonomi/client/
key_derivation.rs

1// Copyright 2025 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use bls::{PK_SIZE, PublicKey, SecretKey, serde_impl::SerdeSecret};
10use rand::RngCore;
11use serde::{Deserialize, Serialize};
12use std::fmt;
13use thiserror::Error;
14
15/// Errors that can occur when decoding a key from a hex string
16#[derive(Error, Debug)]
17pub enum KeyDecodeError {
18    #[error("Failed to decode hex to key")]
19    FailedToDecodeHexToKey,
20    #[error("Failed to parse BLS key")]
21    FailedToParseBlsKey,
22    #[error("Invalid key length")]
23    InvalidKeyLength,
24}
25
26/// This is used to generate a new DerivedPubkey
27/// from a MainPubkey, and the corresponding
28/// DerivedSecretKey from the MainSecretKey of that MainPubkey.
29#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
30pub struct DerivationIndex([u8; 32]);
31
32impl fmt::Debug for DerivationIndex {
33    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
34        write!(
35            formatter,
36            "{:02x}{:02x}{:02x}..",
37            self.0[0], self.0[1], self.0[2]
38        )
39    }
40}
41
42impl DerivationIndex {
43    /// generates a random derivation index
44    pub fn random(rng: &mut impl RngCore) -> DerivationIndex {
45        let mut bytes = [0u8; 32];
46        rng.fill_bytes(&mut bytes);
47        DerivationIndex(bytes)
48    }
49
50    /// returns the inner bytes representation
51    pub fn as_bytes(&self) -> &[u8; 32] {
52        &self.0
53    }
54
55    /// returns the inner bytes
56    pub fn into_bytes(self) -> [u8; 32] {
57        self.0
58    }
59
60    /// Create a new DerivationIndex from a bytes array
61    pub fn from_bytes(bytes: [u8; 32]) -> Self {
62        Self(bytes)
63    }
64}
65
66/// A public key derived from a [`MainPubkey`] using a [`DerivationIndex`]
67/// Its associated secret key is the [`DerivedSecretKey`]
68/// This key is unlinkable to the original [`MainPubkey`]
69#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
70pub struct DerivedPubkey(PublicKey);
71
72impl DerivedPubkey {
73    pub fn new<G: Into<PublicKey>>(public_key: G) -> Self {
74        Self(public_key.into())
75    }
76
77    pub fn to_bytes(&self) -> [u8; bls::PK_SIZE] {
78        self.0.to_bytes()
79    }
80
81    /// Returns `true` if the signature matches the message.
82    pub fn verify<M: AsRef<[u8]>>(&self, sig: &bls::Signature, msg: M) -> bool {
83        self.0.verify(sig, msg)
84    }
85
86    pub fn to_hex(&self) -> String {
87        hex::encode(self.0.to_bytes())
88    }
89
90    pub fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, KeyDecodeError> {
91        let public_key = bls_public_from_hex(hex)?;
92        Ok(Self::new(public_key))
93    }
94}
95
96/// Custom implementation of Serialize and Deserialize for [`DerivedPubkey`] to make it an actionable
97/// hex string that can be copy pasted in apps, instead of a useless array of numbers
98impl Serialize for DerivedPubkey {
99    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
100        serializer.serialize_str(&self.to_hex())
101    }
102}
103
104impl<'de> Deserialize<'de> for DerivedPubkey {
105    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
106    where
107        D: serde::Deserializer<'de>,
108    {
109        let hex = String::deserialize(deserializer)?;
110        DerivedPubkey::from_hex(hex).map_err(|e| {
111            serde::de::Error::custom(format!("Failed to deserialize DerivedPubkey from hex: {e}",))
112        })
113    }
114}
115
116/// Actionable way to print a DerivedPubkey
117/// This way to print it is lengthier but allows to copy/paste it into the cli or other apps
118/// To use for verification purposes
119impl std::fmt::Debug for DerivedPubkey {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        write!(f, "{}", self.to_hex())
122    }
123}
124
125impl std::fmt::Display for DerivedPubkey {
126    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127        write!(f, "{}", self.to_hex())
128    }
129}
130
131/// The secret key of a [`DerivedPubkey`]
132/// It is derived from the [`MainSecretKey`] with the same [`DerivationIndex`]
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct DerivedSecretKey(SerdeSecret<SecretKey>);
135
136impl DerivedSecretKey {
137    pub fn new<S: Into<SecretKey>>(secret_key: S) -> Self {
138        Self(SerdeSecret(secret_key.into()))
139    }
140
141    /// The [`DerivedPubkey`] of this [`DerivedSecretKey`]
142    pub fn public_key(&self) -> DerivedPubkey {
143        DerivedPubkey(self.0.public_key())
144    }
145
146    /// Sign a message with the secret key
147    pub fn sign(&self, msg: &[u8]) -> bls::Signature {
148        self.0.sign(msg)
149    }
150}
151
152/// This is the public key of the [`MainSecretKey`]
153/// One can derive [`DerivedPubkey`]s from this [`MainPubkey`]
154#[derive(Copy, PartialEq, Eq, Ord, PartialOrd, Clone, Serialize, Deserialize, Hash)]
155pub struct MainPubkey(pub PublicKey);
156
157impl MainPubkey {
158    /// Create a new [`MainPubkey`] from a bls [`PublicKey`]
159    pub fn new(public_key: PublicKey) -> Self {
160        Self(public_key)
161    }
162
163    /// Verify that the signature is valid for the message.
164    pub fn verify(&self, sig: &bls::Signature, msg: &[u8]) -> bool {
165        self.0.verify(sig, msg)
166    }
167
168    /// Generate a new [`DerivedPubkey`] from provided [`DerivationIndex`].
169    pub fn derive_key(&self, index: &DerivationIndex) -> DerivedPubkey {
170        DerivedPubkey(self.0.derive_child(&index.0))
171    }
172
173    /// Return the inner pubkey's bytes representation
174    pub fn to_bytes(self) -> [u8; PK_SIZE] {
175        self.0.to_bytes()
176    }
177
178    /// Return a hex representation of the [`MainPubkey`]
179    pub fn to_hex(&self) -> String {
180        hex::encode(self.0.to_bytes())
181    }
182
183    /// Create a new [`MainPubkey`] from a hex string
184    pub fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, KeyDecodeError> {
185        let public_key = bls_public_from_hex(hex)?;
186        Ok(Self::new(public_key))
187    }
188}
189
190impl std::fmt::Debug for MainPubkey {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        write!(f, "{}", self.to_hex())
193    }
194}
195
196/// The secret key of the [`MainPubkey`]
197/// It is held privately and not shared with anyone
198/// With this [`MainSecretKey`], new [`DerivedSecretKey`]:[`DerivedPubkey`] pairs can be generated
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct MainSecretKey(SerdeSecret<SecretKey>);
201
202impl MainSecretKey {
203    /// Create a [`MainSecretKey`] from a bls [`SecretKey`].
204    pub fn new(secret_key: SecretKey) -> Self {
205        Self(SerdeSecret(secret_key))
206    }
207
208    /// Return the matching [`MainPubkey`]
209    pub fn public_key(&self) -> MainPubkey {
210        MainPubkey(self.0.public_key())
211    }
212
213    /// Signs the given message
214    pub fn sign(&self, msg: &[u8]) -> bls::Signature {
215        self.0.sign(msg)
216    }
217
218    /// Derive a [`DerivedSecretKey`] from a [`DerivationIndex`]
219    /// This is used to create a new unlinkable key pair that cannot be linked back to the [`MainSecretKey`] without the [`DerivationIndex`]
220    pub fn derive_key(&self, index: &DerivationIndex) -> DerivedSecretKey {
221        DerivedSecretKey::new(self.0.inner().derive_child(&index.0))
222    }
223
224    /// Return the inner secret key's bytes representation
225    pub fn to_bytes(&self) -> Vec<u8> {
226        self.0.to_bytes().to_vec()
227    }
228
229    /// Generate a new random [`MainSecretKey`]
230    pub fn random() -> Self {
231        Self::new(SecretKey::random())
232    }
233
234    /// Generate a new random [`DerivedSecretKey`] from the [`MainSecretKey`]
235    pub fn random_derived_key(&self, rng: &mut impl RngCore) -> DerivedSecretKey {
236        self.derive_key(&DerivationIndex::random(rng))
237    }
238}
239
240/// Construct a BLS public key from a hex-encoded string.
241fn bls_public_from_hex<T: AsRef<[u8]>>(hex: T) -> Result<PublicKey, KeyDecodeError> {
242    let bytes = hex::decode(hex).map_err(|_| KeyDecodeError::FailedToDecodeHexToKey)?;
243    let bytes_fixed_len: [u8; bls::PK_SIZE] = bytes
244        .as_slice()
245        .try_into()
246        .map_err(|_| KeyDecodeError::InvalidKeyLength)?;
247    let pk =
248        PublicKey::from_bytes(bytes_fixed_len).map_err(|_| KeyDecodeError::FailedToParseBlsKey)?;
249    Ok(pk)
250}
251
252// conversions to bls types
253impl From<MainSecretKey> for SecretKey {
254    fn from(main_secret_key: MainSecretKey) -> Self {
255        main_secret_key.0.inner().to_owned()
256    }
257}
258impl From<DerivedSecretKey> for SecretKey {
259    fn from(derived_secret_key: DerivedSecretKey) -> Self {
260        derived_secret_key.0.inner().to_owned()
261    }
262}
263impl From<DerivedPubkey> for PublicKey {
264    fn from(derived_pubkey: DerivedPubkey) -> Self {
265        derived_pubkey.0
266    }
267}
268impl From<MainPubkey> for PublicKey {
269    fn from(main_pubkey: MainPubkey) -> Self {
270        main_pubkey.0
271    }
272}
273
274// conversions from bls types
275impl From<SecretKey> for MainSecretKey {
276    fn from(secret_key: SecretKey) -> Self {
277        MainSecretKey::new(secret_key)
278    }
279}
280impl From<SecretKey> for DerivedSecretKey {
281    fn from(secret_key: SecretKey) -> Self {
282        DerivedSecretKey::new(secret_key)
283    }
284}
285impl From<PublicKey> for MainPubkey {
286    fn from(public_key: PublicKey) -> Self {
287        MainPubkey::new(public_key)
288    }
289}
290impl From<PublicKey> for DerivedPubkey {
291    fn from(public_key: PublicKey) -> Self {
292        DerivedPubkey::new(public_key)
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    fn test_pubkeys_hex_conversion() -> eyre::Result<()> {
302        let sk = bls::SecretKey::random();
303        let pk = sk.public_key();
304        let main_pubkey = MainPubkey::new(pk);
305        let unique_pubkey =
306            main_pubkey.derive_key(&DerivationIndex::random(&mut rand::thread_rng()));
307
308        let main_pubkey_hex = main_pubkey.to_hex();
309        let unique_pubkey_hex = unique_pubkey.to_hex();
310
311        let main_pubkey_from_hex = MainPubkey::from_hex(main_pubkey_hex)?;
312        let unique_pubkey_from_hex = DerivedPubkey::from_hex(unique_pubkey_hex)?;
313
314        assert_eq!(main_pubkey, main_pubkey_from_hex);
315        assert_eq!(unique_pubkey, unique_pubkey_from_hex);
316        Ok(())
317    }
318
319    #[test]
320    fn test_serialisation() -> eyre::Result<()> {
321        let pk = SecretKey::random().public_key();
322        let main_pubkey = MainPubkey::new(pk);
323        let unique_pk = main_pubkey.derive_key(&DerivationIndex::random(&mut rand::thread_rng()));
324
325        let str_serialised = rmp_serde::to_vec_named(&unique_pk)?;
326        let str_deserialised: DerivedPubkey = rmp_serde::from_slice(&str_serialised)?;
327        assert_eq!(str_deserialised, unique_pk);
328
329        Ok(())
330    }
331
332    #[test]
333    fn verification_using_child_key() -> eyre::Result<()> {
334        let msg = "just a test string".as_bytes();
335        let main_sk = MainSecretKey::random();
336        let derived_sk = main_sk.random_derived_key(&mut rand::thread_rng());
337
338        // Signature signed by parent key can not be verified by the child key.
339        let signature = main_sk.sign(msg);
340        assert!(main_sk.public_key().verify(&signature, msg));
341        assert!(!derived_sk.public_key().verify(&signature, msg));
342
343        // Signature signed by child key can not be verified by the parent key.
344        let signature = derived_sk.sign(msg);
345        assert!(derived_sk.public_key().verify(&signature, msg));
346        assert!(!main_sk.public_key().verify(&signature, msg));
347
348        Ok(())
349    }
350}