secure_db_fields 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +98 -0
  4. data/Rakefile +16 -0
  5. data/db_deployment/mysql/Makefile +62 -0
  6. data/db_deployment/mysql/README.md +33 -0
  7. data/db_deployment/mysql/bin/common +199 -0
  8. data/db_deployment/mysql/bin/disable +40 -0
  9. data/db_deployment/mysql/bin/doctor +57 -0
  10. data/db_deployment/mysql/bin/enable +56 -0
  11. data/db_deployment/mysql/bin/install +64 -0
  12. data/db_deployment/mysql/bin/status +11 -0
  13. data/db_deployment/mysql/bin/uninstall +46 -0
  14. data/db_deployment/mysql/bin/verify +27 -0
  15. data/db_deployment/mysql/sql/examples.sql +20 -0
  16. data/db_deployment/mysql/sql/install.sql +20 -0
  17. data/db_deployment/mysql/sql/uninstall.sql +8 -0
  18. data/docs/final_decision.md +202 -0
  19. data/docs/implementation_notes.md +63 -0
  20. data/docs/ruby_27_mysql_57.md +33 -0
  21. data/examples/clients_phone_migration.sql +6 -0
  22. data/examples/keys.env.example +6 -0
  23. data/examples/ruby_usage.rb +15 -0
  24. data/exe/secure_db_fields +49 -0
  25. data/ext/secure_db_fields/extconf.rb +21 -0
  26. data/ext/secure_db_fields/secure_db_fields_core.c +723 -0
  27. data/ext/secure_db_fields/secure_db_fields_core.h +140 -0
  28. data/ext/secure_db_fields/secure_db_fields_ext.c +426 -0
  29. data/lib/secure_db_fields/active_record.rb +58 -0
  30. data/lib/secure_db_fields/crypto.rb +115 -0
  31. data/lib/secure_db_fields/db_deployment.rb +285 -0
  32. data/lib/secure_db_fields/keys.rb +69 -0
  33. data/lib/secure_db_fields/phone.rb +17 -0
  34. data/lib/secure_db_fields/version.rb +5 -0
  35. data/lib/secure_db_fields.rb +8 -0
  36. data/mysql_udf/Makefile +25 -0
  37. data/mysql_udf/README.md +51 -0
  38. data/mysql_udf/bin/install +11 -0
  39. data/mysql_udf/bin/uninstall +4 -0
  40. data/mysql_udf/bin/verify +3 -0
  41. data/mysql_udf/sql/examples.sql +20 -0
  42. data/mysql_udf/sql/install.sql +20 -0
  43. data/mysql_udf/sql/uninstall.sql +8 -0
  44. data/mysql_udf/src/mysql_udf_abi_57.h +34 -0
  45. data/mysql_udf/src/secure_db_fields_mysql.c +513 -0
  46. data/mysql_udf/test/run_e2e.sh +216 -0
  47. metadata +137 -0
