1mod base64_array;
2mod base64_vec;
3mod error;
4
5use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use std::{borrow::Cow, ops::Deref};
8use uuid::Uuid;
9use zeroize::{Zeroize, ZeroizeOnDrop};
10
11pub use cipherstash_config;
12pub use error::*;
14pub mod testing;
15
16#[async_trait]
17pub trait ViturConnection {
18 async fn send<Request: ViturRequest>(
19 &self,
20 request: Request,
21 access_token: &str,
22 ) -> Result<Request::Response, ViturRequestError>;
23}
24
25pub trait ViturResponse: Serialize + for<'de> Deserialize<'de> + Send {}
26
27#[async_trait]
28pub trait ViturRequest: Serialize + for<'de> Deserialize<'de> + Sized + Send {
29 type Response: ViturResponse;
30
31 const SCOPE: &'static str;
32 const ENDPOINT: &'static str;
33}
34
35#[derive(Debug, Serialize, Deserialize)]
39pub struct CreateDatasetRequest<'a> {
40 pub name: Cow<'a, str>,
41 pub description: Cow<'a, str>,
42}
43
44impl ViturRequest for CreateDatasetRequest<'_> {
45 type Response = Dataset;
46
47 const ENDPOINT: &'static str = "create-dataset";
48 const SCOPE: &'static str = "dataset:create";
49}
50
51#[derive(Default, Debug, Serialize, Deserialize)]
56pub struct ListDatasetRequest {
57 #[serde(default)]
58 pub show_disabled: bool,
59}
60
61impl ViturRequest for ListDatasetRequest {
62 type Response = Vec<Dataset>;
63
64 const ENDPOINT: &'static str = "list-datasets";
65 const SCOPE: &'static str = "dataset:list";
66}
67
68#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
71pub struct Dataset {
72 pub id: Uuid,
73 pub name: String,
74 pub description: String,
75}
76
77impl ViturResponse for Dataset {}
78impl ViturResponse for Vec<Dataset> {}
79
80#[derive(Default, Debug, Serialize, Deserialize)]
82pub struct EmptyResponse {}
83
84impl ViturResponse for EmptyResponse {}
85
86#[derive(Debug, Serialize, Deserialize)]
91pub struct CreateClientRequest<'a> {
92 pub dataset_id: Uuid,
93 pub name: Cow<'a, str>,
94 pub description: Cow<'a, str>,
95}
96
97impl ViturRequest for CreateClientRequest<'_> {
98 type Response = CreateClientResponse;
99
100 const ENDPOINT: &'static str = "create-client";
101 const SCOPE: &'static str = "client:create";
102}
103
104#[derive(Debug, Serialize, Deserialize)]
109pub struct CreateClientResponse {
110 pub id: Uuid,
111 pub dataset_id: Uuid,
112 pub name: String,
113 pub description: String,
114 pub client_key: ViturKeyMaterial,
115}
116
117impl ViturResponse for CreateClientResponse {}
118
119#[derive(Debug, Serialize, Deserialize)]
124pub struct ListClientRequest;
125
126impl ViturRequest for ListClientRequest {
127 type Response = Vec<DatasetClient>;
128
129 const ENDPOINT: &'static str = "list-clients";
130 const SCOPE: &'static str = "client:list";
131}
132
133#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
136#[serde(untagged)]
137pub enum ClientDatasetId {
138 Single(Uuid),
139 Multiple(Vec<Uuid>),
140}
141
142impl PartialEq<Uuid> for ClientDatasetId {
144 fn eq(&self, other: &Uuid) -> bool {
145 if let ClientDatasetId::Single(id) = self {
146 id == other
147 } else {
148 false
149 }
150 }
151}
152
153#[derive(Debug, Serialize, Deserialize)]
155pub struct DatasetClient {
156 pub id: Uuid,
157 pub dataset_id: ClientDatasetId,
158 pub name: String,
159 pub description: String,
160}
161
162impl ViturResponse for Vec<DatasetClient> {}
163
164#[derive(Debug, Serialize, Deserialize)]
169pub struct DeleteClientRequest {
170 pub client_id: Uuid,
171}
172
173impl ViturRequest for DeleteClientRequest {
174 type Response = DeleteClientResponse;
175
176 const ENDPOINT: &'static str = "delete-client";
177 const SCOPE: &'static str = "client:delete";
178}
179
180#[derive(Default, Debug, Serialize, Deserialize)]
181pub struct DeleteClientResponse {}
182
183impl ViturResponse for DeleteClientResponse {}
184
185#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
187pub struct ViturKeyMaterial(#[serde(with = "base64_vec")] Vec<u8>);
188opaque_debug::implement!(ViturKeyMaterial);
189
190impl From<Vec<u8>> for ViturKeyMaterial {
191 fn from(inner: Vec<u8>) -> Self {
192 Self(inner)
193 }
194}
195
196impl Deref for ViturKeyMaterial {
197 type Target = [u8];
198
199 fn deref(&self) -> &Self::Target {
200 &self.0
201 }
202}
203
204#[derive(Debug, Serialize, Deserialize)]
208pub struct GeneratedKey {
209 pub key_material: ViturKeyMaterial,
210 #[serde(with = "base64_vec")]
212 pub tag: Vec<u8>,
213}
214
215#[derive(Debug, Serialize, Deserialize)]
217pub struct GenerateKeyResponse {
218 pub keys: Vec<GeneratedKey>,
219}
220
221impl ViturResponse for GenerateKeyResponse {}
222
223#[derive(Debug, Serialize, Deserialize, Clone)]
225pub struct GenerateKeySpec<'a> {
226 #[serde(with = "base64_array")]
228 pub iv: [u8; 16],
229 pub descriptor: Cow<'a, str>,
231
232 #[serde(default = "Vec::new")]
233 pub context: Vec<Context>,
234}
235
236impl<'a> GenerateKeySpec<'a> {
237 pub fn new(iv: [u8; 16], descriptor: &'a str) -> Self {
238 Self {
239 iv,
240 descriptor: Cow::from(descriptor),
241 context: Vec::new(),
242 }
243 }
244
245 pub fn new_with_context(iv: [u8; 16], descriptor: &'a str, context: Vec<Context>) -> Self {
246 Self {
247 iv,
248 descriptor: Cow::from(descriptor),
249 context,
250 }
251 }
252}
253#[derive(Debug, Serialize, Deserialize, Clone)]
257pub enum Context {
259 Tag(String),
261
262 Value(String, String),
265
266 IdentityClaim(String),
271}
272
273impl Context {
274 pub fn new_tag(tag: impl Into<String>) -> Self {
275 Self::Tag(tag.into())
276 }
277
278 pub fn new_value(key: impl Into<String>, value: impl Into<String>) -> Self {
279 Self::Value(key.into(), value.into())
280 }
281
282 pub fn new_identity_claim(claim: &str) -> Self {
283 Self::IdentityClaim(claim.to_string())
284 }
285}
286
287#[derive(Debug, Serialize, Deserialize)]
295pub struct GenerateKeyRequest<'a> {
296 pub client_id: Uuid,
297 pub dataset_id: Option<Uuid>,
298 pub keys: Cow<'a, [GenerateKeySpec<'a>]>,
299}
300
301impl ViturRequest for GenerateKeyRequest<'_> {
302 type Response = GenerateKeyResponse;
303
304 const ENDPOINT: &'static str = "generate-data-key";
305 const SCOPE: &'static str = "data_key:generate";
306}
307
308#[derive(Debug, Serialize, Deserialize)]
310pub struct RetrievedKey {
311 pub key_material: ViturKeyMaterial,
312}
313
314#[derive(Debug, Serialize, Deserialize)]
317pub struct RetrieveKeyResponse {
318 pub keys: Vec<RetrievedKey>,
319}
320
321impl ViturResponse for RetrieveKeyResponse {}
322
323#[derive(Debug, Serialize, Deserialize, Clone)]
325pub struct RetrieveKeySpec<'a> {
326 #[serde(with = "base64_array")]
327 pub iv: [u8; 16],
328 pub descriptor: Cow<'a, str>,
330 pub tag: Cow<'a, [u8]>,
331
332 #[serde(default = "Vec::new")]
333 pub context: Vec<Context>,
334
335 #[serde(default)]
338 pub tag_version: usize,
339}
340
341impl<'a> RetrieveKeySpec<'a> {
342 const DEFAULT_TAG_VERSION: usize = 0;
343
344 pub fn new(iv: [u8; 16], tag: &'a [u8], descriptor: &'a str) -> Self {
345 Self {
346 iv,
347 descriptor: Cow::from(descriptor),
348 tag: Cow::from(tag),
349 context: Vec::new(),
350 tag_version: Self::DEFAULT_TAG_VERSION,
351 }
352 }
353
354 pub fn with_context(mut self, context: Vec<Context>) -> Self {
355 self.context = context;
356 self
357 }
358}
359
360#[derive(Debug, Serialize, Deserialize)]
366pub struct RetrieveKeyRequest<'a> {
367 pub client_id: Uuid,
368 pub dataset_id: Option<Uuid>,
369 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
370}
371
372impl ViturRequest for RetrieveKeyRequest<'_> {
373 type Response = RetrieveKeyResponse;
374
375 const ENDPOINT: &'static str = "retrieve-data-key";
376 const SCOPE: &'static str = "data_key:retrieve";
377}
378
379#[derive(Debug, Serialize, Deserialize)]
383pub struct DisableDatasetRequest {
384 pub dataset_id: Uuid,
385}
386
387impl ViturRequest for DisableDatasetRequest {
388 type Response = EmptyResponse;
389
390 const ENDPOINT: &'static str = "disable-dataset";
391 const SCOPE: &'static str = "dataset:disable";
392}
393
394#[derive(Debug, Serialize, Deserialize)]
398pub struct EnableDatasetRequest {
399 pub dataset_id: Uuid,
400}
401
402impl ViturRequest for EnableDatasetRequest {
403 type Response = EmptyResponse;
404
405 const ENDPOINT: &'static str = "enable-dataset";
406 const SCOPE: &'static str = "dataset:enable";
407}
408
409#[derive(Debug, Serialize, Deserialize)]
415pub struct ModifyDatasetRequest<'a> {
416 pub dataset_id: Uuid,
417
418 pub name: Option<Cow<'a, str>>,
419 pub description: Option<Cow<'a, str>>,
420}
421
422impl ViturRequest for ModifyDatasetRequest<'_> {
423 type Response = EmptyResponse;
424
425 const ENDPOINT: &'static str = "modify-dataset";
426 const SCOPE: &'static str = "dataset:modify";
427}
428
429#[derive(Debug, Serialize, Deserialize)]
434pub struct GrantDatasetRequest {
435 pub client_id: Uuid,
436 pub dataset_id: Uuid,
437}
438
439impl ViturRequest for GrantDatasetRequest {
440 type Response = EmptyResponse;
441
442 const ENDPOINT: &'static str = "grant-dataset";
443 const SCOPE: &'static str = "dataset:grant";
444}
445
446#[derive(Debug, Serialize, Deserialize)]
450pub struct RevokeDatasetRequest {
451 pub client_id: Uuid,
452 pub dataset_id: Uuid,
453}
454
455impl ViturRequest for RevokeDatasetRequest {
456 type Response = EmptyResponse;
457
458 const ENDPOINT: &'static str = "revoke-dataset";
459 const SCOPE: &'static str = "dataset:revoke";
460}
461
462#[derive(Debug, Serialize, Deserialize)]
471pub struct LoadDatasetRequest {
472 pub client_id: Uuid,
473 pub dataset_id: Option<Uuid>,
474}
475
476impl ViturRequest for LoadDatasetRequest {
477 type Response = LoadDatasetResponse;
478
479 const ENDPOINT: &'static str = "load-dataset";
480 const SCOPE: &'static str = "data_key:retrieve";
484}
485
486#[derive(Debug, Serialize, Deserialize)]
490pub struct LoadDatasetResponse {
491 pub partial_index_key: RetrievedKey,
492 pub dataset: Dataset,
493}
494
495impl ViturResponse for LoadDatasetResponse {}