cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
db_crypt.c
Go to the documentation of this file.
2
3#include <openssl/evp.h>
4#include <openssl/mem.h>
5#include <openssl/rand.h>
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11/* -------------------------------------------------------------------------
12 * Internal helpers
13 * ---------------------------------------------------------------------- */
14
15static const unsigned char MAGIC[4] = {0x43, 0x57, 0x44, 0x42}; /* "CWDB" */
16static const unsigned char VERSION = 0x01;
17
23static int aes256_encrypt(const unsigned char *key, const unsigned char *iv,
24 const unsigned char *in, size_t in_len, unsigned char *out) {
26 if (!ctx) return -1;
27
28 int len1 = 0, len2 = 0;
29
30 if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1 ||
31 EVP_EncryptUpdate(ctx, out, &len1, in, (int)in_len) != 1 ||
32 EVP_EncryptFinal_ex(ctx, out + len1, &len2) != 1) {
34 return -1;
35 }
36
38 return len1 + len2;
39}
40
46static int aes256_decrypt(const unsigned char *key, const unsigned char *iv,
47 const unsigned char *in, size_t in_len, unsigned char *out) {
49 if (!ctx) return -1;
50
51 int len1 = 0, len2 = 0;
52
53 if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1 ||
54 EVP_DecryptUpdate(ctx, out, &len1, in, (int)in_len) != 1 ||
55 EVP_DecryptFinal_ex(ctx, out + len1, &len2) != 1) {
57 return -1;
58 }
59
61 return len1 + len2;
62}
63
64/* -------------------------------------------------------------------------
65 * Public API
66 * ---------------------------------------------------------------------- */
67
68unsigned char *cwist_db_crypt_seal(const cwist_db_crypt_ctx_t *ctx,
69 const unsigned char *sqlite_bytes, size_t sqlite_len,
70 size_t *out_len) {
71 if (!ctx || !sqlite_bytes || !out_len) return NULL;
72
73 /* 1. Generate random DEK and IVs. */
74 unsigned char dek[CWIST_DB_CRYPT_KEY_LEN];
75 unsigned char kek_iv[CWIST_DB_CRYPT_IV_LEN];
76 unsigned char dek_iv[CWIST_DB_CRYPT_IV_LEN];
77
78 if (RAND_bytes(dek, sizeof(dek)) != 1 || RAND_bytes(kek_iv, sizeof(kek_iv)) != 1 ||
79 RAND_bytes(dek_iv, sizeof(dek_iv)) != 1) {
80 return NULL;
81 }
82
83 /* 2. Wrap the DEK: encrypt ( dek_iv || dek ) with KEK.
84 * Plaintext: 16 + 32 = 48 bytes → cipher: 48 bytes (no padding needed,
85 * but EVP adds a full block when input is block-aligned, so we use 48
86 * and disable padding via a temp context).
87 * To keep it simple we just encrypt 48 bytes of dek_iv||dek normally;
88 * EVP pads to 64. We'll store all 64 for safe round-trip. */
92
93 /* Encrypted DEK blob: 48 plaintext bytes → 64 ciphertext bytes. */
95 int enc_dek_len = aes256_encrypt(ctx->kek, kek_iv, dek_plain, sizeof(dek_plain), enc_dek);
96 if (enc_dek_len <= 0) {
97 OPENSSL_cleanse(dek, sizeof(dek));
99 return NULL;
100 }
101
102 /* 3. Encrypt SQLite bytes with DEK. */
103 size_t ct_max = sqlite_len + 16 + 1; /* extra block for padding */
104 unsigned char *ct = (unsigned char *)malloc(ct_max);
105 if (!ct) {
106 OPENSSL_cleanse(dek, sizeof(dek));
108 return NULL;
109 }
110
111 int ct_len = aes256_encrypt(dek, dek_iv, sqlite_bytes, sqlite_len, ct);
112 OPENSSL_cleanse(dek, sizeof(dek));
114 if (ct_len <= 0) {
115 free(ct);
116 return NULL;
117 }
118
119 /* 4. Assemble blob:
120 * magic(4) + ver(1) + kek_iv(16) + enc_dek(enc_dek_len)
121 * + dek_iv(16) + ptlen(8) + ct(ct_len)
122 */
123 size_t hdr_fixed =
125 size_t total = hdr_fixed + (size_t)ct_len;
126
127 unsigned char *blob = (unsigned char *)malloc(total);
128 if (!blob) {
129 free(ct);
130 return NULL;
131 }
132
133 size_t off = 0;
134 memcpy(blob + off, MAGIC, 4);
135 off += 4;
136 blob[off] = VERSION;
137 off += 1;
138 memcpy(blob + off, kek_iv, CWIST_DB_CRYPT_IV_LEN);
140 memcpy(blob + off, enc_dek, (size_t)enc_dek_len);
141 off += (size_t)enc_dek_len;
142 memcpy(blob + off, dek_iv, CWIST_DB_CRYPT_IV_LEN);
144
145 /* plaintext length as little-endian uint64 */
147 unsigned char ptlen_buf[8];
148 for (int i = 0; i < 8; i++) ptlen_buf[i] = (unsigned char)(ptlen_le >> (8 * i));
149 memcpy(blob + off, ptlen_buf, 8);
150 off += 8;
151
152 memcpy(blob + off, ct, (size_t)ct_len);
153
154 free(ct);
155
156 *out_len = total;
157 return blob;
158}
159
160unsigned char *cwist_db_crypt_open(const cwist_db_crypt_ctx_t *ctx, const unsigned char *blob,
161 size_t blob_len, size_t *out_len) {
162 if (!ctx || !blob || !out_len) return NULL;
163
164 /* Minimum header: 4+1+16+64+16+8 = 109 bytes. */
165 if (blob_len < CWIST_DB_CRYPT_HDR_LEN) return NULL;
166
167 size_t off = 0;
168
169 /* 1. Verify magic and version. */
170 if (memcmp(blob + off, MAGIC, 4) != 0) return NULL;
171 off += 4;
172 if (blob[off] != VERSION) return NULL;
173 off += 1;
174
175 /* 2. Read KEK IV. */
176 const unsigned char *kek_iv = blob + off;
178
179 /* 3. Decrypt DEK blob (48 bytes padded to 64 during seal).
180 * The encrypted DEK blob occupies:
181 * enc_dek_len = blob_len - fixed_parts_after_enc_dek
182 * fixed_parts_after: dek_iv(16) + ptlen(8) + at_least_one_block(16)
183 * We know enc_dek_len = (blob_len - off - 16 - 8 - ct_len)
184 * but we stored enc_dek_len as 48+16=64 bytes always.
185 * Recalculate: plain was 48 bytes → padded = 64. */
186 const size_t enc_dek_len = 64;
187 if (blob_len < off + enc_dek_len + CWIST_DB_CRYPT_IV_LEN + 8) return NULL;
188
190 int dek_plain_len = aes256_decrypt(ctx->kek, kek_iv, blob + off, enc_dek_len, dek_plain);
192 off += enc_dek_len;
193
194 const unsigned char *dek_iv = dek_plain;
195 const unsigned char *dek = dek_plain + CWIST_DB_CRYPT_IV_LEN;
196
197 /* 4. Read stored DEK IV (should match what was unwrapped). */
198 off += CWIST_DB_CRYPT_IV_LEN; /* skip dek_iv in header (redundant; we use the unwrapped one) */
199
200 /* 5. Read plaintext length (little-endian uint64). */
201 uint64_t ptlen_le = 0;
202 for (int i = 0; i < 8; i++) ptlen_le |= ((uint64_t)blob[off + i]) << (8 * i);
203 off += 8;
205
206 /* 6. Decrypt SQLite bytes. */
207 size_t ct_len = blob_len - off;
208 if (ct_len == 0) return NULL;
209
210 unsigned char *pt = (unsigned char *)malloc(ct_len + 1); /* extra for safety */
211 if (!pt) return NULL;
212
213 int pt_len = aes256_decrypt(dek, dek_iv, blob + off, ct_len, pt);
215 if (pt_len < 0 || (size_t)pt_len < expected_pt_len) {
216 free(pt);
217 return NULL;
218 }
219
220 pt[expected_pt_len] = '\0'; /* null-terminate for safety (sqlite doesn't need it) */
221 *out_len = expected_pt_len;
222 return pt;
223}
#define OPENSSL_cleanse
Definition crypto.h:16
unsigned char * cwist_db_crypt_seal(const cwist_db_crypt_ctx_t *ctx, const unsigned char *sqlite_bytes, size_t sqlite_len, size_t *out_len)
Encrypt an SQLite database blob.
Definition db_crypt.c:68
unsigned char * cwist_db_crypt_open(const cwist_db_crypt_ctx_t *ctx, const unsigned char *blob, size_t blob_len, size_t *out_len)
Decrypt a sealed blob produced by cwist_db_crypt_seal().
Definition db_crypt.c:160
Two-level key hierarchy for encrypting an SQLite database blob.
#define CWIST_DB_CRYPT_IV_LEN
Definition db_crypt.h:41
#define CWIST_DB_CRYPT_KEY_LEN
Definition db_crypt.h:40
#define CWIST_DB_CRYPT_HDR_LEN
Definition db_crypt.h:43
mbedTLS TLS backend: EVP subset (HMAC digest tags, AES-256-CBC).
#define EVP_DecryptInit_ex
Definition evp.h:37
#define EVP_DecryptFinal_ex
Definition evp.h:39
struct cwist_mtls_cipher_ctx EVP_CIPHER_CTX
Definition evp.h:21
#define EVP_CIPHER_CTX_new
Definition evp.h:32
#define EVP_EncryptInit_ex
Definition evp.h:34
#define EVP_EncryptUpdate
Definition evp.h:35
#define EVP_aes_256_cbc
Definition evp.h:26
#define EVP_EncryptFinal_ex
Definition evp.h:36
#define EVP_CIPHER_CTX_free
Definition evp.h:33
#define EVP_DecryptUpdate
Definition evp.h:38
mbedTLS TLS backend: OPENSSL_cleanse (BoringSSL puts it here).
size_t cap
Definition otel.c:56
mbedTLS TLS backend: RAND_bytes over mbedTLS CTR-DRBG.
#define RAND_bytes
Definition rand.h:17
Caller-managed key pair.
Definition db_crypt.h:53
unsigned char kek[CWIST_DB_CRYPT_KEY_LEN]
Definition db_crypt.h:54