oasis_core_runtime/common/crypto/
x25519.rs

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