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 {}