|
1 | | -//! The deterministic key source of the `deterministic-kms` test build: a |
2 | | -//! copy of `DeterministicSource` in stack-encrypt's `tests/common/mod.rs`, |
3 | | -//! which sealed the record fixture (`tests/fixtures/record_lowering.json`). |
| 1 | +//! The deterministic key source of the `deterministic-kms` test build: |
| 2 | +//! `stack-kms`'s [`DeterministicSource`] (behind its `test-support` feature), |
| 3 | +//! the one definition that also sealed stack-encrypt's record fixture |
| 4 | +//! (`tests/fixtures/record_lowering.json`). A Go test that loads this build |
| 5 | +//! with the fixture's seed opens the records Rust sealed and derives the same |
| 6 | +//! term bytes, because both run the same code. |
4 | 7 | //! |
5 | | -//! Every data key is `SHA-256(seed ‖ "key" ‖ 0 ‖ descriptor ‖ 0 ‖ iv)`, its |
6 | | -//! tag `SHA-256(seed ‖ "tag" ‖ 0 ‖ descriptor ‖ 0 ‖ iv)`, and the IV |
7 | | -//! `SHA-256(seed ‖ "iv" ‖ 0 ‖ descriptor ‖ 0 ‖ counter)[..16]`, where |
8 | | -//! `descriptor` is the context the leaf is sealed under as ZeroKMS renders |
9 | | -//! it. A leaf opens under its own field's descriptor and no other, as under |
10 | | -//! ZeroKMS. The index key is `FakeDataKeySource`'s, the same every test in |
11 | | -//! the repository derives terms under. So a Go test that loads this build |
12 | | -//! with the fixture's seed opens the records Rust sealed and derives the |
13 | | -//! same term bytes. |
14 | | -//! |
15 | | -//! It is a test double. The feature that compiles it is off by default, the |
16 | | -//! Go package never embeds this build, and the fixture README beside the |
17 | | -//! JSON file is the one definition both copies follow. |
18 | | -
|
19 | | -use std::borrow::Cow; |
20 | | -use std::sync::atomic::{AtomicU64, Ordering}; |
21 | | - |
22 | | -use sha2::{Digest, Sha256}; |
23 | | -use stack_kms::{ |
24 | | - DataKey, DataKeySource, DataKeyWithTag, FakeDataKeySource, GenerateKeyPayload, IdentifiedBy, |
25 | | - IndexKey, IndexKeySource, RetrieveKeyPayload, UnverifiedContext, |
26 | | -}; |
27 | | -use uuid::Uuid; |
28 | | - |
29 | | -/// The seeded source. See the [module docs](self). |
30 | | -pub struct DeterministicSource { |
31 | | - seed: [u8; 32], |
32 | | - counter: AtomicU64, |
33 | | - index: FakeDataKeySource, |
34 | | -} |
35 | | - |
36 | | -impl DeterministicSource { |
37 | | - /// A source over `seed`. |
38 | | - pub fn new(seed: [u8; 32]) -> Self { |
39 | | - Self { |
40 | | - seed, |
41 | | - counter: AtomicU64::new(0), |
42 | | - index: FakeDataKeySource::new(), |
43 | | - } |
44 | | - } |
45 | | - |
46 | | - fn derive(&self, what: &str, descriptor: &str, salt: &[u8]) -> [u8; 32] { |
47 | | - let mut hasher = Sha256::new(); |
48 | | - hasher.update(self.seed); |
49 | | - hasher.update(what.as_bytes()); |
50 | | - hasher.update([0u8]); |
51 | | - hasher.update(descriptor.as_bytes()); |
52 | | - hasher.update([0u8]); |
53 | | - hasher.update(salt); |
54 | | - hasher.finalize().into() |
55 | | - } |
56 | | -} |
57 | | - |
58 | | -impl DataKeySource for DeterministicSource { |
59 | | - async fn generate_keys( |
60 | | - &self, |
61 | | - payloads: Vec<GenerateKeyPayload<'_>>, |
62 | | - _keyset_id: Option<Uuid>, |
63 | | - _unverified_context: Option<Cow<'_, UnverifiedContext>>, |
64 | | - ) -> Result<Vec<DataKeyWithTag>, stack_kms::Error> { |
65 | | - Ok(payloads |
66 | | - .into_iter() |
67 | | - .map(|payload| { |
68 | | - let n = self.counter.fetch_add(1, Ordering::SeqCst); |
69 | | - let iv_bytes = self.derive("iv", payload.descriptor, &n.to_le_bytes()); |
70 | | - let mut iv = stack_kms::Iv::default(); |
71 | | - let width = iv.len(); |
72 | | - iv.copy_from_slice(&iv_bytes[..width]); |
73 | | - let key = self.derive("key", payload.descriptor, &iv); |
74 | | - let tag = self.derive("tag", payload.descriptor, &iv); |
75 | | - DataKeyWithTag { |
76 | | - key: DataKey { iv, key }, |
77 | | - tag: tag.to_vec(), |
78 | | - decryption_policy: payload.decryption_policy, |
79 | | - } |
80 | | - }) |
81 | | - .collect()) |
82 | | - } |
83 | | - |
84 | | - async fn retrieve_keys( |
85 | | - &self, |
86 | | - payloads: Vec<RetrieveKeyPayload<'_>>, |
87 | | - _keyset_id: Option<Uuid>, |
88 | | - _unverified_context: Option<&UnverifiedContext>, |
89 | | - ) -> Result<Vec<DataKey>, stack_kms::Error> { |
90 | | - payloads |
91 | | - .iter() |
92 | | - .map(|payload| { |
93 | | - let iv: stack_kms::Iv = *payload.iv.as_ref(); |
94 | | - let tag = self.derive("tag", payload.descriptor, &iv); |
95 | | - if tag[..] != *payload.tag { |
96 | | - return Err(stack_kms::Error::RetrieveKey( |
97 | | - stack_kms::RetrieveKeyError::FailedRetrieval( |
98 | | - "the tag is not this descriptor's".to_string(), |
99 | | - ), |
100 | | - )); |
101 | | - } |
102 | | - let key = self.derive("key", payload.descriptor, &iv); |
103 | | - Ok(DataKey { iv, key }) |
104 | | - }) |
105 | | - .collect() |
106 | | - } |
107 | | -} |
| 8 | +//! It is a test double. The feature that compiles it is off by default, and |
| 9 | +//! the build it produces goes under languages/golang/encrypt/testdata, which |
| 10 | +//! `go build` and the package's `//go:embed wasm` both ignore, so no Go |
| 11 | +//! binary carries it; the tests read it from disk. |
108 | 12 |
|
109 | | -impl IndexKeySource for DeterministicSource { |
110 | | - async fn load_index_key( |
111 | | - &self, |
112 | | - keyset_id: Option<IdentifiedBy>, |
113 | | - ) -> Result<(Uuid, IndexKey), stack_kms::Error> { |
114 | | - self.index.load_index_key(keyset_id).await |
115 | | - } |
116 | | -} |
| 13 | +pub use stack_kms::DeterministicSource; |
0 commit comments