1mod base64_array;
2mod base64_vec;
3mod error;
4mod identified_by;
5
6use cts_common::claims::{
7 ClientPermission, DataKeyPermission, KeysetPermission, Permission, Scope,
8};
9pub use identified_by::*;
10
11mod unverified_context;
12
13use async_trait::async_trait;
14use serde::{Deserialize, Serialize};
15use std::{
16 borrow::Cow,
17 fmt::{self, Debug, Display, Formatter},
18 ops::Deref,
19};
20use uuid::Uuid;
21use zeroize::{Zeroize, ZeroizeOnDrop};
22
23pub use cipherstash_config;
24pub use error::*;
26
27pub use crate::unverified_context::{UnverifiedContext, UnverifiedContextValue};
28pub use crate::{IdentifiedBy, Name};
29pub mod testing;
30
31#[async_trait]
32pub trait ViturConnection {
33 async fn send<Request: ViturRequest>(
34 &self,
35 request: Request,
36 access_token: &str,
37 ) -> Result<Request::Response, ViturRequestError>;
38}
39
40pub trait ViturResponse: Serialize + for<'de> Deserialize<'de> + Send {}
41
42#[async_trait]
43pub trait ViturRequest: Serialize + for<'de> Deserialize<'de> + Sized + Send {
44 type Response: ViturResponse;
45
46 const SCOPE: Scope;
47 const ENDPOINT: &'static str;
48}
49
50#[derive(Debug, Serialize, Deserialize)]
54pub struct CreateKeysetRequest<'a> {
55 pub name: Cow<'a, str>,
56 pub description: Cow<'a, str>,
57}
58
59impl ViturRequest for CreateKeysetRequest<'_> {
60 type Response = Keyset;
61
62 const ENDPOINT: &'static str = "create-keyset";
63 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Create));
64}
65
66#[derive(Default, Debug, Serialize, Deserialize)]
71pub struct ListKeysetRequest {
72 #[serde(default)]
73 pub show_disabled: bool,
74}
75
76impl ViturRequest for ListKeysetRequest {
77 type Response = Vec<Keyset>;
78
79 const ENDPOINT: &'static str = "list-keysets";
80 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::List));
81}
82
83#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
86pub struct Keyset {
87 pub id: Uuid,
88 pub name: String,
89 pub description: String,
90 pub is_disabled: bool,
91}
92
93impl ViturResponse for Keyset {}
94impl ViturResponse for Vec<Keyset> {}
95
96#[derive(Default, Debug, Serialize, Deserialize)]
98pub struct EmptyResponse {}
99
100impl ViturResponse for EmptyResponse {}
101
102#[derive(Debug, Serialize, Deserialize)]
107pub struct CreateClientRequest<'a> {
108 #[serde(alias = "dataset_id")]
109 pub keyset_id: IdentifiedBy,
110 pub name: Cow<'a, str>,
111 pub description: Cow<'a, str>,
112}
113
114impl ViturRequest for CreateClientRequest<'_> {
115 type Response = CreateClientResponse;
116
117 const ENDPOINT: &'static str = "create-client";
118 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::Create));
119}
120
121#[derive(Debug, Serialize, Deserialize)]
126pub struct CreateClientResponse {
127 pub id: Uuid,
128 #[serde(rename = "dataset_id")]
129 pub keyset_id: Uuid,
130 pub name: String,
131 pub description: String,
132 pub client_key: ViturKeyMaterial,
133}
134
135impl ViturResponse for CreateClientResponse {}
136
137#[derive(Debug, Serialize, Deserialize)]
142pub struct ListClientRequest;
143
144impl ViturRequest for ListClientRequest {
145 type Response = Vec<KeysetClient>;
146
147 const ENDPOINT: &'static str = "list-clients";
148 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::List));
149}
150
151#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
154#[serde(untagged)]
155pub enum ClientKeysetId {
156 Single(Uuid),
157 Multiple(Vec<Uuid>),
158}
159
160impl PartialEq<Uuid> for ClientKeysetId {
162 fn eq(&self, other: &Uuid) -> bool {
163 if let ClientKeysetId::Single(id) = self {
164 id == other
165 } else {
166 false
167 }
168 }
169}
170
171#[derive(Debug, Serialize, Deserialize)]
173pub struct KeysetClient {
174 pub id: Uuid,
175 #[serde(alias = "dataset_id")]
176 pub keyset_id: ClientKeysetId,
177 pub name: String,
178 pub description: String,
179}
180
181impl ViturResponse for Vec<KeysetClient> {}
182
183#[derive(Debug, Serialize, Deserialize)]
188pub struct DeleteClientRequest {
189 pub client_id: Uuid,
190}
191
192impl ViturRequest for DeleteClientRequest {
193 type Response = DeleteClientResponse;
194
195 const ENDPOINT: &'static str = "delete-client";
196 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::Delete));
197}
198
199#[derive(Default, Debug, Serialize, Deserialize)]
200pub struct DeleteClientResponse {}
201
202impl ViturResponse for DeleteClientResponse {}
203
204#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
206pub struct ViturKeyMaterial(#[serde(with = "base64_vec")] Vec<u8>);
207opaque_debug::implement!(ViturKeyMaterial);
208
209impl From<Vec<u8>> for ViturKeyMaterial {
210 fn from(inner: Vec<u8>) -> Self {
211 Self(inner)
212 }
213}
214
215impl Deref for ViturKeyMaterial {
216 type Target = [u8];
217
218 fn deref(&self) -> &Self::Target {
219 &self.0
220 }
221}
222
223#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Zeroize)]
224#[serde(transparent)]
225pub struct KeyId(#[serde(with = "base64_array")] [u8; 16]);
226
227impl KeyId {
228 pub fn into_inner(self) -> [u8; 16] {
229 self.0
230 }
231}
232
233impl Display for KeyId {
234 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
235 write!(f, "{}", const_hex::encode(self.0))
236 }
237}
238
239impl From<[u8; 16]> for KeyId {
240 fn from(inner: [u8; 16]) -> Self {
241 Self(inner)
242 }
243}
244
245impl AsRef<[u8; 16]> for KeyId {
246 fn as_ref(&self) -> &[u8; 16] {
247 &self.0
248 }
249}
250
251#[derive(Debug, Serialize, Deserialize)]
255pub struct GeneratedKey {
256 pub key_material: ViturKeyMaterial,
257 #[serde(with = "base64_vec")]
259 pub tag: Vec<u8>,
260}
261
262#[derive(Debug, Serialize, Deserialize)]
264pub struct GenerateKeyResponse {
265 pub keys: Vec<GeneratedKey>,
266}
267
268impl ViturResponse for GenerateKeyResponse {}
269
270#[derive(Debug, Serialize, Deserialize, Clone)]
272pub struct GenerateKeySpec<'a> {
273 #[serde(alias = "id")]
275 pub iv: KeyId,
276 pub descriptor: Cow<'a, str>,
278
279 #[serde(default)]
280 pub context: Cow<'a, [Context]>,
281}
282
283impl<'a> GenerateKeySpec<'a> {
284 pub fn new(iv: [u8; 16], descriptor: &'a str) -> Self {
285 Self {
286 iv: KeyId(iv),
287 descriptor: Cow::from(descriptor),
288 context: Default::default(),
289 }
290 }
291
292 pub fn new_with_context(
293 iv: [u8; 16],
294 descriptor: &'a str,
295 context: Cow<'a, [Context]>,
296 ) -> Self {
297 Self {
298 iv: KeyId(iv),
299 descriptor: Cow::from(descriptor),
300 context,
301 }
302 }
303}
304#[derive(Debug, Serialize, Deserialize, Clone)]
308pub enum Context {
310 Tag(String),
312
313 Value(String, String),
316
317 #[serde(alias = "identityClaim")]
322 IdentityClaim(String),
323}
324
325impl Context {
326 pub fn new_tag(tag: impl Into<String>) -> Self {
327 Self::Tag(tag.into())
328 }
329
330 pub fn new_value(key: impl Into<String>, value: impl Into<String>) -> Self {
331 Self::Value(key.into(), value.into())
332 }
333
334 pub fn new_identity_claim(claim: &str) -> Self {
335 Self::IdentityClaim(claim.to_string())
336 }
337}
338
339#[derive(Debug, Serialize, Deserialize)]
347pub struct GenerateKeyRequest<'a> {
348 pub client_id: Uuid,
349 #[serde(alias = "dataset_id")]
350 pub keyset_id: Option<IdentifiedBy>,
351 pub keys: Cow<'a, [GenerateKeySpec<'a>]>,
352 #[serde(default)]
353 pub unverified_context: Cow<'a, UnverifiedContext>,
354}
355
356impl ViturRequest for GenerateKeyRequest<'_> {
357 type Response = GenerateKeyResponse;
358
359 const ENDPOINT: &'static str = "generate-data-key";
360 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Generate));
361}
362
363#[derive(Debug, Serialize, Deserialize)]
365pub struct RetrievedKey {
366 pub key_material: ViturKeyMaterial,
367}
368
369#[derive(Debug, Serialize, Deserialize)]
372pub struct RetrieveKeyResponse {
373 pub keys: Vec<RetrievedKey>,
374}
375
376impl ViturResponse for RetrieveKeyResponse {}
377
378#[derive(Debug, Serialize, Deserialize, Clone)]
380pub struct RetrieveKeySpec<'a> {
381 #[serde(alias = "id")]
382 pub iv: KeyId,
383 pub descriptor: Cow<'a, str>,
385 pub tag: Cow<'a, [u8]>,
386
387 #[serde(default)]
388 pub context: Cow<'a, [Context]>,
389
390 #[serde(default)]
393 pub tag_version: usize,
394}
395
396impl<'a> RetrieveKeySpec<'a> {
397 const DEFAULT_TAG_VERSION: usize = 0;
398
399 pub fn new(id: KeyId, tag: &'a [u8], descriptor: &'a str) -> Self {
400 Self {
401 iv: id,
402 descriptor: Cow::from(descriptor),
403 tag: Cow::from(tag),
404 context: Cow::Owned(Vec::new()),
405 tag_version: Self::DEFAULT_TAG_VERSION,
406 }
407 }
408
409 pub fn with_context(mut self, context: Cow<'a, [Context]>) -> Self {
410 self.context = context;
411 self
412 }
413}
414
415#[derive(Debug, Serialize, Deserialize)]
421pub struct RetrieveKeyRequest<'a> {
422 pub client_id: Uuid,
423 #[serde(alias = "dataset_id")]
424 pub keyset_id: Option<IdentifiedBy>,
425 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
426 #[serde(default)]
427 pub unverified_context: UnverifiedContext,
428}
429
430impl ViturRequest for RetrieveKeyRequest<'_> {
431 type Response = RetrieveKeyResponse;
432
433 const ENDPOINT: &'static str = "retrieve-data-key";
434 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
435}
436
437#[derive(Debug, Serialize, Deserialize)]
443pub struct RetrieveKeyRequestFallible<'a> {
444 pub client_id: Uuid,
445 #[serde(alias = "dataset_id")]
446 pub keyset_id: Option<IdentifiedBy>,
447 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
448 #[serde(default)]
449 pub unverified_context: Cow<'a, UnverifiedContext>,
450}
451
452impl ViturRequest for RetrieveKeyRequestFallible<'_> {
453 type Response = RetrieveKeyResponseFallible;
454
455 const ENDPOINT: &'static str = "retrieve-data-key-fallible";
456 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
457}
458
459#[derive(Debug, Serialize, Deserialize)]
461pub struct RetrieveKeyResponseFallible {
462 pub keys: Vec<Result<RetrievedKey, String>>, }
464
465impl ViturResponse for RetrieveKeyResponseFallible {}
466
467#[derive(Debug, Serialize, Deserialize)]
471pub struct DisableKeysetRequest {
472 #[serde(alias = "dataset_id")]
473 pub keyset_id: IdentifiedBy,
474}
475
476impl ViturRequest for DisableKeysetRequest {
477 type Response = EmptyResponse;
478
479 const ENDPOINT: &'static str = "disable-keyset";
480 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Disable));
481}
482
483#[derive(Debug, Serialize, Deserialize)]
487pub struct EnableKeysetRequest {
488 #[serde(alias = "dataset_id")]
489 pub keyset_id: IdentifiedBy,
490}
491
492impl ViturRequest for EnableKeysetRequest {
493 type Response = EmptyResponse;
494
495 const ENDPOINT: &'static str = "enable-keyset";
496 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Enable));
497}
498
499#[derive(Debug, Serialize, Deserialize)]
505pub struct ModifyKeysetRequest<'a> {
506 #[serde(alias = "dataset_id")]
507 pub keyset_id: IdentifiedBy,
508
509 pub name: Option<Cow<'a, str>>,
510 pub description: Option<Cow<'a, str>>,
511}
512
513impl ViturRequest for ModifyKeysetRequest<'_> {
514 type Response = EmptyResponse;
515
516 const ENDPOINT: &'static str = "modify-keyset";
517 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Modify));
518}
519
520#[derive(Debug, Serialize, Deserialize)]
525pub struct GrantKeysetRequest {
526 pub client_id: Uuid,
527 #[serde(alias = "dataset_id")]
528 pub keyset_id: IdentifiedBy,
529}
530
531impl ViturRequest for GrantKeysetRequest {
532 type Response = EmptyResponse;
533
534 const ENDPOINT: &'static str = "grant-keyset";
535 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Grant));
536}
537
538#[derive(Debug, Serialize, Deserialize)]
542pub struct RevokeKeysetRequest {
543 pub client_id: Uuid,
544 #[serde(alias = "dataset_id")]
545 pub keyset_id: IdentifiedBy,
546}
547
548impl ViturRequest for RevokeKeysetRequest {
549 type Response = EmptyResponse;
550
551 const ENDPOINT: &'static str = "revoke-keyset";
552 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Revoke));
553}
554
555#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd)]
564pub struct LoadKeysetRequest {
565 pub client_id: Uuid,
566 #[serde(alias = "dataset_id")]
567 pub keyset_id: Option<IdentifiedBy>,
568}
569
570impl ViturRequest for LoadKeysetRequest {
571 type Response = LoadKeysetResponse;
572
573 const ENDPOINT: &'static str = "load-keyset";
574
575 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
579}
580
581#[derive(Debug, Serialize, Deserialize)]
585pub struct LoadKeysetResponse {
586 pub partial_index_key: RetrievedKey,
587 #[serde(rename = "dataset")]
588 pub keyset: Keyset,
589}
590
591impl ViturResponse for LoadKeysetResponse {}
592
593#[cfg(test)]
594mod test {
595 use serde_json::json;
596 use uuid::Uuid;
597
598 use crate::{IdentifiedBy, LoadKeysetRequest, Name};
599
600 mod backwards_compatible_deserialisation {
601 use super::*;
602
603 #[test]
604 fn when_dataset_id_is_uuid() {
605 let client_id = Uuid::new_v4();
606 let dataset_id = Uuid::new_v4();
607
608 let json = json!({
609 "client_id": client_id,
610 "dataset_id": dataset_id,
611 });
612
613 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
614
615 assert_eq!(
616 req,
617 LoadKeysetRequest {
618 client_id,
619 keyset_id: Some(IdentifiedBy::Uuid(dataset_id))
620 }
621 );
622 }
623
624 #[test]
625 fn when_keyset_id_is_uuid() {
626 let client_id = Uuid::new_v4();
627 let keyset_id = Uuid::new_v4();
628
629 let json = json!({
630 "client_id": client_id,
631 "keyset_id": keyset_id,
632 });
633
634 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
635
636 assert_eq!(
637 req,
638 LoadKeysetRequest {
639 client_id,
640 keyset_id: Some(IdentifiedBy::Uuid(keyset_id))
641 }
642 );
643 }
644
645 #[test]
646 fn when_dataset_id_is_id_name() {
647 let client_id = Uuid::new_v4();
648 let dataset_id = IdentifiedBy::Name(Name::new_untrusted("some-dataset-name"));
649
650 let json = json!({
651 "client_id": client_id,
652 "dataset_id": dataset_id,
653 });
654
655 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
656
657 assert_eq!(
658 req,
659 LoadKeysetRequest {
660 client_id,
661 keyset_id: Some(dataset_id)
662 }
663 );
664 }
665 }
666}