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 serde::{Deserialize, Serialize};
14use std::{
15 borrow::Cow,
16 fmt::{self, Debug, Display, Formatter},
17 ops::Deref,
18};
19use uuid::Uuid;
20use zeroize::{Zeroize, ZeroizeOnDrop};
21
22pub use cipherstash_config;
23pub use error::*;
25
26pub use crate::unverified_context::{UnverifiedContext, UnverifiedContextValue};
27pub use crate::{IdentifiedBy, Name};
28pub mod testing;
29
30pub trait ViturResponse: Serialize + for<'de> Deserialize<'de> + Send {}
31
32pub trait ViturRequest: Serialize + for<'de> Deserialize<'de> + Sized + Send {
33 type Response: ViturResponse;
34
35 const SCOPE: Scope;
36 const ENDPOINT: &'static str;
37}
38
39#[derive(Debug, Serialize, Deserialize)]
43pub struct CreateKeysetRequest<'a> {
44 pub name: Cow<'a, str>,
45 pub description: Cow<'a, str>,
46}
47
48impl ViturRequest for CreateKeysetRequest<'_> {
49 type Response = Keyset;
50
51 const ENDPOINT: &'static str = "create-keyset";
52 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Create));
53}
54
55#[derive(Default, Debug, Serialize, Deserialize)]
60pub struct ListKeysetRequest {
61 #[serde(default)]
62 pub show_disabled: bool,
63}
64
65impl ViturRequest for ListKeysetRequest {
66 type Response = Vec<Keyset>;
67
68 const ENDPOINT: &'static str = "list-keysets";
69 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::List));
70}
71
72#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
75pub struct Keyset {
76 pub id: Uuid,
77 pub name: String,
78 pub description: String,
79 pub is_disabled: bool,
80}
81
82impl ViturResponse for Keyset {}
83impl ViturResponse for Vec<Keyset> {}
84
85#[derive(Default, Debug, Serialize, Deserialize)]
87pub struct EmptyResponse {}
88
89impl ViturResponse for EmptyResponse {}
90
91#[derive(Debug, Serialize, Deserialize)]
96pub struct CreateClientRequest<'a> {
97 #[serde(alias = "dataset_id")]
98 pub keyset_id: IdentifiedBy,
99 pub name: Cow<'a, str>,
100 pub description: Cow<'a, str>,
101}
102
103impl ViturRequest for CreateClientRequest<'_> {
104 type Response = CreateClientResponse;
105
106 const ENDPOINT: &'static str = "create-client";
107 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::Create));
108}
109
110#[derive(Debug, Serialize, Deserialize)]
115pub struct CreateClientResponse {
116 pub id: Uuid,
117 #[serde(rename = "dataset_id")]
118 pub keyset_id: Uuid,
119 pub name: String,
120 pub description: String,
121 pub client_key: ViturKeyMaterial,
122}
123
124impl ViturResponse for CreateClientResponse {}
125
126#[derive(Debug, Serialize, Deserialize)]
131pub struct ListClientRequest;
132
133impl ViturRequest for ListClientRequest {
134 type Response = Vec<KeysetClient>;
135
136 const ENDPOINT: &'static str = "list-clients";
137 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::List));
138}
139
140#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
143#[serde(untagged)]
144pub enum ClientKeysetId {
145 Single(Uuid),
146 Multiple(Vec<Uuid>),
147}
148
149impl PartialEq<Uuid> for ClientKeysetId {
151 fn eq(&self, other: &Uuid) -> bool {
152 if let ClientKeysetId::Single(id) = self {
153 id == other
154 } else {
155 false
156 }
157 }
158}
159
160#[derive(Debug, Serialize, Deserialize)]
162pub struct KeysetClient {
163 pub id: Uuid,
164 #[serde(alias = "dataset_id")]
165 pub keyset_id: ClientKeysetId,
166 pub name: String,
167 pub description: String,
168}
169
170impl ViturResponse for Vec<KeysetClient> {}
171
172#[derive(Debug, Serialize, Deserialize)]
177pub struct DeleteClientRequest {
178 pub client_id: Uuid,
179}
180
181impl ViturRequest for DeleteClientRequest {
182 type Response = DeleteClientResponse;
183
184 const ENDPOINT: &'static str = "delete-client";
185 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::Delete));
186}
187
188#[derive(Default, Debug, Serialize, Deserialize)]
189pub struct DeleteClientResponse {}
190
191impl ViturResponse for DeleteClientResponse {}
192
193#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
195pub struct ViturKeyMaterial(#[serde(with = "base64_vec")] Vec<u8>);
196opaque_debug::implement!(ViturKeyMaterial);
197
198impl From<Vec<u8>> for ViturKeyMaterial {
199 fn from(inner: Vec<u8>) -> Self {
200 Self(inner)
201 }
202}
203
204impl Deref for ViturKeyMaterial {
205 type Target = [u8];
206
207 fn deref(&self) -> &Self::Target {
208 &self.0
209 }
210}
211
212#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Zeroize)]
213#[serde(transparent)]
214pub struct KeyId(#[serde(with = "base64_array")] [u8; 16]);
215
216impl KeyId {
217 pub fn into_inner(self) -> [u8; 16] {
218 self.0
219 }
220}
221
222impl Display for KeyId {
223 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
224 write!(f, "{}", const_hex::encode(self.0))
225 }
226}
227
228impl From<[u8; 16]> for KeyId {
229 fn from(inner: [u8; 16]) -> Self {
230 Self(inner)
231 }
232}
233
234impl AsRef<[u8; 16]> for KeyId {
235 fn as_ref(&self) -> &[u8; 16] {
236 &self.0
237 }
238}
239
240#[derive(Debug, Serialize, Deserialize)]
244pub struct GeneratedKey {
245 pub key_material: ViturKeyMaterial,
246 #[serde(with = "base64_vec")]
248 pub tag: Vec<u8>,
249}
250
251#[derive(Debug, Serialize, Deserialize)]
253pub struct GenerateKeyResponse {
254 pub keys: Vec<GeneratedKey>,
255}
256
257impl ViturResponse for GenerateKeyResponse {}
258
259#[derive(Debug, Serialize, Deserialize, Clone)]
261pub struct GenerateKeySpec<'a> {
262 #[serde(alias = "id")]
264 pub iv: KeyId,
265 pub descriptor: Cow<'a, str>,
267
268 #[serde(default)]
269 pub context: Cow<'a, [Context]>,
270}
271
272impl<'a> GenerateKeySpec<'a> {
273 pub fn new(iv: [u8; 16], descriptor: &'a str) -> Self {
274 Self {
275 iv: KeyId(iv),
276 descriptor: Cow::from(descriptor),
277 context: Default::default(),
278 }
279 }
280
281 pub fn new_with_context(
282 iv: [u8; 16],
283 descriptor: &'a str,
284 context: Cow<'a, [Context]>,
285 ) -> Self {
286 Self {
287 iv: KeyId(iv),
288 descriptor: Cow::from(descriptor),
289 context,
290 }
291 }
292}
293#[derive(Debug, Serialize, Deserialize, Clone)]
297pub enum Context {
299 Tag(String),
301
302 Value(String, String),
305
306 #[serde(alias = "identityClaim")]
311 IdentityClaim(String),
312}
313
314impl Context {
315 pub fn new_tag(tag: impl Into<String>) -> Self {
316 Self::Tag(tag.into())
317 }
318
319 pub fn new_value(key: impl Into<String>, value: impl Into<String>) -> Self {
320 Self::Value(key.into(), value.into())
321 }
322
323 pub fn new_identity_claim(claim: &str) -> Self {
324 Self::IdentityClaim(claim.to_string())
325 }
326}
327
328#[derive(Debug, Serialize, Deserialize)]
336pub struct GenerateKeyRequest<'a> {
337 pub client_id: Uuid,
338 #[serde(alias = "dataset_id")]
339 pub keyset_id: Option<IdentifiedBy>,
340 pub keys: Cow<'a, [GenerateKeySpec<'a>]>,
341 #[serde(default)]
342 pub unverified_context: Cow<'a, UnverifiedContext>,
343}
344
345impl ViturRequest for GenerateKeyRequest<'_> {
346 type Response = GenerateKeyResponse;
347
348 const ENDPOINT: &'static str = "generate-data-key";
349 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Generate));
350}
351
352#[derive(Debug, Serialize, Deserialize)]
354pub struct RetrievedKey {
355 pub key_material: ViturKeyMaterial,
356}
357
358#[derive(Debug, Serialize, Deserialize)]
361pub struct RetrieveKeyResponse {
362 pub keys: Vec<RetrievedKey>,
363}
364
365impl ViturResponse for RetrieveKeyResponse {}
366
367#[derive(Debug, Serialize, Deserialize, Clone)]
369pub struct RetrieveKeySpec<'a> {
370 #[serde(alias = "id")]
371 pub iv: KeyId,
372 pub descriptor: Cow<'a, str>,
374 pub tag: Cow<'a, [u8]>,
375
376 #[serde(default)]
377 pub context: Cow<'a, [Context]>,
378
379 #[serde(default)]
382 pub tag_version: usize,
383}
384
385impl<'a> RetrieveKeySpec<'a> {
386 const DEFAULT_TAG_VERSION: usize = 0;
387
388 pub fn new(id: KeyId, tag: &'a [u8], descriptor: &'a str) -> Self {
389 Self {
390 iv: id,
391 descriptor: Cow::from(descriptor),
392 tag: Cow::from(tag),
393 context: Cow::Owned(Vec::new()),
394 tag_version: Self::DEFAULT_TAG_VERSION,
395 }
396 }
397
398 pub fn with_context(mut self, context: Cow<'a, [Context]>) -> Self {
399 self.context = context;
400 self
401 }
402}
403
404#[derive(Debug, Serialize, Deserialize)]
410pub struct RetrieveKeyRequest<'a> {
411 pub client_id: Uuid,
412 #[serde(alias = "dataset_id")]
413 pub keyset_id: Option<IdentifiedBy>,
414 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
415 #[serde(default)]
416 pub unverified_context: UnverifiedContext,
417}
418
419impl ViturRequest for RetrieveKeyRequest<'_> {
420 type Response = RetrieveKeyResponse;
421
422 const ENDPOINT: &'static str = "retrieve-data-key";
423 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
424}
425
426#[derive(Debug, Serialize, Deserialize)]
432pub struct RetrieveKeyRequestFallible<'a> {
433 pub client_id: Uuid,
434 #[serde(alias = "dataset_id")]
435 pub keyset_id: Option<IdentifiedBy>,
436 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
437 #[serde(default)]
438 pub unverified_context: Cow<'a, UnverifiedContext>,
439}
440
441impl ViturRequest for RetrieveKeyRequestFallible<'_> {
442 type Response = RetrieveKeyResponseFallible;
443
444 const ENDPOINT: &'static str = "retrieve-data-key-fallible";
445 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
446}
447
448#[derive(Debug, Serialize, Deserialize)]
450pub struct RetrieveKeyResponseFallible {
451 pub keys: Vec<Result<RetrievedKey, String>>, }
453
454impl ViturResponse for RetrieveKeyResponseFallible {}
455
456#[derive(Debug, Serialize, Deserialize)]
460pub struct DisableKeysetRequest {
461 #[serde(alias = "dataset_id")]
462 pub keyset_id: IdentifiedBy,
463}
464
465impl ViturRequest for DisableKeysetRequest {
466 type Response = EmptyResponse;
467
468 const ENDPOINT: &'static str = "disable-keyset";
469 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Disable));
470}
471
472#[derive(Debug, Serialize, Deserialize)]
476pub struct EnableKeysetRequest {
477 #[serde(alias = "dataset_id")]
478 pub keyset_id: IdentifiedBy,
479}
480
481impl ViturRequest for EnableKeysetRequest {
482 type Response = EmptyResponse;
483
484 const ENDPOINT: &'static str = "enable-keyset";
485 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Enable));
486}
487
488#[derive(Debug, Serialize, Deserialize)]
494pub struct ModifyKeysetRequest<'a> {
495 #[serde(alias = "dataset_id")]
496 pub keyset_id: IdentifiedBy,
497
498 pub name: Option<Cow<'a, str>>,
499 pub description: Option<Cow<'a, str>>,
500}
501
502impl ViturRequest for ModifyKeysetRequest<'_> {
503 type Response = EmptyResponse;
504
505 const ENDPOINT: &'static str = "modify-keyset";
506 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Modify));
507}
508
509#[derive(Debug, Serialize, Deserialize)]
514pub struct GrantKeysetRequest {
515 pub client_id: Uuid,
516 #[serde(alias = "dataset_id")]
517 pub keyset_id: IdentifiedBy,
518}
519
520impl ViturRequest for GrantKeysetRequest {
521 type Response = EmptyResponse;
522
523 const ENDPOINT: &'static str = "grant-keyset";
524 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Grant));
525}
526
527#[derive(Debug, Serialize, Deserialize)]
531pub struct RevokeKeysetRequest {
532 pub client_id: Uuid,
533 #[serde(alias = "dataset_id")]
534 pub keyset_id: IdentifiedBy,
535}
536
537impl ViturRequest for RevokeKeysetRequest {
538 type Response = EmptyResponse;
539
540 const ENDPOINT: &'static str = "revoke-keyset";
541 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Revoke));
542}
543
544#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd)]
553pub struct LoadKeysetRequest {
554 pub client_id: Uuid,
555 #[serde(alias = "dataset_id")]
556 pub keyset_id: Option<IdentifiedBy>,
557}
558
559impl ViturRequest for LoadKeysetRequest {
560 type Response = LoadKeysetResponse;
561
562 const ENDPOINT: &'static str = "load-keyset";
563
564 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
568}
569
570#[derive(Debug, Serialize, Deserialize)]
574pub struct LoadKeysetResponse {
575 pub partial_index_key: RetrievedKey,
576 #[serde(rename = "dataset")]
577 pub keyset: Keyset,
578}
579
580impl ViturResponse for LoadKeysetResponse {}
581
582#[cfg(test)]
583mod test {
584 use serde_json::json;
585 use uuid::Uuid;
586
587 use crate::{IdentifiedBy, LoadKeysetRequest, Name};
588
589 mod backwards_compatible_deserialisation {
590 use super::*;
591
592 #[test]
593 fn when_dataset_id_is_uuid() {
594 let client_id = Uuid::new_v4();
595 let dataset_id = Uuid::new_v4();
596
597 let json = json!({
598 "client_id": client_id,
599 "dataset_id": dataset_id,
600 });
601
602 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
603
604 assert_eq!(
605 req,
606 LoadKeysetRequest {
607 client_id,
608 keyset_id: Some(IdentifiedBy::Uuid(dataset_id))
609 }
610 );
611 }
612
613 #[test]
614 fn when_keyset_id_is_uuid() {
615 let client_id = Uuid::new_v4();
616 let keyset_id = Uuid::new_v4();
617
618 let json = json!({
619 "client_id": client_id,
620 "keyset_id": keyset_id,
621 });
622
623 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
624
625 assert_eq!(
626 req,
627 LoadKeysetRequest {
628 client_id,
629 keyset_id: Some(IdentifiedBy::Uuid(keyset_id))
630 }
631 );
632 }
633
634 #[test]
635 fn when_dataset_id_is_id_name() {
636 let client_id = Uuid::new_v4();
637 let dataset_id = IdentifiedBy::Name(Name::new_untrusted("some-dataset-name"));
638
639 let json = json!({
640 "client_id": client_id,
641 "dataset_id": dataset_id,
642 });
643
644 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
645
646 assert_eq!(
647 req,
648 LoadKeysetRequest {
649 client_id,
650 keyset_id: Some(dataset_id)
651 }
652 );
653 }
654 }
655}