1mod base64_array;
2mod base64_vec;
3mod error;
4mod unverified_context;
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8use std::{borrow::Cow, fmt::Display, ops::Deref};
9use uuid::Uuid;
10use zeroize::{Zeroize, ZeroizeOnDrop};
11
12pub use cipherstash_config;
13pub use error::*;
15
16pub use crate::unverified_context::{UnverifiedContext, UnverifiedContextValue};
17pub mod testing;
18
19#[async_trait]
20pub trait ViturConnection {
21 async fn send<Request: ViturRequest>(
22 &self,
23 request: Request,
24 access_token: &str,
25 ) -> Result<Request::Response, ViturRequestError>;
26}
27
28pub trait ViturResponse: Serialize + for<'de> Deserialize<'de> + Send {}
29
30#[async_trait]
31pub trait ViturRequest: Serialize + for<'de> Deserialize<'de> + Sized + Send {
32 type Response: ViturResponse;
33
34 const SCOPE: &'static str;
35 const ENDPOINT: &'static str;
36}
37
38#[derive(Debug, Serialize, Deserialize)]
42pub struct CreateDatasetRequest<'a> {
43 pub name: Cow<'a, str>,
44 pub description: Cow<'a, str>,
45}
46
47impl ViturRequest for CreateDatasetRequest<'_> {
48 type Response = Dataset;
49
50 const ENDPOINT: &'static str = "create-dataset";
51 const SCOPE: &'static str = "dataset:create";
52}
53
54#[derive(Default, Debug, Serialize, Deserialize)]
59pub struct ListDatasetRequest {
60 #[serde(default)]
61 pub show_disabled: bool,
62}
63
64impl ViturRequest for ListDatasetRequest {
65 type Response = Vec<Dataset>;
66
67 const ENDPOINT: &'static str = "list-datasets";
68 const SCOPE: &'static str = "dataset:list";
69}
70
71#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
74pub struct Dataset {
75 pub id: Uuid,
76 pub name: String,
77 pub description: String,
78 pub is_disabled: bool,
79}
80
81impl ViturResponse for Dataset {}
82impl ViturResponse for Vec<Dataset> {}
83
84#[derive(Default, Debug, Serialize, Deserialize)]
86pub struct EmptyResponse {}
87
88impl ViturResponse for EmptyResponse {}
89
90#[derive(Debug, Serialize, Deserialize)]
95pub struct CreateClientRequest<'a> {
96 pub dataset_id: Uuid,
97 pub name: Cow<'a, str>,
98 pub description: Cow<'a, str>,
99}
100
101impl ViturRequest for CreateClientRequest<'_> {
102 type Response = CreateClientResponse;
103
104 const ENDPOINT: &'static str = "create-client";
105 const SCOPE: &'static str = "client:create";
106}
107
108#[derive(Debug, Serialize, Deserialize)]
113pub struct CreateClientResponse {
114 pub id: Uuid,
115 pub dataset_id: Uuid,
116 pub name: String,
117 pub description: String,
118 pub client_key: ViturKeyMaterial,
119}
120
121impl ViturResponse for CreateClientResponse {}
122
123#[derive(Debug, Serialize, Deserialize)]
128pub struct ListClientRequest;
129
130impl ViturRequest for ListClientRequest {
131 type Response = Vec<DatasetClient>;
132
133 const ENDPOINT: &'static str = "list-clients";
134 const SCOPE: &'static str = "client:list";
135}
136
137#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
140#[serde(untagged)]
141pub enum ClientDatasetId {
142 Single(Uuid),
143 Multiple(Vec<Uuid>),
144}
145
146impl PartialEq<Uuid> for ClientDatasetId {
148 fn eq(&self, other: &Uuid) -> bool {
149 if let ClientDatasetId::Single(id) = self {
150 id == other
151 } else {
152 false
153 }
154 }
155}
156
157#[derive(Debug, Serialize, Deserialize)]
159pub struct DatasetClient {
160 pub id: Uuid,
161 pub dataset_id: ClientDatasetId,
162 pub name: String,
163 pub description: String,
164}
165
166impl ViturResponse for Vec<DatasetClient> {}
167
168#[derive(Debug, Serialize, Deserialize)]
173pub struct DeleteClientRequest {
174 pub client_id: Uuid,
175}
176
177impl ViturRequest for DeleteClientRequest {
178 type Response = DeleteClientResponse;
179
180 const ENDPOINT: &'static str = "delete-client";
181 const SCOPE: &'static str = "client:delete";
182}
183
184#[derive(Default, Debug, Serialize, Deserialize)]
185pub struct DeleteClientResponse {}
186
187impl ViturResponse for DeleteClientResponse {}
188
189#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
191pub struct ViturKeyMaterial(#[serde(with = "base64_vec")] Vec<u8>);
192opaque_debug::implement!(ViturKeyMaterial);
193
194impl From<Vec<u8>> for ViturKeyMaterial {
195 fn from(inner: Vec<u8>) -> Self {
196 Self(inner)
197 }
198}
199
200impl Deref for ViturKeyMaterial {
201 type Target = [u8];
202
203 fn deref(&self) -> &Self::Target {
204 &self.0
205 }
206}
207
208#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Zeroize)]
209#[serde(transparent)]
210pub struct KeyId(#[serde(with = "base64_array")] [u8; 16]);
211
212impl KeyId {
213 pub fn into_inner(self) -> [u8; 16] {
214 self.0
215 }
216}
217
218impl Display for KeyId {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 write!(f, "{}", const_hex::encode(self.0))
221 }
222}
223
224impl From<[u8; 16]> for KeyId {
225 fn from(inner: [u8; 16]) -> Self {
226 Self(inner)
227 }
228}
229
230impl AsRef<[u8; 16]> for KeyId {
231 fn as_ref(&self) -> &[u8; 16] {
232 &self.0
233 }
234}
235
236#[derive(Debug, Serialize, Deserialize)]
240pub struct GeneratedKey {
241 pub key_material: ViturKeyMaterial,
242 #[serde(with = "base64_vec")]
244 pub tag: Vec<u8>,
245}
246
247#[derive(Debug, Serialize, Deserialize)]
249pub struct GenerateKeyResponse {
250 pub keys: Vec<GeneratedKey>,
251}
252
253impl ViturResponse for GenerateKeyResponse {}
254
255#[derive(Debug, Serialize, Deserialize, Clone)]
257pub struct GenerateKeySpec<'a> {
258 #[serde(alias = "id")]
260 pub iv: KeyId,
261 pub descriptor: Cow<'a, str>,
263
264 #[serde(default = "Vec::new")]
265 pub context: Vec<Context>,
266}
267
268impl<'a> GenerateKeySpec<'a> {
269 pub fn new(iv: [u8; 16], descriptor: &'a str) -> Self {
270 Self {
271 iv: KeyId(iv),
272 descriptor: Cow::from(descriptor),
273 context: Vec::new(),
274 }
275 }
276
277 pub fn new_with_context(iv: [u8; 16], descriptor: &'a str, context: Vec<Context>) -> Self {
278 Self {
279 iv: KeyId(iv),
280 descriptor: Cow::from(descriptor),
281 context,
282 }
283 }
284}
285#[derive(Debug, Serialize, Deserialize, Clone)]
289pub enum Context {
291 Tag(String),
293
294 Value(String, String),
297
298 IdentityClaim(String),
303}
304
305impl Context {
306 pub fn new_tag(tag: impl Into<String>) -> Self {
307 Self::Tag(tag.into())
308 }
309
310 pub fn new_value(key: impl Into<String>, value: impl Into<String>) -> Self {
311 Self::Value(key.into(), value.into())
312 }
313
314 pub fn new_identity_claim(claim: &str) -> Self {
315 Self::IdentityClaim(claim.to_string())
316 }
317}
318
319#[derive(Debug, Serialize, Deserialize)]
327pub struct GenerateKeyRequest<'a> {
328 pub client_id: Uuid,
329 pub dataset_id: Option<Uuid>,
330 pub keys: Cow<'a, [GenerateKeySpec<'a>]>,
331 #[serde(default)]
332 pub unverified_context: UnverifiedContext,
333}
334
335impl ViturRequest for GenerateKeyRequest<'_> {
336 type Response = GenerateKeyResponse;
337
338 const ENDPOINT: &'static str = "generate-data-key";
339 const SCOPE: &'static str = "data_key:generate";
340}
341
342#[derive(Debug, Serialize, Deserialize)]
344pub struct RetrievedKey {
345 pub key_material: ViturKeyMaterial,
346}
347
348#[derive(Debug, Serialize, Deserialize)]
351pub struct RetrieveKeyResponse {
352 pub keys: Vec<RetrievedKey>,
353}
354
355impl ViturResponse for RetrieveKeyResponse {}
356
357#[derive(Debug, Serialize, Deserialize, Clone)]
359pub struct RetrieveKeySpec<'a> {
360 #[serde(alias = "id")]
361 pub iv: KeyId,
362 pub descriptor: Cow<'a, str>,
364 pub tag: Cow<'a, [u8]>,
365
366 #[serde(default = "Vec::new")]
367 pub context: Vec<Context>,
368
369 #[serde(default)]
372 pub tag_version: usize,
373}
374
375impl<'a> RetrieveKeySpec<'a> {
376 const DEFAULT_TAG_VERSION: usize = 0;
377
378 pub fn new(id: KeyId, tag: &'a [u8], descriptor: &'a str) -> Self {
379 Self {
380 iv: id,
381 descriptor: Cow::from(descriptor),
382 tag: Cow::from(tag),
383 context: Vec::new(),
384 tag_version: Self::DEFAULT_TAG_VERSION,
385 }
386 }
387
388 pub fn with_context(mut self, context: Vec<Context>) -> Self {
389 self.context = context;
390 self
391 }
392}
393
394#[derive(Debug, Serialize, Deserialize)]
400pub struct RetrieveKeyRequest<'a> {
401 pub client_id: Uuid,
402 pub dataset_id: Option<Uuid>,
403 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
404 #[serde(default)]
405 pub unverified_context: UnverifiedContext,
406}
407
408impl ViturRequest for RetrieveKeyRequest<'_> {
409 type Response = RetrieveKeyResponse;
410
411 const ENDPOINT: &'static str = "retrieve-data-key";
412 const SCOPE: &'static str = "data_key:retrieve";
413}
414
415#[derive(Debug, Serialize, Deserialize)]
421pub struct RetrieveKeyRequestFallible<'a> {
422 pub client_id: Uuid,
423 pub dataset_id: Option<Uuid>,
424 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
425 #[serde(default)]
426 pub unverified_context: UnverifiedContext,
427}
428
429impl ViturRequest for RetrieveKeyRequestFallible<'_> {
430 type Response = RetrieveKeyResponseFallible;
431
432 const ENDPOINT: &'static str = "retrieve-data-key-fallible";
433 const SCOPE: &'static str = "data_key:retrieve";
434}
435
436#[derive(Debug, Serialize, Deserialize)]
438pub struct RetrieveKeyResponseFallible {
439 pub keys: Vec<Result<RetrievedKey, String>>, }
441
442impl ViturResponse for RetrieveKeyResponseFallible {}
443
444#[derive(Debug, Serialize, Deserialize)]
448pub struct DisableDatasetRequest {
449 pub dataset_id: Uuid,
450}
451
452impl ViturRequest for DisableDatasetRequest {
453 type Response = EmptyResponse;
454
455 const ENDPOINT: &'static str = "disable-dataset";
456 const SCOPE: &'static str = "dataset:disable";
457}
458
459#[derive(Debug, Serialize, Deserialize)]
463pub struct EnableDatasetRequest {
464 pub dataset_id: Uuid,
465}
466
467impl ViturRequest for EnableDatasetRequest {
468 type Response = EmptyResponse;
469
470 const ENDPOINT: &'static str = "enable-dataset";
471 const SCOPE: &'static str = "dataset:enable";
472}
473
474#[derive(Debug, Serialize, Deserialize)]
480pub struct ModifyDatasetRequest<'a> {
481 pub dataset_id: Uuid,
482
483 pub name: Option<Cow<'a, str>>,
484 pub description: Option<Cow<'a, str>>,
485}
486
487impl ViturRequest for ModifyDatasetRequest<'_> {
488 type Response = EmptyResponse;
489
490 const ENDPOINT: &'static str = "modify-dataset";
491 const SCOPE: &'static str = "dataset:modify";
492}
493
494#[derive(Debug, Serialize, Deserialize)]
499pub struct GrantDatasetRequest {
500 pub client_id: Uuid,
501 pub dataset_id: Uuid,
502}
503
504impl ViturRequest for GrantDatasetRequest {
505 type Response = EmptyResponse;
506
507 const ENDPOINT: &'static str = "grant-dataset";
508 const SCOPE: &'static str = "dataset:grant";
509}
510
511#[derive(Debug, Serialize, Deserialize)]
515pub struct RevokeDatasetRequest {
516 pub client_id: Uuid,
517 pub dataset_id: Uuid,
518}
519
520impl ViturRequest for RevokeDatasetRequest {
521 type Response = EmptyResponse;
522
523 const ENDPOINT: &'static str = "revoke-dataset";
524 const SCOPE: &'static str = "dataset:revoke";
525}
526
527#[derive(Debug, Serialize, Deserialize)]
536pub struct LoadDatasetRequest {
537 pub client_id: Uuid,
538 pub dataset_id: Option<Uuid>,
539}
540
541impl ViturRequest for LoadDatasetRequest {
542 type Response = LoadDatasetResponse;
543
544 const ENDPOINT: &'static str = "load-dataset";
545 const SCOPE: &'static str = "data_key:retrieve";
549}
550
551#[derive(Debug, Serialize, Deserialize)]
555pub struct LoadDatasetResponse {
556 pub partial_index_key: RetrievedKey,
557 pub dataset: Dataset,
558}
559
560impl ViturResponse for LoadDatasetResponse {}