1mod base64_array;
2mod base64_vec;
3mod error;
4mod identified_by;
5
6pub use identified_by::*;
7
8mod unverified_context;
9
10use async_trait::async_trait;
11use serde::{Deserialize, Serialize};
12use std::{
13 borrow::Cow,
14 fmt::{self, Debug, Display, Formatter},
15 ops::Deref,
16};
17use uuid::Uuid;
18use zeroize::{Zeroize, ZeroizeOnDrop};
19
20pub use cipherstash_config;
21pub use error::*;
23
24pub use crate::unverified_context::{UnverifiedContext, UnverifiedContextValue};
25pub use crate::{IdentifiedBy, Name};
26pub mod testing;
27
28#[async_trait]
29pub trait ViturConnection {
30 async fn send<Request: ViturRequest>(
31 &self,
32 request: Request,
33 access_token: &str,
34 ) -> Result<Request::Response, ViturRequestError>;
35}
36
37pub trait ViturResponse: Serialize + for<'de> Deserialize<'de> + Send {}
38
39#[async_trait]
40pub trait ViturRequest: Serialize + for<'de> Deserialize<'de> + Sized + Send {
41 type Response: ViturResponse;
42
43 const SCOPE: &'static str;
44 const ENDPOINT: &'static str;
45}
46
47#[derive(Debug, Serialize, Deserialize)]
51pub struct CreateKeysetRequest<'a> {
52 pub name: Cow<'a, str>,
53 pub description: Cow<'a, str>,
54}
55
56impl ViturRequest for CreateKeysetRequest<'_> {
57 type Response = Keyset;
58
59 const ENDPOINT: &'static str = "create-keyset";
60 const SCOPE: &'static str = "dataset:create";
61}
62
63#[derive(Default, Debug, Serialize, Deserialize)]
68pub struct ListKeysetRequest {
69 #[serde(default)]
70 pub show_disabled: bool,
71}
72
73impl ViturRequest for ListKeysetRequest {
74 type Response = Vec<Keyset>;
75
76 const ENDPOINT: &'static str = "list-keysets";
77 const SCOPE: &'static str = "dataset:list";
78}
79
80#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
83pub struct Keyset {
84 pub id: Uuid,
85 pub name: String,
86 pub description: String,
87 pub is_disabled: bool,
88}
89
90impl ViturResponse for Keyset {}
91impl ViturResponse for Vec<Keyset> {}
92
93#[derive(Default, Debug, Serialize, Deserialize)]
95pub struct EmptyResponse {}
96
97impl ViturResponse for EmptyResponse {}
98
99#[derive(Debug, Serialize, Deserialize)]
104pub struct CreateClientRequest<'a> {
105 #[serde(alias = "dataset_id")]
106 pub keyset_id: IdentifiedBy,
107 pub name: Cow<'a, str>,
108 pub description: Cow<'a, str>,
109}
110
111impl ViturRequest for CreateClientRequest<'_> {
112 type Response = CreateClientResponse;
113
114 const ENDPOINT: &'static str = "create-client";
115 const SCOPE: &'static str = "client:create";
116}
117
118#[derive(Debug, Serialize, Deserialize)]
123pub struct CreateClientResponse {
124 pub id: Uuid,
125 #[serde(rename = "dataset_id")]
126 pub keyset_id: Uuid,
127 pub name: String,
128 pub description: String,
129 pub client_key: ViturKeyMaterial,
130}
131
132impl ViturResponse for CreateClientResponse {}
133
134#[derive(Debug, Serialize, Deserialize)]
139pub struct ListClientRequest;
140
141impl ViturRequest for ListClientRequest {
142 type Response = Vec<KeysetClient>;
143
144 const ENDPOINT: &'static str = "list-clients";
145 const SCOPE: &'static str = "client:list";
146}
147
148#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
151#[serde(untagged)]
152pub enum ClientKeysetId {
153 Single(Uuid),
154 Multiple(Vec<Uuid>),
155}
156
157impl PartialEq<Uuid> for ClientKeysetId {
159 fn eq(&self, other: &Uuid) -> bool {
160 if let ClientKeysetId::Single(id) = self {
161 id == other
162 } else {
163 false
164 }
165 }
166}
167
168#[derive(Debug, Serialize, Deserialize)]
170pub struct KeysetClient {
171 pub id: Uuid,
172 #[serde(alias = "dataset_id")]
173 pub keyset_id: ClientKeysetId,
174 pub name: String,
175 pub description: String,
176}
177
178impl ViturResponse for Vec<KeysetClient> {}
179
180#[derive(Debug, Serialize, Deserialize)]
185pub struct DeleteClientRequest {
186 pub client_id: Uuid,
187}
188
189impl ViturRequest for DeleteClientRequest {
190 type Response = DeleteClientResponse;
191
192 const ENDPOINT: &'static str = "delete-client";
193 const SCOPE: &'static str = "client:delete";
194}
195
196#[derive(Default, Debug, Serialize, Deserialize)]
197pub struct DeleteClientResponse {}
198
199impl ViturResponse for DeleteClientResponse {}
200
201#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
203pub struct ViturKeyMaterial(#[serde(with = "base64_vec")] Vec<u8>);
204opaque_debug::implement!(ViturKeyMaterial);
205
206impl From<Vec<u8>> for ViturKeyMaterial {
207 fn from(inner: Vec<u8>) -> Self {
208 Self(inner)
209 }
210}
211
212impl Deref for ViturKeyMaterial {
213 type Target = [u8];
214
215 fn deref(&self) -> &Self::Target {
216 &self.0
217 }
218}
219
220#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Zeroize)]
221#[serde(transparent)]
222pub struct KeyId(#[serde(with = "base64_array")] [u8; 16]);
223
224impl KeyId {
225 pub fn into_inner(self) -> [u8; 16] {
226 self.0
227 }
228}
229
230impl Display for KeyId {
231 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
232 write!(f, "{}", const_hex::encode(self.0))
233 }
234}
235
236impl From<[u8; 16]> for KeyId {
237 fn from(inner: [u8; 16]) -> Self {
238 Self(inner)
239 }
240}
241
242impl AsRef<[u8; 16]> for KeyId {
243 fn as_ref(&self) -> &[u8; 16] {
244 &self.0
245 }
246}
247
248#[derive(Debug, Serialize, Deserialize)]
252pub struct GeneratedKey {
253 pub key_material: ViturKeyMaterial,
254 #[serde(with = "base64_vec")]
256 pub tag: Vec<u8>,
257}
258
259#[derive(Debug, Serialize, Deserialize)]
261pub struct GenerateKeyResponse {
262 pub keys: Vec<GeneratedKey>,
263}
264
265impl ViturResponse for GenerateKeyResponse {}
266
267#[derive(Debug, Serialize, Deserialize, Clone)]
269pub struct GenerateKeySpec<'a> {
270 #[serde(alias = "id")]
272 pub iv: KeyId,
273 pub descriptor: Cow<'a, str>,
275
276 #[serde(default = "Vec::new")]
277 pub context: Vec<Context>,
278}
279
280impl<'a> GenerateKeySpec<'a> {
281 pub fn new(iv: [u8; 16], descriptor: &'a str) -> Self {
282 Self {
283 iv: KeyId(iv),
284 descriptor: Cow::from(descriptor),
285 context: Vec::new(),
286 }
287 }
288
289 pub fn new_with_context(iv: [u8; 16], descriptor: &'a str, context: Vec<Context>) -> Self {
290 Self {
291 iv: KeyId(iv),
292 descriptor: Cow::from(descriptor),
293 context,
294 }
295 }
296}
297#[derive(Debug, Serialize, Deserialize, Clone)]
301pub enum Context {
303 Tag(String),
305
306 Value(String, String),
309
310 IdentityClaim(String),
315}
316
317impl Context {
318 pub fn new_tag(tag: impl Into<String>) -> Self {
319 Self::Tag(tag.into())
320 }
321
322 pub fn new_value(key: impl Into<String>, value: impl Into<String>) -> Self {
323 Self::Value(key.into(), value.into())
324 }
325
326 pub fn new_identity_claim(claim: &str) -> Self {
327 Self::IdentityClaim(claim.to_string())
328 }
329}
330
331#[derive(Debug, Serialize, Deserialize)]
339pub struct GenerateKeyRequest<'a> {
340 pub client_id: Uuid,
341 #[serde(alias = "dataset_id")]
342 pub keyset_id: Option<IdentifiedBy>,
343 pub keys: Cow<'a, [GenerateKeySpec<'a>]>,
344 #[serde(default)]
345 pub unverified_context: UnverifiedContext,
346}
347
348impl ViturRequest for GenerateKeyRequest<'_> {
349 type Response = GenerateKeyResponse;
350
351 const ENDPOINT: &'static str = "generate-data-key";
352 const SCOPE: &'static str = "data_key:generate";
353}
354
355#[derive(Debug, Serialize, Deserialize)]
357pub struct RetrievedKey {
358 pub key_material: ViturKeyMaterial,
359}
360
361#[derive(Debug, Serialize, Deserialize)]
364pub struct RetrieveKeyResponse {
365 pub keys: Vec<RetrievedKey>,
366}
367
368impl ViturResponse for RetrieveKeyResponse {}
369
370#[derive(Debug, Serialize, Deserialize, Clone)]
372pub struct RetrieveKeySpec<'a> {
373 #[serde(alias = "id")]
374 pub iv: KeyId,
375 pub descriptor: Cow<'a, str>,
377 pub tag: Cow<'a, [u8]>,
378
379 #[serde(default = "Vec::new")]
380 pub context: Vec<Context>,
381
382 #[serde(default)]
385 pub tag_version: usize,
386}
387
388impl<'a> RetrieveKeySpec<'a> {
389 const DEFAULT_TAG_VERSION: usize = 0;
390
391 pub fn new(id: KeyId, tag: &'a [u8], descriptor: &'a str) -> Self {
392 Self {
393 iv: id,
394 descriptor: Cow::from(descriptor),
395 tag: Cow::from(tag),
396 context: Vec::new(),
397 tag_version: Self::DEFAULT_TAG_VERSION,
398 }
399 }
400
401 pub fn with_context(mut self, context: Vec<Context>) -> Self {
402 self.context = context;
403 self
404 }
405}
406
407#[derive(Debug, Serialize, Deserialize)]
413pub struct RetrieveKeyRequest<'a> {
414 pub client_id: Uuid,
415 #[serde(alias = "dataset_id")]
416 pub keyset_id: Option<IdentifiedBy>,
417 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
418 #[serde(default)]
419 pub unverified_context: UnverifiedContext,
420}
421
422impl ViturRequest for RetrieveKeyRequest<'_> {
423 type Response = RetrieveKeyResponse;
424
425 const ENDPOINT: &'static str = "retrieve-data-key";
426 const SCOPE: &'static str = "data_key:retrieve";
427}
428
429#[derive(Debug, Serialize, Deserialize)]
435pub struct RetrieveKeyRequestFallible<'a> {
436 pub client_id: Uuid,
437 #[serde(alias = "dataset_id")]
438 pub keyset_id: Option<IdentifiedBy>,
439 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
440 #[serde(default)]
441 pub unverified_context: UnverifiedContext,
442}
443
444impl ViturRequest for RetrieveKeyRequestFallible<'_> {
445 type Response = RetrieveKeyResponseFallible;
446
447 const ENDPOINT: &'static str = "retrieve-data-key-fallible";
448 const SCOPE: &'static str = "data_key:retrieve";
449}
450
451#[derive(Debug, Serialize, Deserialize)]
453pub struct RetrieveKeyResponseFallible {
454 pub keys: Vec<Result<RetrievedKey, String>>, }
456
457impl ViturResponse for RetrieveKeyResponseFallible {}
458
459#[derive(Debug, Serialize, Deserialize)]
463pub struct DisableKeysetRequest {
464 #[serde(alias = "dataset_id")]
465 pub keyset_id: IdentifiedBy,
466}
467
468impl ViturRequest for DisableKeysetRequest {
469 type Response = EmptyResponse;
470
471 const ENDPOINT: &'static str = "disable-keyset";
472 const SCOPE: &'static str = "dataset:disable";
473}
474
475#[derive(Debug, Serialize, Deserialize)]
479pub struct EnableKeysetRequest {
480 #[serde(alias = "dataset_id")]
481 pub keyset_id: IdentifiedBy,
482}
483
484impl ViturRequest for EnableKeysetRequest {
485 type Response = EmptyResponse;
486
487 const ENDPOINT: &'static str = "enable-keyset";
488 const SCOPE: &'static str = "dataset:enable";
489}
490
491#[derive(Debug, Serialize, Deserialize)]
497pub struct ModifyKeysetRequest<'a> {
498 #[serde(alias = "dataset_id")]
499 pub keyset_id: IdentifiedBy,
500
501 pub name: Option<Cow<'a, str>>,
502 pub description: Option<Cow<'a, str>>,
503}
504
505impl ViturRequest for ModifyKeysetRequest<'_> {
506 type Response = EmptyResponse;
507
508 const ENDPOINT: &'static str = "modify-keyset";
509 const SCOPE: &'static str = "dataset:modify";
510}
511
512#[derive(Debug, Serialize, Deserialize)]
517pub struct GrantKeysetRequest {
518 pub client_id: Uuid,
519 #[serde(alias = "dataset_id")]
520 pub keyset_id: IdentifiedBy,
521}
522
523impl ViturRequest for GrantKeysetRequest {
524 type Response = EmptyResponse;
525
526 const ENDPOINT: &'static str = "grant-keyset";
527 const SCOPE: &'static str = "dataset:grant";
528}
529
530#[derive(Debug, Serialize, Deserialize)]
534pub struct RevokeKeysetRequest {
535 pub client_id: Uuid,
536 #[serde(alias = "dataset_id")]
537 pub keyset_id: IdentifiedBy,
538}
539
540impl ViturRequest for RevokeKeysetRequest {
541 type Response = EmptyResponse;
542
543 const ENDPOINT: &'static str = "revoke-keyset";
544 const SCOPE: &'static str = "dataset:revoke";
545}
546
547#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd)]
556pub struct LoadKeysetRequest {
557 pub client_id: Uuid,
558 #[serde(alias = "dataset_id")]
559 pub keyset_id: Option<IdentifiedBy>,
560}
561
562impl ViturRequest for LoadKeysetRequest {
563 type Response = LoadKeysetResponse;
564
565 const ENDPOINT: &'static str = "load-keyset";
566
567 const SCOPE: &'static str = "data_key:retrieve";
571}
572
573#[derive(Debug, Serialize, Deserialize)]
577pub struct LoadKeysetResponse {
578 pub partial_index_key: RetrievedKey,
579 #[serde(rename = "dataset")]
580 pub keyset: Keyset,
581}
582
583impl ViturResponse for LoadKeysetResponse {}
584
585#[cfg(test)]
586mod test {
587 use serde_json::json;
588 use uuid::Uuid;
589
590 use crate::{IdentifiedBy, LoadKeysetRequest, Name};
591
592 mod backwards_compatible_deserialisation {
593 use super::*;
594
595 #[test]
596 fn when_dataset_id_is_uuid() {
597 let client_id = Uuid::new_v4();
598 let dataset_id = Uuid::new_v4();
599
600 let json = json!({
601 "client_id": client_id,
602 "dataset_id": dataset_id,
603 });
604
605 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
606
607 assert_eq!(
608 req,
609 LoadKeysetRequest {
610 client_id,
611 keyset_id: Some(IdentifiedBy::Uuid(dataset_id))
612 }
613 );
614 }
615
616 #[test]
617 fn when_keyset_id_is_uuid() {
618 let client_id = Uuid::new_v4();
619 let keyset_id = Uuid::new_v4();
620
621 let json = json!({
622 "client_id": client_id,
623 "keyset_id": keyset_id,
624 });
625
626 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
627
628 assert_eq!(
629 req,
630 LoadKeysetRequest {
631 client_id,
632 keyset_id: Some(IdentifiedBy::Uuid(keyset_id))
633 }
634 );
635 }
636
637 #[test]
638 fn when_dataset_id_is_id_name() {
639 let client_id = Uuid::new_v4();
640 let dataset_id = IdentifiedBy::Name(Name(String::from("some-dataset-name")));
641
642 let json = json!({
643 "client_id": client_id,
644 "dataset_id": dataset_id,
645 });
646
647 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
648
649 assert_eq!(
650 req,
651 LoadKeysetRequest {
652 client_id,
653 keyset_id: Some(dataset_id)
654 }
655 );
656 }
657 }
658}