1use super::{Store, StoreError, commit, key_failed};
6use crate::envelope::{self, Context, Key, WrapLimit};
7
8impl Store {
9 pub fn rewrap(&self, new: &Key, limit: usize) -> Result<usize, StoreError> {
14 let old = self.tenant_key.id();
15 let mut tx = self.catalog.immediate()?;
16 let rows = tx.wrapped_under(old, limit)?;
17 let after = tx.wraps(new.id())?.saturating_add(rows.len() as u64);
18 if !rows.is_empty() && envelope::wrap_limit(after - 1) == WrapLimit::Refuse {
19 return Err(StoreError::LimitReached);
20 }
21 for row in &rows {
22 let ctx = Context::Version {
23 tenant: self.tenant,
24 uuid: row.uuid,
25 version: row.version,
26 };
27 let rewrapped =
28 envelope::rewrap(&self.tenant_key, new, &ctx, &row.wrapped).map_err(key_failed)?;
29 tx.set_wrapped(row.version, new.id(), &rewrapped)?;
30 tx.add_wrap(new.id())?;
31 }
32 commit(tx)?;
33 Ok(rows.len())
34 }
35
36 pub fn rows_under_other_keys(&self) -> Result<u64, StoreError> {
39 self.catalog
40 .snapshot()?
41 .count_not_under(self.tenant_key.id())
42 }
43}
44
45#[cfg(test)]
46mod rekey_tests {
47 use super::super::testing::vcon;
48 use super::super::tests::{Fixture, fixture};
49 use super::*;
50 use crate::envelope::{self, Context};
51 use crate::ids::VersionId;
52 use crate::store::Created;
53
54 fn rows(f: &Fixture) -> Vec<(VersionId, uuid::Uuid, Vec<u8>, Vec<u8>)> {
56 f.store
57 .conn()
58 .prepare(
59 "SELECT version, uuid, tenant_key_id, wrapped_key FROM version ORDER BY version",
60 )
61 .expect("prepare")
62 .query_map([], |r| {
63 Ok((
64 VersionId(r.get(0)?),
65 uuid::Uuid::from_bytes(r.get(1)?),
66 r.get(2)?,
67 r.get(3)?,
68 ))
69 })
70 .expect("query")
71 .collect::<Result<_, _>>()
72 .expect("rows")
73 }
74
75 fn store_five(f: &Fixture) {
76 for n in 0..5 {
77 let uuid = format!("01a07da8-c2bb-83e5-b9a2-279e0d16bc4{n}");
78 let (found, body) = vcon(&uuid, "kept");
79 assert!(matches!(
80 f.store.create(&f.caller, &found, &body),
81 Ok(Created::New(_))
82 ));
83 }
84 }
85
86 fn wraps_of(f: &Fixture, key: &Key) -> i64 {
87 f.store
88 .conn()
89 .query_row(
90 "SELECT coalesce(sum(wraps), 0) FROM key_use WHERE tenant_key_id = ?1",
91 [key.id().bytes()],
92 |r| r.get(0),
93 )
94 .expect("wraps")
95 }
96
97 #[test]
98 fn rewrap_moves_every_data_key_to_the_new_key_in_batches() {
99 let f = fixture();
100 store_five(&f);
101 let (old, new) = (f.tenant_key(), Key::generate().expect("key"));
102 assert_eq!(f.store.rewrap(&new, 2), Ok(2));
103 assert_eq!(f.store.rewrap(&new, 2), Ok(2));
104 assert_eq!(f.store.rewrap(&new, 2), Ok(1));
105 assert_eq!(f.store.rewrap(&new, 2), Ok(0), "none left");
106 let rows = rows(&f);
107 assert_eq!(rows.len(), 5);
108 for (version, uuid, key_id, wrapped) in rows {
109 let ctx = Context::Version {
110 tenant: f.tenant(),
111 uuid,
112 version,
113 };
114 assert_eq!(
115 key_id,
116 new.id().bytes().to_vec(),
117 "the row names the new key"
118 );
119 assert!(
120 envelope::unwrap(&new, &ctx, &wrapped).is_ok(),
121 "opens under the new key"
122 );
123 assert!(
124 envelope::unwrap(&old, &ctx, &wrapped).is_err(),
125 "not under the old one"
126 );
127 }
128 assert_eq!(
129 wraps_of(&f, &new),
130 5,
131 "each rewrap is counted against the new key"
132 );
133 }
134
135 #[test]
136 fn a_rewrap_cut_short_leaves_every_row_opening_under_one_key_or_the_other() {
137 let f = fixture();
138 store_five(&f);
139 let (old, new) = (f.tenant_key(), Key::generate().expect("key"));
140 assert_eq!(f.store.rewrap(&new, 3), Ok(3));
141 let (mut under_old, mut under_new) = (0, 0);
142 for (version, uuid, key_id, wrapped) in rows(&f) {
143 let ctx = Context::Version {
144 tenant: f.tenant(),
145 uuid,
146 version,
147 };
148 let (key, count) = if key_id == new.id().bytes().to_vec() {
149 (&new, &mut under_new)
150 } else {
151 (&old, &mut under_old)
152 };
153 assert!(envelope::unwrap(key, &ctx, &wrapped).is_ok(), "{version}");
154 *count += 1;
155 }
156 assert_eq!((under_old, under_new), (2, 3));
157 }
158
159 #[test]
160 fn a_new_key_at_its_wrap_limit_is_refused_and_nothing_changes() {
161 let f = fixture();
162 store_five(&f);
163 let new = Key::generate().expect("key");
164 f.store
165 .conn()
166 .execute(
167 "INSERT INTO key_use (tenant_key_id, wraps) VALUES (?1, ?2)",
168 rusqlite::params![new.id().bytes(), 1_i64 << 32],
169 )
170 .expect("at the limit");
171 let before = rows(&f);
172 assert_eq!(f.store.rewrap(&new, 10), Err(StoreError::LimitReached));
173 assert_eq!(rows(&f), before);
174 }
175}