oasis_core_runtime/common/crypto/mrae/
deoxysii.rs

1//! Deoxys-II-256-128 MRAE primitives implementation.
2use anyhow::Result;
3use hmac::{Hmac, Mac};
4use rand::rngs::OsRng;
5use sha2::Sha512_256;
6use x25519_dalek::{PublicKey, StaticSecret};
7
8pub use deoxysii::{DeoxysII, KEY_SIZE, NONCE_SIZE, TAG_SIZE};
9
10type Kdf = Hmac<Sha512_256>;
11
12/// An abstract Deoxys-II-256-128 box opener.
13pub trait Opener: Send + Sync {
14    /// Unboxes ("opens") the provided additional data and ciphertext.
15    fn box_open(
16        &self,
17        nonce: &[u8; NONCE_SIZE],
18        ciphertext: Vec<u8>,
19        additional_data: Vec<u8>,
20        peers_public_key: &PublicKey,
21    ) -> Result<Vec<u8>>;
22}
23
24/// Derives a MRAE AEAD symmetric key suitable for use with the asymmetric
25/// box primitives from the provided X25519 public and private keys.
26fn derive_symmetric_key(public: &PublicKey, private: &StaticSecret) -> [u8; KEY_SIZE] {
27    let pmk = private.diffie_hellman(public);
28
29    let mut kdf = Kdf::new_from_slice(b"MRAE_Box_Deoxys-II-256-128").expect("Hmac::new_from_slice");
30    kdf.update(pmk.as_bytes());
31    drop(pmk);
32
33    let mut derived_key = [0u8; KEY_SIZE];
34    let digest = kdf.finalize();
35    derived_key.copy_from_slice(&digest.into_bytes()[..KEY_SIZE]);
36
37    derived_key
38}
39
40/// Generates a public/private key pair suitable for use with
41/// `derive_symmetric_key`, `box_seal`, and `box_open`.
42pub fn generate_key_pair() -> (PublicKey, StaticSecret) {
43    let sk = StaticSecret::random_from_rng(OsRng);
44    let pk = PublicKey::from(&sk);
45
46    (pk, sk)
47}
48
49/// Boxes ("seals") the provided additional data and plaintext via
50/// Deoxys-II-256-128 using a symmetric key derived from the provided
51/// X25519 public and private keys.
52/// The nonce should be `NONCE_SIZE` bytes long and unique for all time
53/// for a given public and private key tuple.
54pub fn box_seal(
55    nonce: &[u8; NONCE_SIZE],
56    plaintext: Vec<u8>,
57    additional_data: Vec<u8>,
58    peers_public_key: &PublicKey,
59    private_key: &StaticSecret,
60) -> Result<Vec<u8>> {
61    let key = derive_symmetric_key(peers_public_key, private_key);
62
63    let d2 = DeoxysII::new(&key);
64
65    Ok(d2.seal(nonce, plaintext, additional_data))
66}
67
68/// Unboxes ("opens") the provided additional data and ciphertext via
69/// Deoxys-II-256-128 using a symmetric key derived from the provided
70/// X25519 public and private keys.
71/// The nonce should be `NONCE_SIZE` bytes long and both it and the additional
72/// data must match the value passed to `box_seal`.
73pub fn box_open(
74    nonce: &[u8; NONCE_SIZE],
75    ciphertext: Vec<u8>,
76    additional_data: Vec<u8>,
77    peers_public_key: &PublicKey,
78    private_key: &StaticSecret,
79) -> Result<Vec<u8>> {
80    let key = derive_symmetric_key(peers_public_key, private_key);
81
82    let d2 = DeoxysII::new(&key);
83
84    d2.open(nonce, ciphertext, additional_data)
85        .map_err(|err| err.into())
86}
87
88#[cfg(test)]
89mod tests {
90    extern crate test;
91
92    use self::test::{black_box, Bencher};
93    use super::*;
94    use rand::RngCore;
95
96    #[test]
97    fn test_mrae_asymmetric() {
98        let (a_pub, a_priv) = generate_key_pair(); // Alice
99        let (b_pub, b_priv) = generate_key_pair(); // Bob
100
101        // None of the generated keys should be the same.
102        assert_ne!(a_pub.to_bytes(), b_pub.to_bytes());
103        assert_ne!(a_priv.to_bytes(), b_priv.to_bytes());
104        assert_ne!(a_pub.to_bytes(), a_priv.to_bytes());
105        assert_ne!(b_pub.to_bytes(), b_priv.to_bytes());
106
107        // Should successfully seal the text in a box.
108        let nonce = [1u8; NONCE_SIZE];
109        let text = String::from("This is a test!").as_bytes().to_vec();
110        let aad = vec![42u8; 10];
111
112        let sealed = box_seal(&nonce, text.clone(), aad.clone(), &b_pub, &a_priv);
113        assert!(sealed.is_ok());
114
115        // Should successfully open the sealed box.
116        let opened = box_open(&nonce, sealed.unwrap(), aad, &a_pub, &b_priv);
117        assert!(opened.is_ok());
118
119        // The deciphered text should match the original.
120        let deciphered = opened.unwrap();
121        assert_eq!(deciphered, text);
122    }
123
124    #[bench]
125    fn bench_mrae_box_seal_4096(b: &mut Bencher) {
126        let mut rng = OsRng {};
127
128        // Set up the keys.
129        let (_a_pub, a_priv) = generate_key_pair(); // Alice
130        let (b_pub, _b_priv) = generate_key_pair(); // Bob
131
132        // Set up the payload.
133        let mut nonce = [0u8; NONCE_SIZE];
134        rng.fill_bytes(&mut nonce);
135        let mut text = [0u8; 4096];
136        rng.fill_bytes(&mut text);
137        let mut aad = [0u8; 64];
138        rng.fill_bytes(&mut aad);
139
140        // Benchmark box sealing.
141        b.iter(|| {
142            let _sealed = black_box(box_seal(
143                &nonce,
144                text.to_vec(),
145                aad.to_vec(),
146                &b_pub,
147                &a_priv,
148            ));
149        });
150    }
151
152    #[bench]
153    fn bench_mrae_box_open_4096(b: &mut Bencher) {
154        let mut rng = OsRng {};
155
156        // Set up the keys.
157        let (a_pub, a_priv) = generate_key_pair(); // Alice
158        let (b_pub, b_priv) = generate_key_pair(); // Bob
159
160        // Set up the payload.
161        let mut nonce = [0u8; NONCE_SIZE];
162        rng.fill_bytes(&mut nonce);
163        let mut text = [0u8; 4096];
164        rng.fill_bytes(&mut text);
165        let mut aad = [0u8; 64];
166        rng.fill_bytes(&mut aad);
167
168        // Seal the payload.
169        let sealed = box_seal(&nonce, text.to_vec(), aad.to_vec(), &b_pub, &a_priv);
170        let ciphertext = sealed.unwrap();
171
172        // Benchmark box opening.
173        b.iter(|| {
174            let _opened = black_box(box_open(
175                &nonce,
176                ciphertext.clone(),
177                aad.to_vec(),
178                &a_pub,
179                &b_priv,
180            ));
181        });
182    }
183}