Skip to main content

oasis_runtime_sdk/storage/
confidential.rs

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