@@ -0,0 +1,723 @@
1
+ #include "secure_db_fields_core.h"
2
+
3
+ #include <ctype.h>
4
+ #include <errno.h>
5
+ #include <openssl/crypto.h>
6
+ #include <openssl/evp.h>
7
+ #include <openssl/rand.h>
8
+ #include <stdio.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+ #ifndef _WIN32
12
+ #include <sys/stat.h>
13
+ #endif
14
+
15
+ static void sdf_set_err(char *err, size_t err_len, const char *msg) {
16
+ if (err && err_len > 0) {
17
+ snprintf(err, err_len, "%s", msg ? msg : "unknown error");
18
+ }
19
+ }
20
+
21
+ static void sdf_set_err2(char *err, size_t err_len, const char *prefix, const char *detail) {
22
+ if (err && err_len > 0) {
23
+ snprintf(err, err_len, "%s%s", prefix ? prefix : "", detail ? detail : "");
24
+ }
25
+ }
26
+
27
+ const char *sdf_status_name(sdf_status status) {
28
+ switch (status) {
29
+ case SDF_OK:
30
+ return "ok";
31
+ case SDF_ERR_INVALID_ARGUMENT:
32
+ return "invalid_argument";
33
+ case SDF_ERR_NO_MEMORY:
34
+ return "no_memory";
35
+ case SDF_ERR_RANDOM:
36
+ return "random_failed";
37
+ case SDF_ERR_CRYPTO:
38
+ return "crypto_failed";
39
+ case SDF_ERR_AUTH:
40
+ return "authentication_failed";
41
+ case SDF_ERR_FORMAT:
42
+ return "invalid_envelope";
43
+ case SDF_ERR_KEY:
44
+ return "key_error";
45
+ case SDF_ERR_UNSUPPORTED:
46
+ return "unsupported";
47
+ default:
48
+ return "unknown";
49
+ }
50
+ }
51
+
52
+ void sdf_secure_clear(void *ptr, size_t len) {
53
+ if (ptr && len > 0)
54
+ OPENSSL_cleanse(ptr, len);
55
+ }
56
+
57
+ static void sdf_u32be_write(unsigned char *dst, uint32_t value) {
58
+ dst[0] = (unsigned char)((value >> 24) & 0xff);
59
+ dst[1] = (unsigned char)((value >> 16) & 0xff);
60
+ dst[2] = (unsigned char)((value >> 8) & 0xff);
61
+ dst[3] = (unsigned char)(value & 0xff);
62
+ }
63
+
64
+ static uint32_t sdf_u32be_read(const unsigned char *src) {
65
+ return ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) | ((uint32_t)src[2] << 8) |
66
+ ((uint32_t)src[3]);
67
+ }
68
+
69
+ static sdf_status sdf_validate_key(const unsigned char *key, size_t key_len, char *err,
70
+ size_t err_len) {
71
+ if (!key || key_len != SDF_KEY_BYTES) {
72
+ sdf_set_err(err, err_len, "key must be exactly 32 bytes");
73
+ return SDF_ERR_KEY;
74
+ }
75
+ return SDF_OK;
76
+ }
77
+
78
+ static int sdf_gcm_ctx_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
79
+ const unsigned char *nonce, int encrypt) {
80
+ if (encrypt) {
81
+ return EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 1 &&
82
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, SDF_GCM_NONCE_BYTES, NULL) == 1 &&
83
+ EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce) == 1;
84
+ }
85
+ return EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 1 &&
86
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, SDF_GCM_NONCE_BYTES, NULL) == 1 &&
87
+ EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce) == 1;
88
+ }
89
+
90
+ size_t sdf_encrypt_aes_256_gcm_output_len(size_t plaintext_len) {
91
+ return SDF_HEADER_BYTES + plaintext_len;
92
+ }
93
+
94
+ sdf_status sdf_encrypt_aes_256_gcm_into(const unsigned char *plaintext, size_t plaintext_len,
95
+ const unsigned char *key, size_t key_len,
96
+ const unsigned char *aad, size_t aad_len, uint32_t key_id,
97
+ unsigned char *out, size_t out_cap, size_t *out_len,
98
+ char *err, size_t err_len) {
99
+ EVP_CIPHER_CTX *ctx = NULL;
100
+ unsigned char nonce[SDF_GCM_NONCE_BYTES];
101
+ int len = 0;
102
+ int total = 0;
103
+ size_t needed;
104
+ sdf_status st = SDF_OK;
105
+ const char *msg = NULL;
106
+
107
+ if (!out || !out_len || (!plaintext && plaintext_len > 0)) {
108
+ sdf_set_err(err, err_len, "invalid encrypt arguments");
109
+ return SDF_ERR_INVALID_ARGUMENT;
110
+ }
111
+ *out_len = 0;
112
+ if (sdf_validate_key(key, key_len, err, err_len) != SDF_OK)
113
+ return SDF_ERR_KEY;
114
+ if (!aad && aad_len > 0) {
115
+ sdf_set_err(err, err_len, "aad pointer is NULL but aad_len > 0");
116
+ return SDF_ERR_INVALID_ARGUMENT;
117
+ }
118
+ if (plaintext_len > (size_t)INT_MAX || aad_len > (size_t)INT_MAX) {
119
+ sdf_set_err(err, err_len, "input too large for OpenSSL EVP int lengths");
120
+ return SDF_ERR_INVALID_ARGUMENT;
121
+ }
122
+ if (key_id == 0) {
123
+ sdf_set_err(err, err_len, "key_id must be positive");
124
+ return SDF_ERR_INVALID_ARGUMENT;
125
+ }
126
+
127
+ needed = sdf_encrypt_aes_256_gcm_output_len(plaintext_len);
128
+ if (out_cap < needed) {
129
+ sdf_set_err(err, err_len, "output buffer too small");
130
+ return SDF_ERR_INVALID_ARGUMENT;
131
+ }
132
+
133
+ if (RAND_bytes(nonce, SDF_GCM_NONCE_BYTES) != 1) {
134
+ sdf_set_err(err, err_len, "RAND_bytes failed");
135
+ return SDF_ERR_RANDOM;
136
+ }
137
+
138
+ memcpy(out, SDF_MAGIC, SDF_MAGIC_LEN);
139
+ out[4] = SDF_ENVELOPE_VERSION;
140
+ out[5] = SDF_ALG_AES_256_GCM;
141
+ sdf_u32be_write(out + 6, key_id);
142
+ memcpy(out + 10, nonce, SDF_GCM_NONCE_BYTES);
143
+
144
+ ctx = EVP_CIPHER_CTX_new();
145
+ if (!ctx) {
146
+ msg = "EVP_CIPHER_CTX_new failed";
147
+ st = SDF_ERR_CRYPTO;
148
+ goto cleanup;
149
+ }
150
+
151
+ if (!sdf_gcm_ctx_init(ctx, key, nonce, 1)) {
152
+ msg = "EVP AES-256-GCM init failed";
153
+ st = SDF_ERR_CRYPTO;
154
+ goto cleanup;
155
+ }
156
+
157
+ if (aad_len > 0 && EVP_EncryptUpdate(ctx, NULL, &len, aad, (int)aad_len) != 1) {
158
+ msg = "EVP AAD update failed";
159
+ st = SDF_ERR_CRYPTO;
160
+ goto cleanup;
161
+ }
162
+
163
+ if (EVP_EncryptUpdate(ctx, out + SDF_HEADER_BYTES, &len, plaintext_len > 0 ? plaintext : out,
164
+ (int)plaintext_len) != 1) {
165
+ msg = "EVP encrypt update failed";
166
+ st = SDF_ERR_CRYPTO;
167
+ goto cleanup;
168
+ }
169
+ total = len;
170
+
171
+ if (EVP_EncryptFinal_ex(ctx, out + SDF_HEADER_BYTES + total, &len) != 1) {
172
+ msg = "EVP encrypt final failed";
173
+ st = SDF_ERR_CRYPTO;
174
+ goto cleanup;
175
+ }
176
+ total += len;
177
+
178
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, SDF_GCM_TAG_BYTES,
179
+ out + 10 + SDF_GCM_NONCE_BYTES) != 1) {
180
+ msg = "EVP get tag failed";
181
+ st = SDF_ERR_CRYPTO;
182
+ goto cleanup;
183
+ }
184
+
185
+ *out_len = SDF_HEADER_BYTES + (size_t)total;
186
+
187
+ cleanup:
188
+ if (ctx)
189
+ EVP_CIPHER_CTX_free(ctx);
190
+ sdf_secure_clear(nonce, sizeof(nonce));
191
+ if (st != SDF_OK)
192
+ sdf_set_err(err, err_len, msg);
193
+ return st;
194
+ }
195
+
196
+ sdf_status sdf_encrypt_aes_256_gcm(const unsigned char *plaintext, size_t plaintext_len,
197
+ const unsigned char *key, size_t key_len,
198
+ const unsigned char *aad, size_t aad_len, uint32_t key_id,
199
+ unsigned char **out, size_t *out_len, char *err,
200
+ size_t err_len) {
201
+ unsigned char *buf;
202
+ size_t needed;
203
+ sdf_status st;
204
+
205
+ if (!out || !out_len) {
206
+ sdf_set_err(err, err_len, "invalid encrypt arguments");
207
+ return SDF_ERR_INVALID_ARGUMENT;
208
+ }
209
+ *out = NULL;
210
+ *out_len = 0;
211
+ needed = sdf_encrypt_aes_256_gcm_output_len(plaintext_len);
212
+ buf = (unsigned char *)malloc(needed == 0 ? 1 : needed);
213
+ if (!buf) {
214
+ sdf_set_err(err, err_len, "malloc failed");
215
+ return SDF_ERR_NO_MEMORY;
216
+ }
217
+ st = sdf_encrypt_aes_256_gcm_into(plaintext, plaintext_len, key, key_len, aad, aad_len, key_id,
218
+ buf, needed, out_len, err, err_len);
219
+ if (st != SDF_OK) {
220
+ free(buf);
221
+ return st;
222
+ }
223
+ *out = buf;
224
+ return SDF_OK;
225
+ }
226
+
227
+ int sdf_is_valid_envelope(const unsigned char *envelope, size_t envelope_len) {
228
+ if (!envelope || envelope_len < SDF_HEADER_BYTES)
229
+ return 0;
230
+ if (memcmp(envelope, SDF_MAGIC, SDF_MAGIC_LEN) != 0)
231
+ return 0;
232
+ if (envelope[4] != SDF_ENVELOPE_VERSION)
233
+ return 0;
234
+ if (envelope[5] != SDF_ALG_AES_256_GCM)
235
+ return 0;
236
+ if (sdf_u32be_read(envelope + 6) == 0)
237
+ return 0;
238
+ return 1;
239
+ }
240
+
241
+ sdf_status sdf_parse_key_id(const unsigned char *envelope, size_t envelope_len, uint32_t *key_id,
242
+ char *err, size_t err_len) {
243
+ if (!key_id) {
244
+ sdf_set_err(err, err_len, "key_id output is NULL");
245
+ return SDF_ERR_INVALID_ARGUMENT;
246
+ }
247
+ if (!sdf_is_valid_envelope(envelope, envelope_len)) {
248
+ sdf_set_err(err, err_len, "invalid MCEN envelope");
249
+ return SDF_ERR_FORMAT;
250
+ }
251
+ *key_id = sdf_u32be_read(envelope + 6);
252
+ return SDF_OK;
253
+ }
254
+
255
+ size_t sdf_decrypt_aes_256_gcm_output_cap(const unsigned char *envelope, size_t envelope_len) {
256
+ if (!sdf_is_valid_envelope(envelope, envelope_len))
257
+ return 0;
258
+ return envelope_len - SDF_HEADER_BYTES;
259
+ }
260
+
261
+ sdf_status sdf_decrypt_aes_256_gcm_into(const unsigned char *envelope, size_t envelope_len,
262
+ const unsigned char *key, size_t key_len,
263
+ const unsigned char *aad, size_t aad_len,
264
+ unsigned char *out, size_t out_cap, size_t *out_len,
265
+ char *err, size_t err_len) {
266
+ EVP_CIPHER_CTX *ctx = NULL;
267
+ const unsigned char *nonce;
268
+ const unsigned char *tag;
269
+ const unsigned char *ciphertext;
270
+ size_t ciphertext_len;
271
+ int len = 0;
272
+ int total = 0;
273
+ int final_ok = 0;
274
+ unsigned char dummy_out[1];
275
+ unsigned char *out_ptr = out ? out : dummy_out;
276
+ sdf_status st = SDF_OK;
277
+ const char *msg = NULL;
278
+
279
+ if (!out_len || !envelope || (!out && out_cap > 0)) {
280
+ sdf_set_err(err, err_len, "invalid decrypt arguments");
281
+ return SDF_ERR_INVALID_ARGUMENT;
282
+ }
283
+ *out_len = 0;
284
+ if (sdf_validate_key(key, key_len, err, err_len) != SDF_OK)
285
+ return SDF_ERR_KEY;
286
+ if (!aad && aad_len > 0) {
287
+ sdf_set_err(err, err_len, "aad pointer is NULL but aad_len > 0");
288
+ return SDF_ERR_INVALID_ARGUMENT;
289
+ }
290
+ if (!sdf_is_valid_envelope(envelope, envelope_len)) {
291
+ sdf_set_err(err, err_len, "invalid MCEN envelope");
292
+ return SDF_ERR_FORMAT;
293
+ }
294
+ if (aad_len > (size_t)INT_MAX) {
295
+ sdf_set_err(err, err_len, "aad too large for OpenSSL EVP int lengths");
296
+ return SDF_ERR_INVALID_ARGUMENT;
297
+ }
298
+
299
+ nonce = envelope + 10;
300
+ tag = envelope + 10 + SDF_GCM_NONCE_BYTES;
301
+ ciphertext = envelope + SDF_HEADER_BYTES;
302
+ ciphertext_len = envelope_len - SDF_HEADER_BYTES;
303
+ if (ciphertext_len > (size_t)INT_MAX) {
304
+ sdf_set_err(err, err_len, "ciphertext too large for OpenSSL EVP int lengths");
305
+ return SDF_ERR_INVALID_ARGUMENT;
306
+ }
307
+ if (out_cap < ciphertext_len) {
308
+ sdf_set_err(err, err_len, "output buffer too small");
309
+ return SDF_ERR_INVALID_ARGUMENT;
310
+ }
311
+
312
+ ctx = EVP_CIPHER_CTX_new();
313
+ if (!ctx) {
314
+ msg = "EVP_CIPHER_CTX_new failed";
315
+ st = SDF_ERR_CRYPTO;
316
+ goto cleanup;
317
+ }
318
+
319
+ if (!sdf_gcm_ctx_init(ctx, key, nonce, 0)) {
320
+ msg = "EVP AES-256-GCM init failed";
321
+ st = SDF_ERR_CRYPTO;
322
+ goto cleanup;
323
+ }
324
+
325
+ if (aad_len > 0 && EVP_DecryptUpdate(ctx, NULL, &len, aad, (int)aad_len) != 1) {
326
+ msg = "EVP AAD update failed";
327
+ st = SDF_ERR_CRYPTO;
328
+ goto cleanup;
329
+ }
330
+
331
+ if (EVP_DecryptUpdate(ctx, out_ptr, &len, ciphertext_len > 0 ? ciphertext : out_ptr,
332
+ (int)ciphertext_len) != 1) {
333
+ msg = "EVP decrypt update failed";
334
+ st = SDF_ERR_CRYPTO;
335
+ goto cleanup;
336
+ }
337
+ total = len;
338
+
339
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, SDF_GCM_TAG_BYTES, (void *)tag) != 1) {
340
+ msg = "EVP set tag failed";
341
+ st = SDF_ERR_CRYPTO;
342
+ goto cleanup;
343
+ }
344
+
345
+ final_ok = EVP_DecryptFinal_ex(ctx, out_ptr + total, &len);
346
+ if (final_ok != 1) {
347
+ if (out && out_cap > 0)
348
+ sdf_secure_clear(out, out_cap);
349
+ msg = "authentication failed";
350
+ st = SDF_ERR_AUTH;
351
+ goto cleanup;
352
+ }
353
+ total += len;
354
+ *out_len = (size_t)total;
355
+
356
+ cleanup:
357
+ if (ctx)
358
+ EVP_CIPHER_CTX_free(ctx);
359
+ sdf_secure_clear(dummy_out, sizeof(dummy_out));
360
+ if (st != SDF_OK)
361
+ sdf_set_err(err, err_len, msg);
362
+ return st;
363
+ }
364
+
365
+ sdf_status sdf_decrypt_aes_256_gcm(const unsigned char *envelope, size_t envelope_len,
366
+ const unsigned char *key, size_t key_len,
367
+ const unsigned char *aad, size_t aad_len, unsigned char **out,
368
+ size_t *out_len, char *err, size_t err_len) {
369
+ unsigned char *buf;
370
+ size_t cap;
371
+ sdf_status st;
372
+
373
+ if (!out || !out_len || !envelope) {
374
+ sdf_set_err(err, err_len, "invalid decrypt arguments");
375
+ return SDF_ERR_INVALID_ARGUMENT;
376
+ }
377
+ *out = NULL;
378
+ *out_len = 0;
379
+ cap = sdf_decrypt_aes_256_gcm_output_cap(envelope, envelope_len);
380
+ if (cap == 0 && !sdf_is_valid_envelope(envelope, envelope_len)) {
381
+ sdf_set_err(err, err_len, "invalid MCEN envelope");
382
+ return SDF_ERR_FORMAT;
383
+ }
384
+ buf = (unsigned char *)malloc(cap == 0 ? 1 : cap);
385
+ if (!buf) {
386
+ sdf_set_err(err, err_len, "malloc failed");
387
+ return SDF_ERR_NO_MEMORY;
388
+ }
389
+ st = sdf_decrypt_aes_256_gcm_into(envelope, envelope_len, key, key_len, aad, aad_len, buf, cap,
390
+ out_len, err, err_len);
391
+ if (st != SDF_OK) {
392
+ sdf_secure_clear(buf, cap == 0 ? 1 : cap);
393
+ free(buf);
394
+ return st;
395
+ }
396
+ *out = buf;
397
+ return SDF_OK;
398
+ }
399
+
400
+ sdf_status sdf_hmac_sha256_key_prepare(sdf_hmac_sha256_key *prepared, const unsigned char *key,
401
+ size_t key_len, char *err, size_t err_len) {
402
+ size_t i;
403
+ unsigned char ipad[SDF_HMAC_SHA256_BLOCK_BYTES];
404
+ unsigned char opad[SDF_HMAC_SHA256_BLOCK_BYTES];
405
+
406
+ if (!prepared) {
407
+ sdf_set_err(err, err_len, "prepared HMAC key is NULL");
408
+ return SDF_ERR_INVALID_ARGUMENT;
409
+ }
410
+ memset(prepared, 0, sizeof(*prepared));
411
+ if (sdf_validate_key(key, key_len, err, err_len) != SDF_OK)
412
+ return SDF_ERR_KEY;
413
+
414
+ for (i = 0; i < SDF_HMAC_SHA256_BLOCK_BYTES; i++) {
415
+ ipad[i] = 0x36;
416
+ opad[i] = 0x5c;
417
+ }
418
+ for (i = 0; i < SDF_KEY_BYTES; i++) {
419
+ ipad[i] ^= key[i];
420
+ opad[i] ^= key[i];
421
+ }
422
+
423
+ if (SHA256_Init(&prepared->inner0) != 1 ||
424
+ SHA256_Update(&prepared->inner0, ipad, SDF_HMAC_SHA256_BLOCK_BYTES) != 1 ||
425
+ SHA256_Init(&prepared->outer0) != 1 ||
426
+ SHA256_Update(&prepared->outer0, opad, SDF_HMAC_SHA256_BLOCK_BYTES) != 1) {
427
+ sdf_secure_clear(ipad, sizeof(ipad));
428
+ sdf_secure_clear(opad, sizeof(opad));
429
+ sdf_hmac_sha256_key_clear(prepared);
430
+ sdf_set_err(err, err_len, "SHA256 HMAC prepare failed");
431
+ return SDF_ERR_CRYPTO;
432
+ }
433
+
434
+ sdf_secure_clear(ipad, sizeof(ipad));
435
+ sdf_secure_clear(opad, sizeof(opad));
436
+ return SDF_OK;
437
+ }
438
+
439
+ sdf_status sdf_hmac_sha256_digest(const sdf_hmac_sha256_key *prepared, const unsigned char *value,
440
+ size_t value_len, unsigned char out[SDF_BIDX_BYTES], char *err,
441
+ size_t err_len) {
442
+ SHA256_CTX inner_ctx;
443
+ SHA256_CTX outer_ctx;
444
+ unsigned char inner[SDF_BIDX_BYTES];
445
+ int ok;
446
+
447
+ if (!prepared || !out || (!value && value_len > 0)) {
448
+ sdf_set_err(err, err_len, "invalid HMAC digest arguments");
449
+ return SDF_ERR_INVALID_ARGUMENT;
450
+ }
451
+
452
+ inner_ctx = prepared->inner0;
453
+ outer_ctx = prepared->outer0;
454
+ ok = (value_len == 0 || SHA256_Update(&inner_ctx, value, value_len) == 1) &&
455
+ SHA256_Final(inner, &inner_ctx) == 1 &&
456
+ SHA256_Update(&outer_ctx, inner, SDF_BIDX_BYTES) == 1 &&
457
+ SHA256_Final(out, &outer_ctx) == 1;
458
+
459
+ sdf_secure_clear(inner, sizeof(inner));
460
+ sdf_secure_clear(&inner_ctx, sizeof(inner_ctx));
461
+ sdf_secure_clear(&outer_ctx, sizeof(outer_ctx));
462
+ if (!ok) {
463
+ sdf_set_err(err, err_len, "HMAC-SHA256 failed");
464
+ return SDF_ERR_CRYPTO;
465
+ }
466
+ return SDF_OK;
467
+ }
468
+
469
+ void sdf_hmac_sha256_key_clear(sdf_hmac_sha256_key *prepared) {
470
+ if (prepared)
471
+ sdf_secure_clear(prepared, sizeof(*prepared));
472
+ }
473
+
474
+ sdf_status sdf_blind_index_prepared(const unsigned char *value, size_t value_len,
475
+ const sdf_hmac_sha256_key *prepared,
476
+ unsigned char out[SDF_BIDX_BYTES], char *err, size_t err_len) {
477
+ if ((!value && value_len > 0) || !out || !prepared) {
478
+ sdf_set_err(err, err_len, "invalid blind index arguments");
479
+ return SDF_ERR_INVALID_ARGUMENT;
480
+ }
481
+ return sdf_hmac_sha256_digest(prepared, value, value_len, out, err, err_len);
482
+ }
483
+
484
+ sdf_status sdf_blind_index(const unsigned char *value, size_t value_len, const unsigned char *key,
485
+ size_t key_len, unsigned char out[SDF_BIDX_BYTES], char *err,
486
+ size_t err_len) {
487
+ sdf_hmac_sha256_key prepared;
488
+ sdf_status st;
489
+
490
+ st = sdf_hmac_sha256_key_prepare(&prepared, key, key_len, err, err_len);
491
+ if (st == SDF_OK)
492
+ st = sdf_blind_index_prepared(value, value_len, &prepared, out, err, err_len);
493
+ sdf_hmac_sha256_key_clear(&prepared);
494
+ return st;
495
+ }
496
+
497
+ int sdf_is_canonical_e164(const unsigned char *value, size_t value_len) {
498
+ size_t i;
499
+ if (!value || value_len < 3 || value_len > 16)
500
+ return 0;
501
+ if (value[0] != '+')
502
+ return 0;
503
+ if (value[1] == '0')
504
+ return 0;
505
+ for (i = 1; i < value_len; i++) {
506
+ if (value[i] < '0' || value[i] > '9')
507
+ return 0;
508
+ }
509
+ return 1;
510
+ }
511
+
512
+ sdf_status sdf_phone_exact_bidx_prepared(const unsigned char *e164, size_t e164_len,
513
+ const sdf_hmac_sha256_key *prepared,
514
+ unsigned char out[SDF_BIDX_BYTES], char *err,
515
+ size_t err_len) {
516
+ if (!sdf_is_canonical_e164(e164, e164_len)) {
517
+ sdf_set_err(err, err_len, "phone must be canonical E.164, e.g. +77771234567");
518
+ return SDF_ERR_INVALID_ARGUMENT;
519
+ }
520
+ return sdf_blind_index_prepared(e164, e164_len, prepared, out, err, err_len);
521
+ }
522
+
523
+ sdf_status sdf_phone_exact_bidx(const unsigned char *e164, size_t e164_len,
524
+ const unsigned char *key, size_t key_len,
525
+ unsigned char out[SDF_BIDX_BYTES], char *err, size_t err_len) {
526
+ sdf_hmac_sha256_key prepared;
527
+ sdf_status st;
528
+
529
+ st = sdf_hmac_sha256_key_prepare(&prepared, key, key_len, err, err_len);
530
+ if (st == SDF_OK)
531
+ st = sdf_phone_exact_bidx_prepared(e164, e164_len, &prepared, out, err, err_len);
532
+ sdf_hmac_sha256_key_clear(&prepared);
533
+ return st;
534
+ }
535
+
536
+ sdf_status sdf_phone_prefix_bidx_prepared(const unsigned char *e164, size_t e164_len,
537
+ unsigned int prefix_digits,
538
+ const sdf_hmac_sha256_key *prepared,
539
+ unsigned char out[SDF_BIDX_BYTES], char *err,
540
+ size_t err_len) {
541
+ size_t prefix_len;
542
+ if (!sdf_is_canonical_e164(e164, e164_len)) {
543
+ sdf_set_err(err, err_len, "phone must be canonical E.164, e.g. +77771234567");
544
+ return SDF_ERR_INVALID_ARGUMENT;
545
+ }
546
+ if (prefix_digits == 0 || prefix_digits > 15) {
547
+ sdf_set_err(err, err_len, "prefix_digits must be between 1 and 15");
548
+ return SDF_ERR_INVALID_ARGUMENT;
549
+ }
550
+ if ((size_t)prefix_digits > e164_len - 1) {
551
+ sdf_set_err(err, err_len, "prefix_digits exceeds phone digit length");
552
+ return SDF_ERR_INVALID_ARGUMENT;
553
+ }
554
+ prefix_len = 1 + (size_t)prefix_digits;
555
+ return sdf_blind_index_prepared(e164, prefix_len, prepared, out, err, err_len);
556
+ }
557
+
558
+ sdf_status sdf_phone_prefix_bidx(const unsigned char *e164, size_t e164_len,
559
+ unsigned int prefix_digits, const unsigned char *key,
560
+ size_t key_len, unsigned char out[SDF_BIDX_BYTES], char *err,
561
+ size_t err_len) {
562
+ sdf_hmac_sha256_key prepared;
563
+ sdf_status st;
564
+
565
+ st = sdf_hmac_sha256_key_prepare(&prepared, key, key_len, err, err_len);
566
+ if (st == SDF_OK)
567
+ st = sdf_phone_prefix_bidx_prepared(e164, e164_len, prefix_digits, &prepared, out, err,
568
+ err_len);
569
+ sdf_hmac_sha256_key_clear(&prepared);
570
+ return st;
571
+ }
572
+
573
+ static sdf_status sdf_validate_key_file_permissions(const char *path, char *err, size_t err_len) {
574
+ #ifndef _WIN32
575
+ struct stat st;
576
+ if (stat(path, &st) != 0) {
577
+ sdf_set_err2(err, err_len, "cannot stat key file: ", path);
578
+ return SDF_ERR_KEY;
579
+ }
580
+ if (!S_ISREG(st.st_mode)) {
581
+ sdf_set_err2(err, err_len, "key file is not a regular file: ", path);
582
+ return SDF_ERR_KEY;
583
+ }
584
+ if ((st.st_mode & (S_IWGRP | S_IRWXO)) != 0) {
585
+ sdf_set_err2(err, err_len, "key file permissions are too open: ", path);
586
+ return SDF_ERR_KEY;
587
+ }
588
+ #else
589
+ (void)path;
590
+ (void)err;
591
+ (void)err_len;
592
+ #endif
593
+ return SDF_OK;
594
+ }
595
+
596
+ static int sdf_hex_value(char c) {
597
+ if (c >= '0' && c <= '9')
598
+ return c - '0';
599
+ if (c >= 'a' && c <= 'f')
600
+ return 10 + (c - 'a');
601
+ if (c >= 'A' && c <= 'F')
602
+ return 10 + (c - 'A');
603
+ return -1;
604
+ }
605
+
606
+ sdf_status sdf_hex_decode_32(const char *hex, unsigned char out[SDF_KEY_BYTES], char *err,
607
+ size_t err_len) {
608
+ size_t i;
609
+ if (!hex || !out) {
610
+ sdf_set_err(err, err_len, "hex key is NULL");
611
+ return SDF_ERR_INVALID_ARGUMENT;
612
+ }
613
+ if (strlen(hex) != SDF_KEY_BYTES * 2) {
614
+ sdf_set_err(err, err_len, "hex key must be exactly 64 hex characters");
615
+ return SDF_ERR_KEY;
616
+ }
617
+ for (i = 0; i < SDF_KEY_BYTES; i++) {
618
+ int hi = sdf_hex_value(hex[i * 2]);
619
+ int lo = sdf_hex_value(hex[i * 2 + 1]);
620
+ if (hi < 0 || lo < 0) {
621
+ sdf_set_err(err, err_len, "hex key contains non-hex characters");
622
+ return SDF_ERR_KEY;
623
+ }
624
+ out[i] = (unsigned char)((hi << 4) | lo);
625
+ }
626
+ return SDF_OK;
627
+ }
628
+
629
+ static char *sdf_trim(char *s) {
630
+ char *end;
631
+ while (*s && isspace((unsigned char)*s))
632
+ s++;
633
+ end = s + strlen(s);
634
+ while (end > s && isspace((unsigned char)*(end - 1)))
635
+ end--;
636
+ *end = '\0';
637
+ return s;
638
+ }
639
+
640
+ sdf_status sdf_load_key_from_env_file(const char *path, const char *name,
641
+ unsigned char out[SDF_KEY_BYTES], char *err, size_t err_len) {
642
+ FILE *fp = NULL;
643
+ char line[SDF_MAX_KEY_LINE];
644
+ const char *effective_path = path && path[0] ? path : SDF_DEFAULT_KEY_FILE;
645
+ sdf_status st = SDF_ERR_KEY;
646
+
647
+ memset(line, 0, sizeof(line));
648
+ if (!name || !out) {
649
+ sdf_set_err(err, err_len, "invalid key lookup arguments");
650
+ return SDF_ERR_INVALID_ARGUMENT;
651
+ }
652
+ if (sdf_validate_key_file_permissions(effective_path, err, err_len) != SDF_OK)
653
+ return SDF_ERR_KEY;
654
+
655
+ fp = fopen(effective_path, "r");
656
+ if (!fp) {
657
+ sdf_set_err2(err, err_len, "cannot open key file: ", effective_path);
658
+ return SDF_ERR_KEY;
659
+ }
660
+
661
+ while (fgets(line, sizeof(line), fp)) {
662
+ char *p = sdf_trim(line);
663
+ char *eq;
664
+ if (*p == '\0' || *p == '#')
665
+ goto next_line;
666
+ eq = strchr(p, '=');
667
+ if (!eq)
668
+ goto next_line;
669
+ *eq = '\0';
670
+ if (strcmp(sdf_trim(p), name) == 0) {
671
+ char *value = sdf_trim(eq + 1);
672
+ st = sdf_hex_decode_32(value, out, err, err_len);
673
+ goto done;
674
+ }
675
+ next_line:
676
+ sdf_secure_clear(line, sizeof(line));
677
+ }
678
+
679
+ sdf_set_err2(err, err_len, "key not found in key file: ", name);
680
+
681
+ done:
682
+ sdf_secure_clear(line, sizeof(line));
683
+ if (fp)
684
+ fclose(fp);
685
+ return st;
686
+ }
687
+
688
+ sdf_status sdf_load_encryption_key(uint32_t key_id, const char *path,
689
+ unsigned char out[SDF_KEY_BYTES], char *err, size_t err_len) {
690
+ char name[64];
691
+ sdf_status st;
692
+ snprintf(name, sizeof(name), "SDF_ENC_KEY_%u_HEX", key_id);
693
+ st = sdf_load_key_from_env_file(path, name, out, err, err_len);
694
+ if (st == SDF_OK)
695
+ return st;
696
+ if (key_id == 1) {
697
+ return sdf_load_key_from_env_file(path, "SDF_ENC_KEY_HEX", out, err, err_len);
698
+ }
699
+ return st;
700
+ }
701
+
702
+ sdf_status sdf_load_bidx_key(const char *path, const char *domain, unsigned char out[SDF_KEY_BYTES],
703
+ char *err, size_t err_len) {
704
+ char name[128];
705
+ size_t i;
706
+ if (!domain || !domain[0]) {
707
+ return sdf_load_key_from_env_file(path, "SDF_BIDX_KEY_HEX", out, err, err_len);
708
+ }
709
+ if (strlen(domain) > 80) {
710
+ sdf_set_err(err, err_len, "bidx domain is too long");
711
+ return SDF_ERR_KEY;
712
+ }
713
+ snprintf(name, sizeof(name), "SDF_BIDX_%s_KEY_HEX", domain);
714
+ for (i = 0; name[i]; i++) {
715
+ if (name[i] >= 'a' && name[i] <= 'z')
716
+ name[i] = (char)(name[i] - 32);
717
+ if (!(name[i] == '_' || (name[i] >= 'A' && name[i] <= 'Z') ||
718
+ (name[i] >= '0' && name[i] <= '9'))) {
719
+ name[i] = '_';
720
+ }
721
+ }
722
+ return sdf_load_key_from_env_file(path, name, out, err, err_len);
723
+ }