Skip to main content

oasis_core_runtime/common/crypto/
x25519.rs

1//! CBOR serializable X25519 types.
2use anyhow::Result;
3use rand::{rand_core::UnwrapErr, rngs::SysRng};
4use zeroize::{Zeroize, ZeroizeOnDrop};
5
6use super::hash::Hash;
7
8/// The length of an X25519 private key, in bytes.
9pub const PRIVATE_KEY_LENGTH: usize = 32;
10
11/// The length of an X25519 public key, in bytes.
12pub const PUBLIC_KEY_LENGTH: usize = 32;
13
14/// A CBOR serializable Diffie-Hellman X25519 private key.
15#[derive(Clone, ZeroizeOnDrop)]
16pub struct PrivateKey(pub x25519_dalek::StaticSecret);
17
18impl PrivateKey {
19    /// Generate a new private key.
20    pub fn generate() -> Self {
21        let mut rng = UnwrapErr(SysRng);
22        PrivateKey(x25519_dalek::StaticSecret::random_from_rng(&mut rng))
23    }
24
25    /// Compute corresponding public key.
26    pub fn public_key(&self) -> PublicKey {
27        PublicKey(x25519_dalek::PublicKey::from(&self.0))
28    }
29
30    /// Generate a new private key from a test key seed.
31    pub fn from_test_seed(seed: String) -> Self {
32        let seed = Hash::digest_bytes(seed.as_bytes());
33        Self::from(seed.0)
34    }
35}
36
37impl From<[u8; PRIVATE_KEY_LENGTH]> for PrivateKey {
38    /// Load private key from a byte array.
39    fn from(bytes: [u8; PRIVATE_KEY_LENGTH]) -> PrivateKey {
40        PrivateKey(x25519_dalek::StaticSecret::from(bytes))
41    }
42}
43
44impl Default for PrivateKey {
45    fn default() -> Self {
46        Self::from([0; PRIVATE_KEY_LENGTH])
47    }
48}
49
50impl AsRef<[u8]> for PrivateKey {
51    fn as_ref(&self) -> &[u8] {
52        self.0.as_ref()
53    }
54}
55
56impl From<PrivateKey> for x25519_dalek::StaticSecret {
57    fn from(sk: PrivateKey) -> Self {
58        sk.0.clone() // `sk` will be zeroized on drop, so clone is ok.
59    }
60}
61
62impl From<x25519_dalek::StaticSecret> for PrivateKey {
63    fn from(sk: x25519_dalek::StaticSecret) -> Self {
64        Self(sk)
65    }
66}
67
68impl cbor::Encode for PrivateKey {
69    fn into_cbor_value(self) -> cbor::Value {
70        cbor::to_value(self.0.to_bytes())
71    }
72}
73
74impl cbor::Decode for PrivateKey {
75    fn try_default() -> Result<Self, cbor::DecodeError> {
76        Ok(Default::default())
77    }
78
79    fn try_from_cbor_value(value: cbor::Value) -> Result<Self, cbor::DecodeError> {
80        let mut bytes: [u8; PRIVATE_KEY_LENGTH] = cbor::Decode::try_from_cbor_value(value)?;
81        let sk = PrivateKey(x25519_dalek::StaticSecret::from(bytes));
82        bytes.zeroize();
83        Ok(sk)
84    }
85}
86
87/// A CBOR serializable Diffie-Hellman X25519 public key.
88#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
89pub struct PublicKey(pub x25519_dalek::PublicKey);
90
91impl From<[u8; PUBLIC_KEY_LENGTH]> for PublicKey {
92    /// Load public key from a byte array.
93    fn from(bytes: [u8; PUBLIC_KEY_LENGTH]) -> PublicKey {
94        PublicKey(x25519_dalek::PublicKey::from(bytes))
95    }
96}
97
98impl From<&PrivateKey> for PublicKey {
99    /// Given an X25519 private key, compute its corresponding public key.
100    fn from(sk: &PrivateKey) -> PublicKey {
101        PublicKey(x25519_dalek::PublicKey::from(&sk.0))
102    }
103}
104
105impl Default for PublicKey {
106    fn default() -> Self {
107        Self::from([0; PUBLIC_KEY_LENGTH])
108    }
109}
110
111impl AsRef<[u8]> for PublicKey {
112    fn as_ref(&self) -> &[u8] {
113        self.0.as_ref()
114    }
115}
116
117impl From<PublicKey> for x25519_dalek::PublicKey {
118    fn from(pk: PublicKey) -> Self {
119        pk.0
120    }
121}
122
123impl From<x25519_dalek::PublicKey> for PublicKey {
124    fn from(pk: x25519_dalek::PublicKey) -> Self {
125        Self(pk)
126    }
127}
128
129impl cbor::Encode for PublicKey {
130    fn into_cbor_value(self) -> cbor::Value {
131        cbor::to_value(*self.0.as_bytes())
132    }
133}
134
135impl cbor::Decode for PublicKey {
136    fn try_default() -> Result<Self, cbor::DecodeError> {
137        Ok(Default::default())
138    }
139
140    fn try_from_cbor_value(value: cbor::Value) -> Result<Self, cbor::DecodeError> {
141        let bytes: [u8; PUBLIC_KEY_LENGTH] = cbor::Decode::try_from_cbor_value(value)?;
142        let pk = PublicKey(x25519_dalek::PublicKey::from(bytes));
143        Ok(pk)
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use crate::common::crypto::x25519::{PrivateKey, PublicKey, PRIVATE_KEY_LENGTH};
150
151    #[test]
152    fn test_cbor_serialization() {
153        let sk = PrivateKey::from([1; PRIVATE_KEY_LENGTH]);
154        let pk = PublicKey::from(&sk);
155
156        // Encode/decode private key.
157        let enc = cbor::to_vec(sk.clone());
158        let dec: PrivateKey = cbor::from_slice(&enc).expect("deserialization should succeed");
159        assert_eq!(
160            sk.0.to_bytes(),
161            dec.0.to_bytes(),
162            "serialization should round-trip"
163        );
164
165        // Encode/decode public key.
166        let enc = cbor::to_vec(pk.clone());
167        let dec: PublicKey = cbor::from_slice(&enc).expect("deserialization should succeed");
168        assert_eq!(
169            pk.0.to_bytes(),
170            dec.0.to_bytes(),
171            "serialization should round-trip"
172        );
173    }
174
175    #[test]
176    fn test_zeroize_on_drop() {
177        // Prepare private key.
178        let private_key_ptr;
179        {
180            let private_key = PrivateKey([10; 32].into());
181            private_key_ptr = private_key.0.as_bytes().as_ptr();
182        }
183
184        // Access the elements of the private key using pointer
185        // arithmetic and verify that they are all zero.
186        unsafe {
187            for i in 0..32 {
188                assert_eq!(*private_key_ptr.add(i), 0);
189            }
190        }
191    }
192}