Skip to main content

hotshot_types/
x25519.rs

1use std::{fmt, str::FromStr};
2
3use cliquenet::x25519::{InvalidKeypair, InvalidPublicKey, InvalidSecretKey};
4use rand::{Rng, SeedableRng};
5use rand_chacha::ChaCha20Rng;
6use serde::{Deserialize, Serialize};
7use tagged_base64::{TaggedBase64, Tb64Error};
8
9use crate::traits::signature_key::{PrivateSignatureKey, SignatureKey};
10
11#[derive(Clone, PartialEq, Eq, Hash)]
12pub struct Keypair(cliquenet::x25519::Keypair);
13
14#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct PublicKey(cliquenet::x25519::PublicKey);
17
18#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
19#[serde(transparent)]
20pub struct SecretKey(cliquenet::x25519::SecretKey);
21
22impl Keypair {
23    pub fn generate() -> Result<Self, InvalidKeypair> {
24        cliquenet::x25519::Keypair::generate().map(Self)
25    }
26
27    pub fn generated_from_seed_indexed(seed: [u8; 32], index: u64) -> Result<Self, InvalidKeypair> {
28        let mut hasher = blake3::Hasher::new();
29        hasher.update(&seed);
30        hasher.update(&index.to_be_bytes());
31        let mut rng = ChaCha20Rng::from_seed(*hasher.finalize().as_bytes());
32        let seed: [u8; 32] = rng.r#gen();
33        cliquenet::x25519::Keypair::from_seed(seed).map(Self)
34    }
35
36    pub fn derive_from<K: SignatureKey>(k: &K::PrivateKey) -> Result<Self, InvalidSecretKey> {
37        let seed = blake3::derive_key("signing key -> x25519 key", &k.to_bytes());
38        let skey = SecretKey::try_from(seed)?;
39        Ok(skey.into())
40    }
41
42    pub fn public_key(&self) -> PublicKey {
43        PublicKey(self.0.public_key())
44    }
45
46    pub fn secret_key(&self) -> SecretKey {
47        SecretKey(self.0.secret_key())
48    }
49}
50
51impl PublicKey {
52    pub fn as_bytes(&self) -> [u8; 32] {
53        self.0.as_bytes()
54    }
55
56    pub fn as_slice(&self) -> &[u8] {
57        self.0.as_slice()
58    }
59}
60
61impl SecretKey {
62    pub fn public_key(&self) -> PublicKey {
63        PublicKey(self.0.public_key())
64    }
65
66    pub fn as_bytes(&self) -> [u8; 32] {
67        self.0.as_bytes()
68    }
69
70    pub fn as_slice(&self) -> &[u8] {
71        self.0.as_slice()
72    }
73}
74
75impl From<Keypair> for cliquenet::x25519::Keypair {
76    fn from(k: Keypair) -> Self {
77        k.0
78    }
79}
80
81impl From<PublicKey> for cliquenet::x25519::PublicKey {
82    fn from(k: PublicKey) -> Self {
83        k.0
84    }
85}
86
87impl From<cliquenet::x25519::PublicKey> for PublicKey {
88    fn from(k: cliquenet::x25519::PublicKey) -> Self {
89        Self(k)
90    }
91}
92
93impl From<SecretKey> for Keypair {
94    fn from(k: SecretKey) -> Self {
95        Self(k.0.into())
96    }
97}
98
99impl From<&SecretKey> for Keypair {
100    fn from(k: &SecretKey) -> Self {
101        Self::from(k.clone())
102    }
103}
104
105impl From<SecretKey> for PublicKey {
106    fn from(k: SecretKey) -> Self {
107        k.public_key()
108    }
109}
110
111impl fmt::Debug for SecretKey {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        f.write_str("SecretKey")
114    }
115}
116
117impl fmt::Debug for Keypair {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.debug_struct("Keypair")
120            .field("public_key", &self.public_key())
121            .field("secret_key", &"SecretKey")
122            .finish()
123    }
124}
125
126impl fmt::Debug for PublicKey {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        write!(f, "{}", bs58::encode(&self.as_bytes()).into_string())
129    }
130}
131
132impl fmt::Display for PublicKey {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        let tb =
135            TaggedBase64::new(X25519_PUBLIC_KEY, &self.as_bytes()[..]).map_err(|_| fmt::Error)?;
136        write!(f, "{tb}")
137    }
138}
139
140impl TryFrom<&[u8]> for PublicKey {
141    type Error = InvalidPublicKey;
142
143    fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
144        Ok(Self(cliquenet::x25519::PublicKey::try_from(s)?))
145    }
146}
147
148impl TryFrom<&[u8]> for SecretKey {
149    type Error = InvalidSecretKey;
150
151    fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
152        Ok(Self(cliquenet::x25519::SecretKey::try_from(s)?))
153    }
154}
155
156impl TryFrom<[u8; 32]> for SecretKey {
157    type Error = InvalidSecretKey;
158
159    fn try_from(a: [u8; 32]) -> Result<Self, Self::Error> {
160        Ok(Self(cliquenet::x25519::SecretKey::try_from(a)?))
161    }
162}
163
164impl TryFrom<&str> for PublicKey {
165    type Error = InvalidPublicKey;
166
167    fn try_from(s: &str) -> Result<Self, Self::Error> {
168        Ok(Self(cliquenet::x25519::PublicKey::try_from(s)?))
169    }
170}
171
172impl TryFrom<&str> for SecretKey {
173    type Error = InvalidSecretKey;
174
175    fn try_from(s: &str) -> Result<Self, Self::Error> {
176        Ok(Self(cliquenet::x25519::SecretKey::try_from(s)?))
177    }
178}
179
180const X25519_PUBLIC_KEY: &str = "X25519_PK";
181
182impl TryFrom<TaggedBase64> for PublicKey {
183    type Error = Tb64Error;
184
185    fn try_from(tb: TaggedBase64) -> Result<Self, Self::Error> {
186        if tb.tag() != X25519_PUBLIC_KEY {
187            return Err(Tb64Error::InvalidTag);
188        }
189        Self::try_from(tb.as_ref()).map_err(|_| Tb64Error::InvalidData)
190    }
191}
192
193impl TryFrom<PublicKey> for TaggedBase64 {
194    type Error = Tb64Error;
195
196    fn try_from(k: PublicKey) -> Result<Self, Self::Error> {
197        TaggedBase64::new(X25519_PUBLIC_KEY, &k.as_bytes()[..])
198    }
199}
200
201impl FromStr for PublicKey {
202    type Err = Tb64Error;
203
204    fn from_str(s: &str) -> Result<Self, Self::Err> {
205        Self::try_from(s.parse::<TaggedBase64>()?)
206    }
207}
208
209const X25519_SECRET_KEY: &str = "X25519_SK";
210
211impl TryFrom<TaggedBase64> for SecretKey {
212    type Error = Tb64Error;
213
214    fn try_from(tb: TaggedBase64) -> Result<Self, Self::Error> {
215        if tb.tag() != X25519_SECRET_KEY {
216            return Err(Tb64Error::InvalidTag);
217        }
218        Self::try_from(tb.as_ref()).map_err(|_| Tb64Error::InvalidData)
219    }
220}
221
222impl TryFrom<SecretKey> for TaggedBase64 {
223    type Error = Tb64Error;
224
225    fn try_from(k: SecretKey) -> Result<Self, Self::Error> {
226        TaggedBase64::new(X25519_SECRET_KEY, &k.as_bytes()[..])
227    }
228}