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