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
25fn 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#[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
49pub 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 pub fn new_with_key(inner: S, key: [u8; KEY_SIZE], value_context: &[&[u8]]) -> Self {
61 let actual_key = Zeroizing::new(key);
62
63 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 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 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 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 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 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 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 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 const KEY: &[u8] = b"key";
408 const VALUE: &[u8] = b"value";
409
410 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 let (_, enc_key) = unpack_nonce_slice(&key).expect("unpacking encrypted key should work");
421 assert_ne!(enc_key, b"key");
422
423 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 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 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 const KEY: &[u8] = b"key";
457 const VALUE: &[u8] = b"value";
458
459 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 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 const KEY: &[u8] = b"key";
489 const VALUE: &[u8] = b"value";
490
491 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 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}