1use crate::{
40 traits::{
41 self, CryptoMaterialError, ValidCryptoMaterial,
42 ValidCryptoMaterialStringExt,
43 },
44 x25519,
45};
46use diem_crypto_derive::{
47 DeserializeKey, SerializeKey, SilentDebug, SilentDisplay,
48};
49use rand::{CryptoRng, RngCore};
50use std::convert::{TryFrom, TryInto};
51
52#[cfg(any(test, feature = "fuzzing"))]
53use proptest_derive::Arbitrary;
54
55pub use x25519_dalek;
64
65pub const PRIVATE_KEY_SIZE: usize = 32;
72
73pub const PUBLIC_KEY_SIZE: usize = 32;
75
76pub const SHARED_SECRET_SIZE: usize = 32;
78
79#[derive(DeserializeKey, SilentDisplay, SilentDebug, SerializeKey)]
81#[cfg_attr(any(test, feature = "fuzzing"), derive(Clone))]
82pub struct PrivateKey(x25519_dalek::StaticSecret);
83
84#[derive(
86 Default,
87 Clone,
88 Copy,
89 PartialEq,
90 Eq,
91 Hash,
92 PartialOrd,
93 Ord,
94 SerializeKey,
95 DeserializeKey,
96)]
97#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
98pub struct PublicKey([u8; PUBLIC_KEY_SIZE]);
99
100impl PrivateKey {
106 pub fn public_key(&self) -> PublicKey {
108 let public_key: x25519_dalek::PublicKey = (&self.0).into();
109 PublicKey(public_key.as_bytes().to_owned())
110 }
111
112 pub fn diffie_hellman(
114 &self, remote_public_key: &PublicKey,
115 ) -> [u8; SHARED_SECRET_SIZE] {
116 let remote_public_key =
117 x25519_dalek::PublicKey::from(remote_public_key.0);
118 let shared_secret = self.0.diffie_hellman(&remote_public_key);
119 shared_secret.as_bytes().to_owned()
120 }
121
122 pub fn from_ed25519_private_bytes(
134 private_slice: &[u8],
135 ) -> Result<Self, CryptoMaterialError> {
136 let ed25519_secretkey =
137 ed25519_dalek::SecretKey::from_bytes(private_slice)
138 .map_err(|_| CryptoMaterialError::DeserializationError)?;
139 let expanded_key =
140 ed25519_dalek::ExpandedSecretKey::from(&ed25519_secretkey);
141
142 let mut expanded_keypart = [0u8; 32];
143 expanded_keypart.copy_from_slice(&expanded_key.to_bytes()[..32]);
144 let potential_x25519 = x25519::PrivateKey::from(expanded_keypart);
145
146 if potential_x25519.to_bytes()[..] != expanded_key.to_bytes()[..32] {
149 Err(CryptoMaterialError::DeserializationError)
150 } else {
151 Ok(potential_x25519)
152 }
153 }
154}
155
156impl PublicKey {
157 pub fn as_slice(&self) -> &[u8] { &self.0 }
159
160 pub fn from_ed25519_public_bytes(
166 ed25519_bytes: &[u8],
167 ) -> Result<Self, CryptoMaterialError> {
168 if ed25519_bytes.len() != 32 {
169 return Err(CryptoMaterialError::DeserializationError);
170 }
171 let ed_point =
172 curve25519_dalek::edwards::CompressedEdwardsY::from_slice(
173 ed25519_bytes,
174 )
175 .decompress()
176 .ok_or(CryptoMaterialError::DeserializationError)?;
177
178 Ok(x25519::PublicKey::from(ed_point.to_montgomery().to_bytes()))
179 }
180}
181
182impl std::convert::From<[u8; PRIVATE_KEY_SIZE]> for PrivateKey {
190 fn from(private_key_bytes: [u8; PRIVATE_KEY_SIZE]) -> Self {
191 Self(x25519_dalek::StaticSecret::from(private_key_bytes))
192 }
193}
194
195impl std::convert::TryFrom<&[u8]> for PrivateKey {
196 type Error = traits::CryptoMaterialError;
197
198 fn try_from(private_key_bytes: &[u8]) -> Result<Self, Self::Error> {
199 let private_key_bytes: [u8; PRIVATE_KEY_SIZE] = private_key_bytes
200 .try_into()
201 .map_err(|_| traits::CryptoMaterialError::DeserializationError)?;
202 Ok(Self(x25519_dalek::StaticSecret::from(private_key_bytes)))
203 }
204}
205
206impl traits::PrivateKey for PrivateKey {
207 type PublicKeyMaterial = PublicKey;
208}
209
210impl traits::Uniform for PrivateKey {
211 fn generate<R>(rng: &mut R) -> Self
212 where R: RngCore + CryptoRng {
213 Self(x25519_dalek::StaticSecret::new(rng))
214 }
215}
216
217impl traits::ValidCryptoMaterial for PrivateKey {
219 fn to_bytes(&self) -> Vec<u8> { self.0.to_bytes().to_vec() }
220}
221
222#[cfg(any(test, feature = "fuzzing"))]
223impl PartialEq for PrivateKey {
224 fn eq(&self, other: &Self) -> bool { self.to_bytes() == other.to_bytes() }
225}
226
227#[cfg(any(test, feature = "fuzzing"))]
228impl proptest::arbitrary::Arbitrary for PrivateKey {
229 type Parameters = ();
230 type Strategy = proptest::strategy::BoxedStrategy<Self>;
231
232 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
233 use proptest::strategy::Strategy as _;
234 proptest::arbitrary::any::<[u8; 32]>()
235 .prop_map(PrivateKey::from)
236 .boxed()
237 }
238}
239
240impl From<&PrivateKey> for PublicKey {
243 fn from(private_key: &PrivateKey) -> Self { private_key.public_key() }
244}
245
246impl std::convert::From<[u8; PUBLIC_KEY_SIZE]> for PublicKey {
247 fn from(public_key_bytes: [u8; PUBLIC_KEY_SIZE]) -> Self {
248 Self(public_key_bytes)
249 }
250}
251
252impl std::convert::TryFrom<&[u8]> for PublicKey {
253 type Error = traits::CryptoMaterialError;
254
255 fn try_from(public_key_bytes: &[u8]) -> Result<Self, Self::Error> {
256 let public_key_bytes: [u8; PUBLIC_KEY_SIZE] = public_key_bytes
257 .try_into()
258 .map_err(|_| traits::CryptoMaterialError::WrongLengthError)?;
259 Ok(Self(public_key_bytes))
260 }
261}
262
263impl traits::PublicKey for PublicKey {
264 type PrivateKeyMaterial = PrivateKey;
265}
266
267impl traits::ValidCryptoMaterial for PublicKey {
268 fn to_bytes(&self) -> Vec<u8> { self.0.to_vec() }
269}
270
271impl std::fmt::Display for PublicKey {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 write!(f, "{}", hex::encode(&self.0))
274 }
275}
276
277impl std::fmt::Debug for PublicKey {
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 write!(f, "x25519::PublicKey({})", self)
280 }
281}
282
283#[cfg(any(test, feature = "fuzzing"))]
284use crate::test_utils::{self, KeyPair};
285#[cfg(any(test, feature = "fuzzing"))]
286use proptest::prelude::*;
287
288#[cfg(any(test, feature = "fuzzing"))]
290pub fn keypair_strategy(
291) -> impl Strategy<Value = KeyPair<PrivateKey, PublicKey>> {
292 test_utils::uniform_keypair_strategy::<PrivateKey, PublicKey>()
293}