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