1mod base64_array;
2mod base64_vec;
3mod error;
4
5use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use std::{borrow::Cow, fmt::Display, 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    pub is_disabled: bool,
76}
77
78impl ViturResponse for Dataset {}
79impl ViturResponse for Vec<Dataset> {}
80
81#[derive(Default, Debug, Serialize, Deserialize)]
83pub struct EmptyResponse {}
84
85impl ViturResponse for EmptyResponse {}
86
87#[derive(Debug, Serialize, Deserialize)]
92pub struct CreateClientRequest<'a> {
93    pub dataset_id: Uuid,
94    pub name: Cow<'a, str>,
95    pub description: Cow<'a, str>,
96}
97
98impl ViturRequest for CreateClientRequest<'_> {
99    type Response = CreateClientResponse;
100
101    const ENDPOINT: &'static str = "create-client";
102    const SCOPE: &'static str = "client:create";
103}
104
105#[derive(Debug, Serialize, Deserialize)]
110pub struct CreateClientResponse {
111    pub id: Uuid,
112    pub dataset_id: Uuid,
113    pub name: String,
114    pub description: String,
115    pub client_key: ViturKeyMaterial,
116}
117
118impl ViturResponse for CreateClientResponse {}
119
120#[derive(Debug, Serialize, Deserialize)]
125pub struct ListClientRequest;
126
127impl ViturRequest for ListClientRequest {
128    type Response = Vec<DatasetClient>;
129
130    const ENDPOINT: &'static str = "list-clients";
131    const SCOPE: &'static str = "client:list";
132}
133
134#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
137#[serde(untagged)]
138pub enum ClientDatasetId {
139    Single(Uuid),
140    Multiple(Vec<Uuid>),
141}
142
143impl PartialEq<Uuid> for ClientDatasetId {
145    fn eq(&self, other: &Uuid) -> bool {
146        if let ClientDatasetId::Single(id) = self {
147            id == other
148        } else {
149            false
150        }
151    }
152}
153
154#[derive(Debug, Serialize, Deserialize)]
156pub struct DatasetClient {
157    pub id: Uuid,
158    pub dataset_id: ClientDatasetId,
159    pub name: String,
160    pub description: String,
161}
162
163impl ViturResponse for Vec<DatasetClient> {}
164
165#[derive(Debug, Serialize, Deserialize)]
170pub struct DeleteClientRequest {
171    pub client_id: Uuid,
172}
173
174impl ViturRequest for DeleteClientRequest {
175    type Response = DeleteClientResponse;
176
177    const ENDPOINT: &'static str = "delete-client";
178    const SCOPE: &'static str = "client:delete";
179}
180
181#[derive(Default, Debug, Serialize, Deserialize)]
182pub struct DeleteClientResponse {}
183
184impl ViturResponse for DeleteClientResponse {}
185
186#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
188pub struct ViturKeyMaterial(#[serde(with = "base64_vec")] Vec<u8>);
189opaque_debug::implement!(ViturKeyMaterial);
190
191impl From<Vec<u8>> for ViturKeyMaterial {
192    fn from(inner: Vec<u8>) -> Self {
193        Self(inner)
194    }
195}
196
197impl Deref for ViturKeyMaterial {
198    type Target = [u8];
199
200    fn deref(&self) -> &Self::Target {
201        &self.0
202    }
203}
204
205#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Zeroize)]
206#[serde(transparent)]
207pub struct KeyId(#[serde(with = "base64_array")] [u8; 16]);
208
209impl KeyId {
210    pub fn into_inner(self) -> [u8; 16] {
211        self.0
212    }
213}
214
215impl Display for KeyId {
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        write!(f, "{}", const_hex::encode(self.0))
218    }
219}
220
221impl From<[u8; 16]> for KeyId {
222    fn from(inner: [u8; 16]) -> Self {
223        Self(inner)
224    }
225}
226
227impl AsRef<[u8; 16]> for KeyId {
228    fn as_ref(&self) -> &[u8; 16] {
229        &self.0
230    }
231}
232
233#[derive(Debug, Serialize, Deserialize)]
237pub struct GeneratedKey {
238    pub key_material: ViturKeyMaterial,
239    #[serde(with = "base64_vec")]
241    pub tag: Vec<u8>,
242}
243
244#[derive(Debug, Serialize, Deserialize)]
246pub struct GenerateKeyResponse {
247    pub keys: Vec<GeneratedKey>,
248}
249
250impl ViturResponse for GenerateKeyResponse {}
251
252#[derive(Debug, Serialize, Deserialize, Clone)]
254pub struct GenerateKeySpec<'a> {
255    #[serde(alias = "id")]
257    pub iv: KeyId,
258    pub descriptor: Cow<'a, str>,
260
261    #[serde(default = "Vec::new")]
262    pub context: Vec<Context>,
263}
264
265impl<'a> GenerateKeySpec<'a> {
266    pub fn new(iv: [u8; 16], descriptor: &'a str) -> Self {
267        Self {
268            iv: KeyId(iv),
269            descriptor: Cow::from(descriptor),
270            context: Vec::new(),
271        }
272    }
273
274    pub fn new_with_context(iv: [u8; 16], descriptor: &'a str, context: Vec<Context>) -> Self {
275        Self {
276            iv: KeyId(iv),
277            descriptor: Cow::from(descriptor),
278            context,
279        }
280    }
281}
282#[derive(Debug, Serialize, Deserialize, Clone)]
286pub enum Context {
288    Tag(String),
290
291    Value(String, String),
294
295    IdentityClaim(String),
300}
301
302impl Context {
303    pub fn new_tag(tag: impl Into<String>) -> Self {
304        Self::Tag(tag.into())
305    }
306
307    pub fn new_value(key: impl Into<String>, value: impl Into<String>) -> Self {
308        Self::Value(key.into(), value.into())
309    }
310
311    pub fn new_identity_claim(claim: &str) -> Self {
312        Self::IdentityClaim(claim.to_string())
313    }
314}
315
316#[derive(Debug, Serialize, Deserialize)]
324pub struct GenerateKeyRequest<'a> {
325    pub client_id: Uuid,
326    pub dataset_id: Option<Uuid>,
327    pub keys: Cow<'a, [GenerateKeySpec<'a>]>,
328}
329
330impl ViturRequest for GenerateKeyRequest<'_> {
331    type Response = GenerateKeyResponse;
332
333    const ENDPOINT: &'static str = "generate-data-key";
334    const SCOPE: &'static str = "data_key:generate";
335}
336
337#[derive(Debug, Serialize, Deserialize)]
339pub struct RetrievedKey {
340    pub key_material: ViturKeyMaterial,
341}
342
343#[derive(Debug, Serialize, Deserialize)]
346pub struct RetrieveKeyResponse {
347    pub keys: Vec<RetrievedKey>,
348}
349
350impl ViturResponse for RetrieveKeyResponse {}
351
352#[derive(Debug, Serialize, Deserialize, Clone)]
354pub struct RetrieveKeySpec<'a> {
355    #[serde(alias = "id")]
356    pub iv: KeyId,
357    pub descriptor: Cow<'a, str>,
359    pub tag: Cow<'a, [u8]>,
360
361    #[serde(default = "Vec::new")]
362    pub context: Vec<Context>,
363
364    #[serde(default)]
367    pub tag_version: usize,
368}
369
370impl<'a> RetrieveKeySpec<'a> {
371    const DEFAULT_TAG_VERSION: usize = 0;
372
373    pub fn new(id: KeyId, tag: &'a [u8], descriptor: &'a str) -> Self {
374        Self {
375            iv: id,
376            descriptor: Cow::from(descriptor),
377            tag: Cow::from(tag),
378            context: Vec::new(),
379            tag_version: Self::DEFAULT_TAG_VERSION,
380        }
381    }
382
383    pub fn with_context(mut self, context: Vec<Context>) -> Self {
384        self.context = context;
385        self
386    }
387}
388
389#[derive(Debug, Serialize, Deserialize)]
395pub struct RetrieveKeyRequest<'a> {
396    pub client_id: Uuid,
397    pub dataset_id: Option<Uuid>,
398    pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
399}
400
401impl ViturRequest for RetrieveKeyRequest<'_> {
402    type Response = RetrieveKeyResponse;
403
404    const ENDPOINT: &'static str = "retrieve-data-key";
405    const SCOPE: &'static str = "data_key:retrieve";
406}
407
408#[derive(Debug, Serialize, Deserialize)]
414pub struct RetrieveKeyRequestFallible<'a> {
415    pub client_id: Uuid,
416    pub dataset_id: Option<Uuid>,
417    pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
418}
419
420impl ViturRequest for RetrieveKeyRequestFallible<'_> {
421    type Response = RetrieveKeyResponseFallible;
422
423    const ENDPOINT: &'static str = "retrieve-data-key-fallible";
424    const SCOPE: &'static str = "data_key:retrieve";
425}
426
427#[derive(Debug, Serialize, Deserialize)]
429pub struct RetrieveKeyResponseFallible {
430    pub keys: Vec<Result<RetrievedKey, String>>, }
432
433impl ViturResponse for RetrieveKeyResponseFallible {}
434
435#[derive(Debug, Serialize, Deserialize)]
439pub struct DisableDatasetRequest {
440    pub dataset_id: Uuid,
441}
442
443impl ViturRequest for DisableDatasetRequest {
444    type Response = EmptyResponse;
445
446    const ENDPOINT: &'static str = "disable-dataset";
447    const SCOPE: &'static str = "dataset:disable";
448}
449
450#[derive(Debug, Serialize, Deserialize)]
454pub struct EnableDatasetRequest {
455    pub dataset_id: Uuid,
456}
457
458impl ViturRequest for EnableDatasetRequest {
459    type Response = EmptyResponse;
460
461    const ENDPOINT: &'static str = "enable-dataset";
462    const SCOPE: &'static str = "dataset:enable";
463}
464
465#[derive(Debug, Serialize, Deserialize)]
471pub struct ModifyDatasetRequest<'a> {
472    pub dataset_id: Uuid,
473
474    pub name: Option<Cow<'a, str>>,
475    pub description: Option<Cow<'a, str>>,
476}
477
478impl ViturRequest for ModifyDatasetRequest<'_> {
479    type Response = EmptyResponse;
480
481    const ENDPOINT: &'static str = "modify-dataset";
482    const SCOPE: &'static str = "dataset:modify";
483}
484
485#[derive(Debug, Serialize, Deserialize)]
490pub struct GrantDatasetRequest {
491    pub client_id: Uuid,
492    pub dataset_id: Uuid,
493}
494
495impl ViturRequest for GrantDatasetRequest {
496    type Response = EmptyResponse;
497
498    const ENDPOINT: &'static str = "grant-dataset";
499    const SCOPE: &'static str = "dataset:grant";
500}
501
502#[derive(Debug, Serialize, Deserialize)]
506pub struct RevokeDatasetRequest {
507    pub client_id: Uuid,
508    pub dataset_id: Uuid,
509}
510
511impl ViturRequest for RevokeDatasetRequest {
512    type Response = EmptyResponse;
513
514    const ENDPOINT: &'static str = "revoke-dataset";
515    const SCOPE: &'static str = "dataset:revoke";
516}
517
518#[derive(Debug, Serialize, Deserialize)]
527pub struct LoadDatasetRequest {
528    pub client_id: Uuid,
529    pub dataset_id: Option<Uuid>,
530}
531
532impl ViturRequest for LoadDatasetRequest {
533    type Response = LoadDatasetResponse;
534
535    const ENDPOINT: &'static str = "load-dataset";
536    const SCOPE: &'static str = "data_key:retrieve";
540}
541
542#[derive(Debug, Serialize, Deserialize)]
546pub struct LoadDatasetResponse {
547    pub partial_index_key: RetrievedKey,
548    pub dataset: Dataset,
549}
550
551impl ViturResponse for LoadDatasetResponse {}