oasis_runtime_sdk/storage/
confidential.rs

1use std::convert::TryInto as _;
2
3use anyhow;
4use hmac::{Hmac, Mac as _};
5use sha2::Sha512_256;
6use slog::error;
7use thiserror::Error;
8use zeroize::{Zeroize, Zeroizing};
9
10pub use crate::core::common::crypto::mrae::deoxysii::KEY_SIZE;
11use crate::{
12    core::{
13        common::crypto::{
14            hash::Hash,
15            mrae::deoxysii::{self, NONCE_SIZE},
16        },
17        storage::mkvs,
18    },
19    storage::Store,
20};
21
22type Nonce = [u8; NONCE_SIZE];
23type Kdf = Hmac<Sha512_256>;
24
25/// Unpack the concatenation of (nonce || byte_slice) into (Nonce, &[u8]).
26fn unpack_nonce_slice<'a>(packed: &'a [u8]) -> Option<(&'a Nonce, &'a [u8])> {
27    if packed.len() <= NONCE_SIZE {
28        return None;
29    }
30    let nonce_ref: &'a Nonce = packed[..NONCE_SIZE]
31        .try_into()
32        .expect("nonce size mismatch");
33    Some((nonce_ref, &packed[NONCE_SIZE..]))
34}
35
36/// Errors emitted by the confidential store.
37#[derive(Error, Debug)]
38pub enum Error {
39    #[error("corrupt key")]
40    CorruptKey,
41
42    #[error("corrupt value")]
43    CorruptValue,
44
45    #[error("decryption failure: {0}")]
46    DecryptionFailure(anyhow::Error),
47}
48
49/// A key-value store that encrypts all content with DeoxysII.
50pub struct ConfidentialStore<S: Store> {
51    inner: S,
52    deoxys: deoxysii::DeoxysII,
53    base_value_prefix: Vec<u8>,
54    nonce_counter: usize,
55    nonce_key: Zeroizing<Vec<u8>>,
56}
57
58impl<S: Store> ConfidentialStore<S> {
59    /// Create a new confidential store with the given keypair.
60    pub fn new_with_key(inner: S, key: [u8; KEY_SIZE], value_context: &[&[u8]]) -> Self {
61        let actual_key = Zeroizing::new(key);
62
63        // Derive a nonce key for nonces used to encrypt storage keys in the store:
64        // nonce_key = KDF(key)
65        let mut kdf = Kdf::new_from_slice(b"oasis-runtime-sdk/confidential-store: nonce key")
66            .expect("Hmac::new_from_slice");
67        kdf.update(&key);
68        let mut derived = kdf.finalize().into_bytes();
69        // Try to destroy as much of the bytes as possible; there's
70        // no neat way to get from kdf output to a Vec<u8> without copying a lot.
71        let derived = Zeroizing::new(derived.iter_mut());
72
73        ConfidentialStore {
74            inner,
75            deoxys: deoxysii::DeoxysII::new(&actual_key),
76            base_value_prefix: value_context.concat(),
77            nonce_counter: 0,
78            nonce_key: Zeroizing::new(derived.as_slice().to_vec()),
79        }
80    }
81
82    fn pack_nonce_slice(&self, nonce: &Nonce, slice: &[u8]) -> Vec<u8> {
83        let mut ret = Vec::with_capacity(nonce.len() + slice.len());
84        ret.extend_from_slice(nonce);
85        ret.extend_from_slice(slice);
86        ret
87    }
88
89    fn make_key(&self, plain_key: &[u8]) -> (Nonce, Vec<u8>) {
90        // The nonce used to encrypt storage keys is derived from a combination
91        // of a base nonce key (derived from the encryption key) and the
92        // incoming plaintext storage key:
93        // nonce = Trunc(NONCE_SIZE, H(nonce_key || plain_key))
94        let mut nonce = [0u8; NONCE_SIZE];
95
96        let mut nonce_src = self.nonce_key.clone();
97        nonce_src.extend_from_slice(plain_key);
98
99        let hash = Hash::digest_bytes(&nonce_src);
100        nonce.copy_from_slice(hash.truncated(NONCE_SIZE));
101
102        let enc_key = self.deoxys.seal(&nonce, plain_key, vec![]);
103        let key = self.pack_nonce_slice(&nonce, &enc_key);
104        (nonce, key)
105    }
106
107    fn make_value(&mut self, plain_value: &[u8]) -> (Nonce, Vec<u8>) {
108        // Nonces for value encryption are derived from deterministic
109        // environmental data which hopefully changes a lot. In particular,
110        // the base_value_prefix should change every time the store is
111        // instantiated, and the nonce_counter changes during the store's lifetime.
112        // nonce = Trunc(NONCE_SIZE, H(base_prefix || nonce_counter))
113        let mut nonce = [0u8; NONCE_SIZE];
114
115        self.nonce_counter += 1;
116        let hash = Hash::digest_bytes_list(&[
117            self.base_value_prefix.as_slice(),
118            self.nonce_counter.to_le_bytes().as_slice(),
119        ]);
120        nonce.copy_from_slice(hash.truncated(NONCE_SIZE));
121
122        let enc_value = self.deoxys.seal(&nonce, plain_value, vec![]);
123        let value = self.pack_nonce_slice(&nonce, &enc_value);
124        (nonce, value)
125    }
126
127    fn get_item(&self, raw: &[u8]) -> Result<(Nonce, Vec<u8>), Error> {
128        match unpack_nonce_slice(raw) {
129            Some((nonce, enc_ref)) => {
130                let enc = Vec::from(enc_ref);
131                let plain = self
132                    .deoxys
133                    .open(nonce, enc, vec![])
134                    .map_err(|err| Error::DecryptionFailure(err.into()))?;
135                Ok((*nonce, plain))
136            }
137            None => Err(Error::CorruptKey),
138        }
139    }
140}
141
142impl<S: Store> Zeroize for ConfidentialStore<S> {
143    fn zeroize(&mut self) {
144        self.deoxys.zeroize();
145        self.nonce_key.zeroize();
146    }
147}
148
149impl<S: Store> Store for ConfidentialStore<S> {
150    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
151        let (_, inner_key) = self.make_key(key);
152        self.inner.get(&inner_key).map(|inner_value| {
153            self.get_item(&inner_value)
154                .expect("error decrypting value")
155                .1
156        })
157    }
158
159    fn insert(&mut self, key: &[u8], value: &[u8]) {
160        let (_, inner_key) = self.make_key(key);
161        let (_, inner_value) = self.make_value(value);
162        self.inner.insert(&inner_key, &inner_value)
163    }
164
165    fn remove(&mut self, key: &[u8]) {
166        let (_, inner_key) = self.make_key(key);
167        self.inner.remove(&inner_key)
168    }
169
170    fn iter(&self) -> Box<dyn mkvs::Iterator + '_> {
171        Box::new(ConfidentialStoreIterator::new(self))
172    }
173
174    fn prefetch_prefixes(&mut self, prefixes: Vec<mkvs::Prefix>, limit: u16) {
175        self.inner.prefetch_prefixes(prefixes, limit);
176    }
177}
178
179struct ConfidentialStoreIterator<'store, S: Store> {
180    inner: Box<dyn mkvs::Iterator + 'store>,
181    store: &'store ConfidentialStore<S>,
182
183    key: Option<mkvs::Key>,
184    value: Option<Vec<u8>>,
185    error: Option<anyhow::Error>,
186}
187
188impl<'store, S: Store> ConfidentialStoreIterator<'store, S> {
189    fn new(store: &'store ConfidentialStore<S>) -> ConfidentialStoreIterator<'store, S> {
190        ConfidentialStoreIterator {
191            inner: store.inner.iter(),
192            store,
193            key: None,
194            value: None,
195            error: None,
196        }
197    }
198
199    fn reset(&mut self) {
200        self.key = None;
201        self.value = None;
202        self.error = None;
203    }
204
205    fn load(&mut self, inner_key: &[u8], inner_value: &[u8]) {
206        if !mkvs::Iterator::is_valid(self) {
207            return;
208        }
209
210        match self.store.get_item(inner_key) {
211            Ok((_, key)) => match self.store.get_item(inner_value) {
212                Ok((_, value)) => {
213                    self.key = Some(key);
214                    self.value = Some(value);
215                }
216                Err(err) => {
217                    self.error = Some(err.into());
218                }
219            },
220            Err(err) => {
221                self.error = Some(err.into());
222            }
223        }
224    }
225
226    fn reset_and_load(&mut self) {
227        self.reset();
228        if self.inner.is_valid() {
229            if let Some(ref inner_key) = self.inner.get_key().clone() {
230                if let Some(ref inner_value) = self.inner.get_value().clone() {
231                    self.load(inner_key, inner_value);
232                } else {
233                    self.error = Some(anyhow::anyhow!("no value in valid inner iterator"));
234                }
235            } else {
236                self.error = Some(anyhow::anyhow!("no key in valid inner iterator"));
237            }
238        }
239    }
240}
241
242impl<S: Store> Iterator for ConfidentialStoreIterator<'_, S> {
243    type Item = (Vec<u8>, Vec<u8>);
244
245    fn next(&mut self) -> Option<Self::Item> {
246        self.reset_and_load();
247        if !mkvs::Iterator::is_valid(self) {
248            return None;
249        }
250        mkvs::Iterator::next(&mut *self.inner);
251        Some((self.key.clone().unwrap(), self.value.clone().unwrap()))
252    }
253}
254
255impl<S: Store> mkvs::Iterator for ConfidentialStoreIterator<'_, S> {
256    fn set_prefetch(&mut self, prefetch: usize) {
257        self.inner.set_prefetch(prefetch)
258    }
259
260    fn is_valid(&self) -> bool {
261        self.error.is_none() && self.inner.is_valid()
262    }
263
264    fn error(&self) -> &Option<anyhow::Error> {
265        match self.error {
266            Some(_) => &self.error,
267            None => self.inner.error(),
268        }
269    }
270
271    fn rewind(&mut self) {
272        self.inner.rewind();
273        self.reset_and_load();
274    }
275
276    fn seek(&mut self, key: &[u8]) {
277        let (_, inner_key) = self.store.make_key(key);
278        self.inner.seek(&inner_key);
279        self.reset_and_load();
280    }
281
282    fn get_key(&self) -> &Option<mkvs::Key> {
283        &self.key
284    }
285
286    fn get_value(&self) -> &Option<Vec<u8>> {
287        &self.value
288    }
289
290    fn next(&mut self) {
291        mkvs::Iterator::next(&mut *self.inner);
292        self.reset_and_load();
293    }
294}
295
296#[cfg(test)]
297mod test {
298    extern crate test;
299    use super::*;
300    use crate::{keymanager::KeyPair, storage, testing::mock::empty_store};
301    use test::Bencher;
302
303    const ITEM_COUNT: usize = 10_000;
304
305    fn confidential<'ctx, S: Store + 'ctx>(inner: S, consistent: bool) -> Box<dyn Store + 'ctx> {
306        let state_key = if consistent {
307            [0xaau8; 32]
308        } else {
309            KeyPair::generate_mock().state_key.0
310        };
311        Box::new(ConfidentialStore::new_with_key(
312            inner,
313            state_key,
314            &[b"confidential store unit tests"],
315        ))
316    }
317
318    fn make_inner<'ctx, S: Store + 'ctx>(
319        store: S,
320        make_confidential: bool,
321    ) -> Box<dyn Store + 'ctx> {
322        let inner = storage::PrefixStore::new(
323            storage::PrefixStore::new(
324                storage::PrefixStore::new(store, "test module"),
325                "instance prefix",
326            ),
327            "type prefix",
328        );
329
330        // Replicate the stack as constructed in modules/contracts.
331        if make_confidential {
332            confidential(inner, false)
333        } else {
334            Box::new(storage::HashedStore::<_, blake3::Hasher>::new(inner))
335        }
336    }
337
338    fn make_items(mut num: usize) -> Vec<(Vec<u8>, Vec<u8>)> {
339        let mut items = Vec::new();
340        if num == 0 {
341            num = ITEM_COUNT;
342        }
343        for i in 0..num {
344            items.push((
345                format!("key{}", i).into_bytes(),
346                format!("value{}", i).into_bytes(),
347            ));
348        }
349        items
350    }
351
352    #[test]
353    fn basic_operations() {
354        let mut store = confidential(empty_store(), true);
355        let items = make_items(10);
356
357        // Nothing should exist at the beginning.
358        for (k, _) in items.iter() {
359            assert!(store.get(k).is_none());
360        }
361        let mut iter = store.iter();
362        iter.rewind();
363        assert!(iter.next().is_none());
364        drop(iter);
365
366        // Insert even items, then verify they're
367        // exactly the ones that exist.
368        for (k, v) in items.iter().step_by(2) {
369            store.insert(k, v);
370        }
371        for (i, (k, v)) in items.iter().enumerate() {
372            if i % 2 == 0 {
373                assert_eq!(&store.get(k).expect("item should exist"), v);
374            } else {
375                assert!(store.get(k).is_none());
376            }
377        }
378        let mut iter = store.iter();
379        iter.rewind();
380        assert_eq!(iter.count(), items.len() / 2);
381
382        // Remove some items that exist and some that don't.
383        // The stepper should remove key0 and key6, and also
384        // try removing key3 and key9.
385        for (k, _) in items.iter().step_by(3) {
386            store.remove(k);
387        }
388        for (i, (k, v)) in items.iter().enumerate() {
389            if i % 2 == 0 && i % 3 != 0 {
390                assert_eq!(&store.get(k).expect("item should exist"), v);
391            } else {
392                assert!(store.get(k).is_none());
393            }
394        }
395        let mut iter = store.iter();
396        iter.rewind();
397        assert_eq!(iter.count(), 3);
398    }
399
400    #[test]
401    fn base_corruption() {
402        let mut plain_store = empty_store();
403        let mut store = confidential(&mut plain_store, true);
404
405        // Insert something, try corrupting its bytes, then see
406        // what the confidential store does with it.
407        const KEY: &[u8] = b"key";
408        const VALUE: &[u8] = b"value";
409
410        // Insert the key and then try getting it out again, because we don't
411        // know the actual bytes in the underlying store.
412        store.insert(KEY, VALUE);
413        drop(store);
414        let mut iter = plain_store.iter();
415        iter.rewind();
416        let (key, value) = Iterator::next(&mut iter).expect("should have one item");
417        drop(iter);
418
419        // Actually encrypted?
420        let (_, enc_key) = unpack_nonce_slice(&key).expect("unpacking encrypted key should work");
421        assert_ne!(enc_key, b"key");
422
423        // Corrupt nonce part of the key.
424        let mut corrupt_key_nonce = key.clone();
425        corrupt_key_nonce[4] ^= 0xaau8;
426        plain_store.insert(&corrupt_key_nonce, &value);
427        plain_store.remove(&key);
428        let store = confidential(&mut plain_store, true);
429        assert!(store.get(KEY).is_none());
430        drop(store);
431        plain_store.remove(&corrupt_key_nonce);
432
433        // Corrupt key part of the key.
434        let mut corrupt_key_key = key.clone();
435        *corrupt_key_key.last_mut().unwrap() ^= 0xaau8;
436        plain_store.insert(&corrupt_key_key, &value);
437        let store = confidential(&mut plain_store, true);
438        assert!(store.get(KEY).is_none());
439        drop(store);
440        plain_store.remove(&corrupt_key_key);
441
442        // Validate inserting into underlying store.
443        plain_store.insert(&key, &value);
444        let store = confidential(&mut plain_store, true);
445        assert_eq!(store.get(KEY).expect("key should exist"), VALUE);
446    }
447
448    #[test]
449    #[should_panic]
450    fn corruption_value_nonce() {
451        let mut plain_store = empty_store();
452        let mut store = confidential(&mut plain_store, true);
453
454        // Insert something, try corrupting its bytes, then see
455        // what the confidential store does with it.
456        const KEY: &[u8] = b"key";
457        const VALUE: &[u8] = b"value";
458
459        // Insert the key and then try getting it out again, because we don't
460        // know the actual bytes in the underlying store.
461        store.insert(KEY, VALUE);
462        assert!(store.get(KEY).is_some());
463        drop(store);
464        let mut iter = plain_store.iter();
465        iter.rewind();
466        let (key, value) = Iterator::next(&mut iter).expect("should have one item");
467        drop(iter);
468
469        // Corrupt the nonce part of the value.
470        let mut corrupt_value_nonce = value;
471        corrupt_value_nonce[4] ^= 0xaau8;
472        plain_store.remove(&key);
473        plain_store.insert(&key, &corrupt_value_nonce);
474        let store = confidential(&mut plain_store, true);
475        store.get(KEY);
476        drop(store);
477        plain_store.remove(&key);
478    }
479
480    #[test]
481    #[should_panic]
482    fn corruption_value_value() {
483        let mut plain_store = empty_store();
484        let mut store = confidential(&mut plain_store, true);
485
486        // Insert something, try corrupting its bytes, then see
487        // what the confidential store does with it.
488        const KEY: &[u8] = b"key";
489        const VALUE: &[u8] = b"value";
490
491        // Insert the key and then try getting it out again, because we don't
492        // know the actual bytes in the underlying store.
493        store.insert(KEY, VALUE);
494        assert!(store.get(KEY).is_some());
495        drop(store);
496        let mut iter = plain_store.iter();
497        iter.rewind();
498        let (key, value) = Iterator::next(&mut iter).expect("should have one item");
499        drop(iter);
500
501        // Corrupt the nonce part of the value.
502        let mut corrupt_value_value = value;
503        *corrupt_value_value.last_mut().unwrap() ^= 0xaau8;
504        plain_store.remove(&key);
505        plain_store.insert(&key, &corrupt_value_value);
506        let store = confidential(&mut plain_store, true);
507        store.get(KEY);
508        drop(store);
509        plain_store.remove(&key);
510    }
511
512    fn run<F>(confidential: bool, inserts: usize, mut cb: F)
513    where
514        F: FnMut(&mut Box<dyn Store + '_>, &Vec<(Vec<u8>, Vec<u8>)>),
515    {
516        let mut store = make_inner(empty_store(), confidential);
517
518        let items = make_items(0);
519        for i in 0..inserts {
520            let item = &items[i % items.len()];
521            store.insert(&item.0, &item.1);
522        }
523
524        cb(&mut store, &items);
525    }
526
527    #[bench]
528    fn plain_insert(b: &mut Bencher) {
529        run(false, 0, |store, items| {
530            let mut i = 0;
531            b.iter(|| {
532                let item = &items[i % items.len()];
533                store.insert(&item.0, &item.1);
534                i += 1;
535            });
536        });
537    }
538
539    #[bench]
540    fn plain_get(b: &mut Bencher) {
541        run(false, ITEM_COUNT / 2, |store, items| {
542            let mut i = 0;
543            b.iter(|| {
544                let j =
545                    (2 * i + ((items.len() + 1) % 2) * ((i / (items.len() / 2)) % 2)) % items.len();
546                let item = &items[j % items.len()];
547                store.get(&item.0);
548                i += 1;
549            });
550        });
551    }
552
553    #[bench]
554    fn plain_scan(b: &mut Bencher) {
555        run(false, ITEM_COUNT, |store, _| {
556            let mut it = store.iter();
557            b.iter(|| {
558                match it.next() {
559                    Some(_) => {}
560                    None => {
561                        it = store.iter();
562                    }
563                };
564            });
565        });
566    }
567
568    #[bench]
569    fn confidential_insert(b: &mut Bencher) {
570        run(true, 0, |store, items| {
571            let mut i = 0;
572            b.iter(|| {
573                let item = &items[i % items.len()];
574                store.insert(&item.0, &item.1);
575                i += 1;
576            });
577        });
578    }
579
580    #[bench]
581    fn confidential_get(b: &mut Bencher) {
582        run(true, ITEM_COUNT / 2, |store, items| {
583            let mut i = 0;
584            b.iter(|| {
585                let j =
586                    (2 * i + ((items.len() + 1) % 2) * ((i / (items.len() / 2)) % 2)) % items.len();
587                let item = &items[j % items.len()];
588                store.get(&item.0);
589                i += 1;
590            });
591        });
592    }
593
594    #[bench]
595    fn confidential_scan(b: &mut Bencher) {
596        run(true, ITEM_COUNT, |store, _| {
597            let mut it = store.iter();
598            b.iter(|| {
599                match it.next() {
600                    Some(_) => {}
601                    None => {
602                        it = store.iter();
603                    }
604                };
605            });
606        });
607    }
608}