cipherstash_dynamodb/crypto/
mod.rs1mod attrs;
2mod b64_encode;
3mod sealed;
4mod sealer;
5mod unsealed;
6use crate::{
7 traits::{PrimaryKeyError, PrimaryKeyParts, ReadConversionError, WriteConversionError},
8 Identifiable, IndexType, PrimaryKey,
9};
10use cipherstash_client::{
11 credentials::{service_credentials::ServiceToken, Credentials},
12 encryption::{
13 compound_indexer::{CompoundIndex, ExactIndex},
14 Encryption, EncryptionError, Plaintext, TypeParseError,
15 },
16 zerokms::Error as ZeroKmsError,
17};
18use miette::Diagnostic;
19use std::borrow::Cow;
20use thiserror::Error;
21
22pub use b64_encode::*;
24pub use sealed::{SealedTableEntry, UnsealSpec};
25pub use sealer::{Sealer, UnsealedIndex};
26pub use unsealed::Unsealed;
27
28const MAX_TERMS_PER_INDEX: usize = 25;
32
33#[derive(Debug, Error, Diagnostic)]
34pub enum SealError {
35 #[error("Error when creating primary key: {0}")]
36 PrimaryKeyError(#[from] PrimaryKeyError),
37 #[error("ReadConversionError: {0}")]
38 ReadConversionError(#[from] ReadConversionError),
39 #[error("WriteConversionError: {0}")]
40 WriteConversionError(#[from] WriteConversionError),
41 #[error("TypeParseError: {0}")]
42 TypeParseError(#[from] TypeParseError),
43 #[error("Missing attribute: {0}")]
44 MissingAttribute(String),
45 #[error("Invalid ciphertext value: {0}")]
46 InvalidCiphertext(String),
47 #[error("Assertion failed: {0}")]
48 AssertionFailed(String),
49
50 #[error(transparent)]
53 EncryptionError(#[from] EncryptionError),
54
55 #[error(transparent)]
56 ZeroKmsError(#[from] ZeroKmsError),
57}
58
59#[derive(Error, Debug)]
60pub enum CryptoError {
61 #[error("EncryptionError: {0}")]
62 EncryptionError(#[from] EncryptionError),
63 #[error("ReadConversionError: {0}")]
64 ReadConversionError(#[from] ReadConversionError),
65 #[error("{0}")]
66 Other(String),
67}
68
69pub fn format_term_key(
70 sort_key: &str,
71 index_name: &str,
72 index_type: IndexType,
73 counter: usize,
74) -> String {
75 format!("{sort_key}#{index_name}#{index_type}#{counter}")
76}
77
78pub(crate) fn all_index_keys<'a>(
82 sort_key: &str,
83 protected_indexes: impl AsRef<[(Cow<'a, str>, IndexType)]>,
84) -> Vec<String> {
85 protected_indexes
86 .as_ref()
87 .iter()
88 .flat_map(|(index_name, index_type)| {
89 (0..)
90 .take(MAX_TERMS_PER_INDEX)
91 .map(|i| format_term_key(sort_key, index_name, *index_type, i))
92 .collect::<Vec<String>>()
93 })
94 .collect()
95}
96
97pub fn hmac(
101 value: &str,
102 salt: Option<&str>,
103 cipher: &Encryption<impl Credentials<Token = ServiceToken>>,
104) -> Result<Vec<u8>, EncryptionError> {
105 let plaintext = Plaintext::Utf8Str(Some(value.to_string()));
106 let index = CompoundIndex::new(ExactIndex::new(vec![]));
107
108 cipher
109 .compound_index(
110 &index,
111 plaintext,
112 Some(salt.unwrap_or("")),
114 32,
115 )?
116 .as_binary()
117 .ok_or(EncryptionError::IndexingError(
118 "Invalid term type".to_string(),
119 ))
120}
121
122#[derive(Clone)]
124pub struct PreparedPrimaryKey {
125 pub primary_key_parts: PrimaryKeyParts,
126 pub is_pk_encrypted: bool,
127 pub is_sk_encrypted: bool,
128}
129
130impl PreparedPrimaryKey {
131 pub fn new<R>(k: impl Into<R::PrimaryKey>) -> Self
132 where
133 R: Identifiable,
134 {
135 let primary_key_parts = k
136 .into()
137 .into_parts(&R::type_name(), R::sort_key_prefix().as_deref());
138
139 Self::new_from_parts::<R>(primary_key_parts)
140 }
141
142 pub fn new_from_parts<R>(primary_key_parts: PrimaryKeyParts) -> Self
143 where
144 R: Identifiable,
145 {
146 Self {
147 primary_key_parts,
148 is_pk_encrypted: R::is_pk_encrypted(),
149 is_sk_encrypted: R::is_sk_encrypted(),
150 }
151 }
152}