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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +98 -0
- data/Rakefile +16 -0
- data/db_deployment/mysql/Makefile +62 -0
- data/db_deployment/mysql/README.md +33 -0
- data/db_deployment/mysql/bin/common +199 -0
- data/db_deployment/mysql/bin/disable +40 -0
- data/db_deployment/mysql/bin/doctor +57 -0
- data/db_deployment/mysql/bin/enable +56 -0
- data/db_deployment/mysql/bin/install +64 -0
- data/db_deployment/mysql/bin/status +11 -0
- data/db_deployment/mysql/bin/uninstall +46 -0
- data/db_deployment/mysql/bin/verify +27 -0
- data/db_deployment/mysql/sql/examples.sql +20 -0
- data/db_deployment/mysql/sql/install.sql +20 -0
- data/db_deployment/mysql/sql/uninstall.sql +8 -0
- data/docs/final_decision.md +202 -0
- data/docs/implementation_notes.md +63 -0
- data/docs/ruby_27_mysql_57.md +33 -0
- data/examples/clients_phone_migration.sql +6 -0
- data/examples/keys.env.example +6 -0
- data/examples/ruby_usage.rb +15 -0
- data/exe/secure_db_fields +49 -0
- data/ext/secure_db_fields/extconf.rb +21 -0
- data/ext/secure_db_fields/secure_db_fields_core.c +723 -0
- data/ext/secure_db_fields/secure_db_fields_core.h +140 -0
- data/ext/secure_db_fields/secure_db_fields_ext.c +426 -0
- data/lib/secure_db_fields/active_record.rb +58 -0
- data/lib/secure_db_fields/crypto.rb +115 -0
- data/lib/secure_db_fields/db_deployment.rb +285 -0
- data/lib/secure_db_fields/keys.rb +69 -0
- data/lib/secure_db_fields/phone.rb +17 -0
- data/lib/secure_db_fields/version.rb +5 -0
- data/lib/secure_db_fields.rb +8 -0
- data/mysql_udf/Makefile +25 -0
- data/mysql_udf/README.md +51 -0
- data/mysql_udf/bin/install +11 -0
- data/mysql_udf/bin/uninstall +4 -0
- data/mysql_udf/bin/verify +3 -0
- data/mysql_udf/sql/examples.sql +20 -0
- data/mysql_udf/sql/install.sql +20 -0
- data/mysql_udf/sql/uninstall.sql +8 -0
- data/mysql_udf/src/mysql_udf_abi_57.h +34 -0
- data/mysql_udf/src/secure_db_fields_mysql.c +513 -0
- data/mysql_udf/test/run_e2e.sh +216 -0
- metadata +137 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#ifndef SECURE_DB_FIELDS_CORE_H
|
|
2
|
+
#define SECURE_DB_FIELDS_CORE_H
|
|
3
|
+
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
|
|
7
|
+
#ifndef OPENSSL_SUPPRESS_DEPRECATED
|
|
8
|
+
#define OPENSSL_SUPPRESS_DEPRECATED 1
|
|
9
|
+
#endif
|
|
10
|
+
#include <openssl/sha.h>
|
|
11
|
+
|
|
12
|
+
#define SDF_VERSION "0.1.1"
|
|
13
|
+
|
|
14
|
+
#define SDF_MAGIC "MCEN"
|
|
15
|
+
#define SDF_MAGIC_LEN 4
|
|
16
|
+
#define SDF_ENVELOPE_VERSION 1
|
|
17
|
+
#define SDF_ALG_AES_256_GCM 1
|
|
18
|
+
#define SDF_KEY_BYTES 32
|
|
19
|
+
#define SDF_GCM_NONCE_BYTES 12
|
|
20
|
+
#define SDF_GCM_TAG_BYTES 16
|
|
21
|
+
#define SDF_BIDX_BYTES 32
|
|
22
|
+
#define SDF_ROW_UID_BYTES 16
|
|
23
|
+
#define SDF_HEADER_BYTES (SDF_MAGIC_LEN + 1 + 1 + 4 + SDF_GCM_NONCE_BYTES + SDF_GCM_TAG_BYTES)
|
|
24
|
+
#define SDF_MAX_ERR 256
|
|
25
|
+
#define SDF_MAX_KEY_LINE 512
|
|
26
|
+
#define SDF_DEFAULT_KEY_FILE "/etc/secure_db_fields/keys.env"
|
|
27
|
+
#define SDF_HMAC_SHA256_BLOCK_BYTES 64
|
|
28
|
+
|
|
29
|
+
#ifdef __cplusplus
|
|
30
|
+
extern "C" {
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
typedef struct {
|
|
34
|
+
SHA256_CTX inner0;
|
|
35
|
+
SHA256_CTX outer0;
|
|
36
|
+
} sdf_hmac_sha256_key;
|
|
37
|
+
|
|
38
|
+
typedef enum {
|
|
39
|
+
SDF_OK = 0,
|
|
40
|
+
SDF_ERR_INVALID_ARGUMENT = 1,
|
|
41
|
+
SDF_ERR_NO_MEMORY = 2,
|
|
42
|
+
SDF_ERR_RANDOM = 3,
|
|
43
|
+
SDF_ERR_CRYPTO = 4,
|
|
44
|
+
SDF_ERR_AUTH = 5,
|
|
45
|
+
SDF_ERR_FORMAT = 6,
|
|
46
|
+
SDF_ERR_KEY = 7,
|
|
47
|
+
SDF_ERR_UNSUPPORTED = 8
|
|
48
|
+
} sdf_status;
|
|
49
|
+
|
|
50
|
+
const char *sdf_status_name(sdf_status status);
|
|
51
|
+
|
|
52
|
+
size_t sdf_encrypt_aes_256_gcm_output_len(size_t plaintext_len);
|
|
53
|
+
|
|
54
|
+
sdf_status sdf_encrypt_aes_256_gcm_into(const unsigned char *plaintext, size_t plaintext_len,
|
|
55
|
+
const unsigned char *key, size_t key_len,
|
|
56
|
+
const unsigned char *aad, size_t aad_len, uint32_t key_id,
|
|
57
|
+
unsigned char *out, size_t out_cap, size_t *out_len,
|
|
58
|
+
char *err, size_t err_len);
|
|
59
|
+
|
|
60
|
+
sdf_status sdf_encrypt_aes_256_gcm(const unsigned char *plaintext, size_t plaintext_len,
|
|
61
|
+
const unsigned char *key, size_t key_len,
|
|
62
|
+
const unsigned char *aad, size_t aad_len, uint32_t key_id,
|
|
63
|
+
unsigned char **out, size_t *out_len, char *err, size_t err_len);
|
|
64
|
+
|
|
65
|
+
size_t sdf_decrypt_aes_256_gcm_output_cap(const unsigned char *envelope, size_t envelope_len);
|
|
66
|
+
|
|
67
|
+
sdf_status sdf_decrypt_aes_256_gcm_into(const unsigned char *envelope, size_t envelope_len,
|
|
68
|
+
const unsigned char *key, size_t key_len,
|
|
69
|
+
const unsigned char *aad, size_t aad_len,
|
|
70
|
+
unsigned char *out, size_t out_cap, size_t *out_len,
|
|
71
|
+
char *err, size_t err_len);
|
|
72
|
+
|
|
73
|
+
sdf_status sdf_decrypt_aes_256_gcm(const unsigned char *envelope, size_t envelope_len,
|
|
74
|
+
const unsigned char *key, size_t key_len,
|
|
75
|
+
const unsigned char *aad, size_t aad_len, unsigned char **out,
|
|
76
|
+
size_t *out_len, char *err, size_t err_len);
|
|
77
|
+
|
|
78
|
+
sdf_status sdf_parse_key_id(const unsigned char *envelope, size_t envelope_len, uint32_t *key_id,
|
|
79
|
+
char *err, size_t err_len);
|
|
80
|
+
|
|
81
|
+
int sdf_is_valid_envelope(const unsigned char *envelope, size_t envelope_len);
|
|
82
|
+
|
|
83
|
+
sdf_status sdf_hmac_sha256_key_prepare(sdf_hmac_sha256_key *prepared, const unsigned char *key,
|
|
84
|
+
size_t key_len, char *err, size_t err_len);
|
|
85
|
+
|
|
86
|
+
sdf_status sdf_hmac_sha256_digest(const sdf_hmac_sha256_key *prepared, const unsigned char *value,
|
|
87
|
+
size_t value_len, unsigned char out[SDF_BIDX_BYTES], char *err,
|
|
88
|
+
size_t err_len);
|
|
89
|
+
|
|
90
|
+
void sdf_hmac_sha256_key_clear(sdf_hmac_sha256_key *prepared);
|
|
91
|
+
|
|
92
|
+
sdf_status sdf_blind_index_prepared(const unsigned char *value, size_t value_len,
|
|
93
|
+
const sdf_hmac_sha256_key *prepared,
|
|
94
|
+
unsigned char out[SDF_BIDX_BYTES], char *err, size_t err_len);
|
|
95
|
+
|
|
96
|
+
sdf_status sdf_blind_index(const unsigned char *value, size_t value_len, const unsigned char *key,
|
|
97
|
+
size_t key_len, unsigned char out[SDF_BIDX_BYTES], char *err,
|
|
98
|
+
size_t err_len);
|
|
99
|
+
|
|
100
|
+
int sdf_is_canonical_e164(const unsigned char *value, size_t value_len);
|
|
101
|
+
|
|
102
|
+
sdf_status sdf_phone_exact_bidx_prepared(const unsigned char *e164, size_t e164_len,
|
|
103
|
+
const sdf_hmac_sha256_key *prepared,
|
|
104
|
+
unsigned char out[SDF_BIDX_BYTES], char *err,
|
|
105
|
+
size_t err_len);
|
|
106
|
+
|
|
107
|
+
sdf_status sdf_phone_exact_bidx(const unsigned char *e164, size_t e164_len,
|
|
108
|
+
const unsigned char *key, size_t key_len,
|
|
109
|
+
unsigned char out[SDF_BIDX_BYTES], char *err, size_t err_len);
|
|
110
|
+
|
|
111
|
+
sdf_status sdf_phone_prefix_bidx_prepared(const unsigned char *e164, size_t e164_len,
|
|
112
|
+
unsigned int prefix_digits,
|
|
113
|
+
const sdf_hmac_sha256_key *prepared,
|
|
114
|
+
unsigned char out[SDF_BIDX_BYTES], char *err,
|
|
115
|
+
size_t err_len);
|
|
116
|
+
|
|
117
|
+
sdf_status sdf_phone_prefix_bidx(const unsigned char *e164, size_t e164_len,
|
|
118
|
+
unsigned int prefix_digits, const unsigned char *key,
|
|
119
|
+
size_t key_len, unsigned char out[SDF_BIDX_BYTES], char *err,
|
|
120
|
+
size_t err_len);
|
|
121
|
+
|
|
122
|
+
sdf_status sdf_hex_decode_32(const char *hex, unsigned char out[SDF_KEY_BYTES], char *err,
|
|
123
|
+
size_t err_len);
|
|
124
|
+
|
|
125
|
+
sdf_status sdf_load_key_from_env_file(const char *path, const char *name,
|
|
126
|
+
unsigned char out[SDF_KEY_BYTES], char *err, size_t err_len);
|
|
127
|
+
|
|
128
|
+
sdf_status sdf_load_encryption_key(uint32_t key_id, const char *path,
|
|
129
|
+
unsigned char out[SDF_KEY_BYTES], char *err, size_t err_len);
|
|
130
|
+
|
|
131
|
+
sdf_status sdf_load_bidx_key(const char *path, const char *domain, unsigned char out[SDF_KEY_BYTES],
|
|
132
|
+
char *err, size_t err_len);
|
|
133
|
+
|
|
134
|
+
void sdf_secure_clear(void *ptr, size_t len);
|
|
135
|
+
|
|
136
|
+
#ifdef __cplusplus
|
|
137
|
+
}
|
|
138
|
+
#endif
|
|
139
|
+
|
|
140
|
+
#endif
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#define OPENSSL_SUPPRESS_DEPRECATED 1
|
|
2
|
+
#include <ruby.h>
|
|
3
|
+
#include <stdint.h>
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
#include <limits.h>
|
|
7
|
+
|
|
8
|
+
#include "secure_db_fields_core.h"
|
|
9
|
+
|
|
10
|
+
static VALUE mSecureDBFields;
|
|
11
|
+
static VALUE mNative;
|
|
12
|
+
static VALUE eError;
|
|
13
|
+
static VALUE eAuthError;
|
|
14
|
+
static VALUE eFormatError;
|
|
15
|
+
static VALUE eKeyError;
|
|
16
|
+
|
|
17
|
+
static void rb_sdf_raise(sdf_status st, const char *err) {
|
|
18
|
+
VALUE klass = eError;
|
|
19
|
+
if (st == SDF_ERR_AUTH)
|
|
20
|
+
klass = eAuthError;
|
|
21
|
+
else if (st == SDF_ERR_FORMAT)
|
|
22
|
+
klass = eFormatError;
|
|
23
|
+
else if (st == SDF_ERR_KEY)
|
|
24
|
+
klass = eKeyError;
|
|
25
|
+
rb_raise(klass, "%s: %s", sdf_status_name(st), err && err[0] ? err : "unknown error");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static void rb_sdf_assert_array_lengths(VALUE values, VALUE aads) {
|
|
29
|
+
Check_Type(values, T_ARRAY);
|
|
30
|
+
Check_Type(aads, T_ARRAY);
|
|
31
|
+
if (RARRAY_LEN(values) != RARRAY_LEN(aads))
|
|
32
|
+
rb_raise(rb_eArgError, "values and aads must have the same length");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static void rb_sdf_assert_key_value(VALUE *key) {
|
|
36
|
+
StringValue(*key);
|
|
37
|
+
if (RSTRING_LEN(*key) != SDF_KEY_BYTES)
|
|
38
|
+
rb_raise(eKeyError, "key_error: key must be exactly 32 bytes");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static uint32_t rb_sdf_key_id_from_value(VALUE key_id_value) {
|
|
42
|
+
unsigned long id = NUM2ULONG(key_id_value);
|
|
43
|
+
if (id == 0 || id > UINT32_MAX)
|
|
44
|
+
rb_raise(rb_eArgError, "key_id must be between 1 and 2^32-1");
|
|
45
|
+
return (uint32_t)id;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static void rb_sdf_hmac_key_init(sdf_hmac_sha256_key *prepared, VALUE key) {
|
|
49
|
+
char err[SDF_MAX_ERR];
|
|
50
|
+
sdf_status st;
|
|
51
|
+
|
|
52
|
+
rb_sdf_assert_key_value(&key);
|
|
53
|
+
st = sdf_hmac_sha256_key_prepare(prepared, (const unsigned char *)RSTRING_PTR(key),
|
|
54
|
+
(size_t)RSTRING_LEN(key), err, sizeof(err));
|
|
55
|
+
if (st != SDF_OK)
|
|
56
|
+
rb_sdf_raise(st, err);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static VALUE rb_sdf_bidx_string(const unsigned char out[SDF_BIDX_BYTES]) {
|
|
60
|
+
return rb_str_new((const char *)out, SDF_BIDX_BYTES);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static VALUE rb_sdf_version(VALUE self) {
|
|
64
|
+
(void)self;
|
|
65
|
+
return rb_str_new_cstr(SDF_VERSION);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static VALUE rb_sdf_encrypt_one(VALUE plaintext, VALUE key, VALUE aad, uint32_t key_id) {
|
|
69
|
+
char err[SDF_MAX_ERR];
|
|
70
|
+
sdf_status st;
|
|
71
|
+
size_t out_len = 0;
|
|
72
|
+
size_t out_cap;
|
|
73
|
+
VALUE result;
|
|
74
|
+
|
|
75
|
+
StringValue(plaintext);
|
|
76
|
+
StringValue(aad);
|
|
77
|
+
out_cap = sdf_encrypt_aes_256_gcm_output_len((size_t)RSTRING_LEN(plaintext));
|
|
78
|
+
if (out_cap > (size_t)LONG_MAX)
|
|
79
|
+
rb_raise(rb_eArgError, "plaintext too large");
|
|
80
|
+
result = rb_str_new(NULL, (long)out_cap);
|
|
81
|
+
|
|
82
|
+
st = sdf_encrypt_aes_256_gcm_into(
|
|
83
|
+
(const unsigned char *)RSTRING_PTR(plaintext), (size_t)RSTRING_LEN(plaintext),
|
|
84
|
+
(const unsigned char *)RSTRING_PTR(key), (size_t)RSTRING_LEN(key),
|
|
85
|
+
(const unsigned char *)RSTRING_PTR(aad), (size_t)RSTRING_LEN(aad), key_id,
|
|
86
|
+
(unsigned char *)RSTRING_PTR(result), out_cap, &out_len, err, sizeof(err));
|
|
87
|
+
if (st != SDF_OK)
|
|
88
|
+
rb_sdf_raise(st, err);
|
|
89
|
+
if (out_len != out_cap)
|
|
90
|
+
rb_str_resize(result, (long)out_len);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static VALUE rb_sdf_decrypt_one(VALUE envelope, VALUE key, VALUE aad) {
|
|
95
|
+
char err[SDF_MAX_ERR];
|
|
96
|
+
sdf_status st;
|
|
97
|
+
size_t out_cap;
|
|
98
|
+
size_t out_len = 0;
|
|
99
|
+
VALUE result;
|
|
100
|
+
|
|
101
|
+
StringValue(envelope);
|
|
102
|
+
StringValue(aad);
|
|
103
|
+
out_cap = sdf_decrypt_aes_256_gcm_output_cap((const unsigned char *)RSTRING_PTR(envelope),
|
|
104
|
+
(size_t)RSTRING_LEN(envelope));
|
|
105
|
+
if (out_cap == 0 && !sdf_is_valid_envelope((const unsigned char *)RSTRING_PTR(envelope),
|
|
106
|
+
(size_t)RSTRING_LEN(envelope))) {
|
|
107
|
+
rb_raise(eFormatError, "invalid_envelope: invalid MCEN envelope");
|
|
108
|
+
}
|
|
109
|
+
if (out_cap > (size_t)LONG_MAX)
|
|
110
|
+
rb_raise(rb_eArgError, "ciphertext too large");
|
|
111
|
+
|
|
112
|
+
result = rb_str_new(NULL, (long)out_cap);
|
|
113
|
+
st = sdf_decrypt_aes_256_gcm_into(
|
|
114
|
+
(const unsigned char *)RSTRING_PTR(envelope), (size_t)RSTRING_LEN(envelope),
|
|
115
|
+
(const unsigned char *)RSTRING_PTR(key), (size_t)RSTRING_LEN(key),
|
|
116
|
+
(const unsigned char *)RSTRING_PTR(aad), (size_t)RSTRING_LEN(aad),
|
|
117
|
+
(unsigned char *)RSTRING_PTR(result), out_cap, &out_len, err, sizeof(err));
|
|
118
|
+
if (st != SDF_OK) {
|
|
119
|
+
if (out_cap > 0)
|
|
120
|
+
sdf_secure_clear((void *)RSTRING_PTR(result), out_cap);
|
|
121
|
+
rb_sdf_raise(st, err);
|
|
122
|
+
}
|
|
123
|
+
if (out_len != out_cap)
|
|
124
|
+
rb_str_resize(result, (long)out_len);
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static VALUE rb_sdf_encrypt(int argc, VALUE *argv, VALUE self) {
|
|
129
|
+
VALUE plaintext, key, aad, key_id_value;
|
|
130
|
+
uint32_t key_id = 1;
|
|
131
|
+
(void)self;
|
|
132
|
+
|
|
133
|
+
rb_scan_args(argc, argv, "31", &plaintext, &key, &aad, &key_id_value);
|
|
134
|
+
rb_sdf_assert_key_value(&key);
|
|
135
|
+
if (!NIL_P(key_id_value))
|
|
136
|
+
key_id = rb_sdf_key_id_from_value(key_id_value);
|
|
137
|
+
return rb_sdf_encrypt_one(plaintext, key, aad, key_id);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static VALUE rb_sdf_decrypt(VALUE self, VALUE envelope, VALUE key, VALUE aad) {
|
|
141
|
+
(void)self;
|
|
142
|
+
rb_sdf_assert_key_value(&key);
|
|
143
|
+
return rb_sdf_decrypt_one(envelope, key, aad);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static VALUE rb_sdf_key_id(VALUE self, VALUE envelope) {
|
|
147
|
+
uint32_t key_id = 0;
|
|
148
|
+
char err[SDF_MAX_ERR];
|
|
149
|
+
sdf_status st;
|
|
150
|
+
(void)self;
|
|
151
|
+
StringValue(envelope);
|
|
152
|
+
st = sdf_parse_key_id((const unsigned char *)RSTRING_PTR(envelope),
|
|
153
|
+
(size_t)RSTRING_LEN(envelope), &key_id, err, sizeof(err));
|
|
154
|
+
if (st != SDF_OK)
|
|
155
|
+
rb_sdf_raise(st, err);
|
|
156
|
+
return UINT2NUM(key_id);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static VALUE rb_sdf_valid_envelope(VALUE self, VALUE envelope) {
|
|
160
|
+
(void)self;
|
|
161
|
+
StringValue(envelope);
|
|
162
|
+
return sdf_is_valid_envelope((const unsigned char *)RSTRING_PTR(envelope),
|
|
163
|
+
(size_t)RSTRING_LEN(envelope))
|
|
164
|
+
? Qtrue
|
|
165
|
+
: Qfalse;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static VALUE rb_sdf_blind_index(VALUE self, VALUE value, VALUE key) {
|
|
169
|
+
unsigned char out[SDF_BIDX_BYTES];
|
|
170
|
+
char err[SDF_MAX_ERR];
|
|
171
|
+
sdf_status st;
|
|
172
|
+
(void)self;
|
|
173
|
+
StringValue(value);
|
|
174
|
+
rb_sdf_assert_key_value(&key);
|
|
175
|
+
st = sdf_blind_index((const unsigned char *)RSTRING_PTR(value), (size_t)RSTRING_LEN(value),
|
|
176
|
+
(const unsigned char *)RSTRING_PTR(key), (size_t)RSTRING_LEN(key), out,
|
|
177
|
+
err, sizeof(err));
|
|
178
|
+
if (st != SDF_OK)
|
|
179
|
+
rb_sdf_raise(st, err);
|
|
180
|
+
return rb_sdf_bidx_string(out);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
static VALUE rb_sdf_e164_p(VALUE self, VALUE value) {
|
|
184
|
+
(void)self;
|
|
185
|
+
StringValue(value);
|
|
186
|
+
return sdf_is_canonical_e164((const unsigned char *)RSTRING_PTR(value),
|
|
187
|
+
(size_t)RSTRING_LEN(value))
|
|
188
|
+
? Qtrue
|
|
189
|
+
: Qfalse;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static VALUE rb_sdf_phone_bidx(VALUE self, VALUE e164, VALUE key) {
|
|
193
|
+
unsigned char out[SDF_BIDX_BYTES];
|
|
194
|
+
char err[SDF_MAX_ERR];
|
|
195
|
+
sdf_status st;
|
|
196
|
+
(void)self;
|
|
197
|
+
StringValue(e164);
|
|
198
|
+
rb_sdf_assert_key_value(&key);
|
|
199
|
+
st = sdf_phone_exact_bidx((const unsigned char *)RSTRING_PTR(e164), (size_t)RSTRING_LEN(e164),
|
|
200
|
+
(const unsigned char *)RSTRING_PTR(key), (size_t)RSTRING_LEN(key),
|
|
201
|
+
out, err, sizeof(err));
|
|
202
|
+
if (st != SDF_OK)
|
|
203
|
+
rb_sdf_raise(st, err);
|
|
204
|
+
return rb_sdf_bidx_string(out);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static VALUE rb_sdf_phone_prefix_bidx(VALUE self, VALUE e164, VALUE prefix_digits_value,
|
|
208
|
+
VALUE key) {
|
|
209
|
+
unsigned char out[SDF_BIDX_BYTES];
|
|
210
|
+
char err[SDF_MAX_ERR];
|
|
211
|
+
sdf_status st;
|
|
212
|
+
unsigned long prefix_digits;
|
|
213
|
+
(void)self;
|
|
214
|
+
StringValue(e164);
|
|
215
|
+
rb_sdf_assert_key_value(&key);
|
|
216
|
+
prefix_digits = NUM2ULONG(prefix_digits_value);
|
|
217
|
+
if (prefix_digits == 0 || prefix_digits > 15)
|
|
218
|
+
rb_raise(rb_eArgError, "prefix_digits must be between 1 and 15");
|
|
219
|
+
st = sdf_phone_prefix_bidx((const unsigned char *)RSTRING_PTR(e164), (size_t)RSTRING_LEN(e164),
|
|
220
|
+
(unsigned int)prefix_digits, (const unsigned char *)RSTRING_PTR(key),
|
|
221
|
+
(size_t)RSTRING_LEN(key), out, err, sizeof(err));
|
|
222
|
+
if (st != SDF_OK)
|
|
223
|
+
rb_sdf_raise(st, err);
|
|
224
|
+
return rb_sdf_bidx_string(out);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
static void rb_sdf_assert_packed_len(long n) {
|
|
228
|
+
if (n < 0 || n > LONG_MAX / SDF_BIDX_BYTES)
|
|
229
|
+
rb_raise(rb_eArgError, "too many values for packed blind index output");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
typedef enum {
|
|
233
|
+
SDF_RB_BIDX_RAW = 0,
|
|
234
|
+
SDF_RB_BIDX_PHONE = 1,
|
|
235
|
+
SDF_RB_BIDX_PHONE_PREFIX = 2
|
|
236
|
+
} sdf_rb_bidx_kind;
|
|
237
|
+
|
|
238
|
+
static void rb_sdf_bidx_raise(sdf_status st, const char *err, sdf_hmac_sha256_key *prepared,
|
|
239
|
+
unsigned char *packed, size_t packed_len,
|
|
240
|
+
unsigned char out[SDF_BIDX_BYTES]) {
|
|
241
|
+
if (packed && packed_len > 0)
|
|
242
|
+
sdf_secure_clear(packed, packed_len);
|
|
243
|
+
if (out)
|
|
244
|
+
sdf_secure_clear(out, SDF_BIDX_BYTES);
|
|
245
|
+
sdf_hmac_sha256_key_clear(prepared);
|
|
246
|
+
if (st == SDF_ERR_INVALID_ARGUMENT)
|
|
247
|
+
rb_raise(rb_eArgError, "%s", err && err[0] ? err : "invalid argument");
|
|
248
|
+
rb_sdf_raise(st, err);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
static VALUE rb_sdf_bidx_many_impl(VALUE values, VALUE key, int packed, sdf_rb_bidx_kind kind,
|
|
252
|
+
unsigned long prefix_digits) {
|
|
253
|
+
long i, n;
|
|
254
|
+
VALUE result;
|
|
255
|
+
sdf_hmac_sha256_key prepared;
|
|
256
|
+
unsigned char out[SDF_BIDX_BYTES];
|
|
257
|
+
unsigned char *dst = NULL;
|
|
258
|
+
size_t packed_len = 0;
|
|
259
|
+
|
|
260
|
+
Check_Type(values, T_ARRAY);
|
|
261
|
+
if (kind == SDF_RB_BIDX_PHONE_PREFIX && (prefix_digits == 0 || prefix_digits > 15))
|
|
262
|
+
rb_raise(rb_eArgError, "prefix_digits must be between 1 and 15");
|
|
263
|
+
|
|
264
|
+
rb_sdf_hmac_key_init(&prepared, key);
|
|
265
|
+
n = RARRAY_LEN(values);
|
|
266
|
+
if (packed) {
|
|
267
|
+
rb_sdf_assert_packed_len(n);
|
|
268
|
+
packed_len = (size_t)n * SDF_BIDX_BYTES;
|
|
269
|
+
result = rb_str_new(NULL, (long)packed_len);
|
|
270
|
+
dst = (unsigned char *)RSTRING_PTR(result);
|
|
271
|
+
} else {
|
|
272
|
+
result = rb_ary_new_capa(n);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (i = 0; i < n; i++) {
|
|
276
|
+
VALUE value = rb_ary_entry(values, i);
|
|
277
|
+
unsigned char *target = packed ? dst + ((size_t)i * SDF_BIDX_BYTES) : out;
|
|
278
|
+
char err[SDF_MAX_ERR];
|
|
279
|
+
sdf_status st;
|
|
280
|
+
StringValue(value);
|
|
281
|
+
if (kind == SDF_RB_BIDX_PHONE) {
|
|
282
|
+
st = sdf_phone_exact_bidx_prepared((const unsigned char *)RSTRING_PTR(value),
|
|
283
|
+
(size_t)RSTRING_LEN(value), &prepared, target, err,
|
|
284
|
+
sizeof(err));
|
|
285
|
+
} else if (kind == SDF_RB_BIDX_PHONE_PREFIX) {
|
|
286
|
+
st = sdf_phone_prefix_bidx_prepared(
|
|
287
|
+
(const unsigned char *)RSTRING_PTR(value), (size_t)RSTRING_LEN(value),
|
|
288
|
+
(unsigned int)prefix_digits, &prepared, target, err, sizeof(err));
|
|
289
|
+
} else {
|
|
290
|
+
st = sdf_blind_index_prepared((const unsigned char *)RSTRING_PTR(value),
|
|
291
|
+
(size_t)RSTRING_LEN(value), &prepared, target, err,
|
|
292
|
+
sizeof(err));
|
|
293
|
+
}
|
|
294
|
+
if (st != SDF_OK)
|
|
295
|
+
rb_sdf_bidx_raise(st, err, &prepared, dst, packed_len, packed ? NULL : out);
|
|
296
|
+
if (!packed)
|
|
297
|
+
rb_ary_push(result, rb_sdf_bidx_string(out));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!packed)
|
|
301
|
+
sdf_secure_clear(out, sizeof(out));
|
|
302
|
+
sdf_hmac_sha256_key_clear(&prepared);
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
static VALUE rb_sdf_blind_index_many(VALUE self, VALUE values, VALUE key) {
|
|
307
|
+
(void)self;
|
|
308
|
+
return rb_sdf_bidx_many_impl(values, key, 0, SDF_RB_BIDX_RAW, 0);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
static VALUE rb_sdf_blind_index_many_packed(VALUE self, VALUE values, VALUE key) {
|
|
312
|
+
(void)self;
|
|
313
|
+
return rb_sdf_bidx_many_impl(values, key, 1, SDF_RB_BIDX_RAW, 0);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
static VALUE rb_sdf_phone_bidx_many(VALUE self, VALUE values, VALUE key) {
|
|
317
|
+
(void)self;
|
|
318
|
+
return rb_sdf_bidx_many_impl(values, key, 0, SDF_RB_BIDX_PHONE, 0);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
static VALUE rb_sdf_phone_bidx_many_packed(VALUE self, VALUE values, VALUE key) {
|
|
322
|
+
(void)self;
|
|
323
|
+
return rb_sdf_bidx_many_impl(values, key, 1, SDF_RB_BIDX_PHONE, 0);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
static VALUE rb_sdf_phone_prefix_bidx_many(VALUE self, VALUE values, VALUE prefix_digits_value,
|
|
327
|
+
VALUE key) {
|
|
328
|
+
(void)self;
|
|
329
|
+
return rb_sdf_bidx_many_impl(values, key, 0, SDF_RB_BIDX_PHONE_PREFIX,
|
|
330
|
+
NUM2ULONG(prefix_digits_value));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
static VALUE rb_sdf_phone_prefix_bidx_many_packed(VALUE self, VALUE values,
|
|
334
|
+
VALUE prefix_digits_value, VALUE key) {
|
|
335
|
+
(void)self;
|
|
336
|
+
return rb_sdf_bidx_many_impl(values, key, 1, SDF_RB_BIDX_PHONE_PREFIX,
|
|
337
|
+
NUM2ULONG(prefix_digits_value));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
static VALUE rb_sdf_encrypt_many(VALUE self, VALUE values, VALUE key, VALUE aads,
|
|
341
|
+
VALUE key_id_value) {
|
|
342
|
+
long i, n;
|
|
343
|
+
VALUE result;
|
|
344
|
+
uint32_t key_id;
|
|
345
|
+
(void)self;
|
|
346
|
+
|
|
347
|
+
rb_sdf_assert_array_lengths(values, aads);
|
|
348
|
+
rb_sdf_assert_key_value(&key);
|
|
349
|
+
key_id = rb_sdf_key_id_from_value(key_id_value);
|
|
350
|
+
|
|
351
|
+
n = RARRAY_LEN(values);
|
|
352
|
+
result = rb_ary_new_capa(n);
|
|
353
|
+
for (i = 0; i < n; i++)
|
|
354
|
+
rb_ary_push(result, rb_sdf_encrypt_one(rb_ary_entry(values, i), key, rb_ary_entry(aads, i),
|
|
355
|
+
key_id));
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
static VALUE rb_sdf_decrypt_many(VALUE self, VALUE envelopes, VALUE key, VALUE aads) {
|
|
360
|
+
long i, n;
|
|
361
|
+
VALUE result;
|
|
362
|
+
(void)self;
|
|
363
|
+
|
|
364
|
+
rb_sdf_assert_array_lengths(envelopes, aads);
|
|
365
|
+
rb_sdf_assert_key_value(&key);
|
|
366
|
+
|
|
367
|
+
n = RARRAY_LEN(envelopes);
|
|
368
|
+
result = rb_ary_new_capa(n);
|
|
369
|
+
for (i = 0; i < n; i++)
|
|
370
|
+
rb_ary_push(result,
|
|
371
|
+
rb_sdf_decrypt_one(rb_ary_entry(envelopes, i), key, rb_ary_entry(aads, i)));
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
static VALUE rb_sdf_hex_decode_key(VALUE self, VALUE hex) {
|
|
376
|
+
unsigned char out[SDF_KEY_BYTES];
|
|
377
|
+
char err[SDF_MAX_ERR];
|
|
378
|
+
sdf_status st;
|
|
379
|
+
(void)self;
|
|
380
|
+
StringValue(hex);
|
|
381
|
+
st = sdf_hex_decode_32(StringValueCStr(hex), out, err, sizeof(err));
|
|
382
|
+
if (st != SDF_OK)
|
|
383
|
+
rb_sdf_raise(st, err);
|
|
384
|
+
return rb_str_new((const char *)out, SDF_KEY_BYTES);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
typedef struct {
|
|
388
|
+
const char *name;
|
|
389
|
+
VALUE (*func)(ANYARGS);
|
|
390
|
+
int arity;
|
|
391
|
+
} rb_sdf_method;
|
|
392
|
+
|
|
393
|
+
static const rb_sdf_method rb_sdf_methods[] = {
|
|
394
|
+
{"version", rb_sdf_version, 0},
|
|
395
|
+
{"encrypt", rb_sdf_encrypt, -1},
|
|
396
|
+
{"decrypt", rb_sdf_decrypt, 3},
|
|
397
|
+
{"key_id", rb_sdf_key_id, 1},
|
|
398
|
+
{"valid_envelope?", rb_sdf_valid_envelope, 1},
|
|
399
|
+
{"blind_index", rb_sdf_blind_index, 2},
|
|
400
|
+
{"e164?", rb_sdf_e164_p, 1},
|
|
401
|
+
{"phone_blind_index", rb_sdf_phone_bidx, 2},
|
|
402
|
+
{"phone_prefix_blind_index", rb_sdf_phone_prefix_bidx, 3},
|
|
403
|
+
{"encrypt_many", rb_sdf_encrypt_many, 4},
|
|
404
|
+
{"decrypt_many", rb_sdf_decrypt_many, 3},
|
|
405
|
+
{"blind_index_many", rb_sdf_blind_index_many, 2},
|
|
406
|
+
{"blind_index_many_packed", rb_sdf_blind_index_many_packed, 2},
|
|
407
|
+
{"phone_blind_index_many", rb_sdf_phone_bidx_many, 2},
|
|
408
|
+
{"phone_blind_index_many_packed", rb_sdf_phone_bidx_many_packed, 2},
|
|
409
|
+
{"phone_prefix_blind_index_many", rb_sdf_phone_prefix_bidx_many, 3},
|
|
410
|
+
{"phone_prefix_blind_index_many_packed", rb_sdf_phone_prefix_bidx_many_packed, 3},
|
|
411
|
+
{"hex_decode_key", rb_sdf_hex_decode_key, 1}};
|
|
412
|
+
|
|
413
|
+
void Init_secure_db_fields(void) {
|
|
414
|
+
size_t i;
|
|
415
|
+
mSecureDBFields = rb_define_module("SecureDBFields");
|
|
416
|
+
mNative = rb_define_module_under(mSecureDBFields, "Native");
|
|
417
|
+
|
|
418
|
+
eError = rb_define_class_under(mSecureDBFields, "Error", rb_eStandardError);
|
|
419
|
+
eAuthError = rb_define_class_under(mSecureDBFields, "AuthenticationError", eError);
|
|
420
|
+
eFormatError = rb_define_class_under(mSecureDBFields, "FormatError", eError);
|
|
421
|
+
eKeyError = rb_define_class_under(mSecureDBFields, "KeyError", eError);
|
|
422
|
+
|
|
423
|
+
for (i = 0; i < sizeof(rb_sdf_methods) / sizeof(rb_sdf_methods[0]); i++)
|
|
424
|
+
rb_define_singleton_method(mNative, rb_sdf_methods[i].name, rb_sdf_methods[i].func,
|
|
425
|
+
rb_sdf_methods[i].arity);
|
|
426
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SecureDBFields
|
|
4
|
+
module ActiveRecord
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def secure_db_field(name, encrypted:, blind_index:, row_uid: :secure_row_uid, normalizer: nil, keyring: SecureDBFields::Keyring.new, table_name_for_aad: nil, key_domain: nil)
|
|
11
|
+
field_name = name.to_s
|
|
12
|
+
if normalizer == :phone || (normalizer.nil? && field_name == "phone")
|
|
13
|
+
normalizer = ->(value) { value.nil? ? nil : SecureDBFields::Phone.assert_canonical_e164!(value) }
|
|
14
|
+
key_domain ||= "PHONE"
|
|
15
|
+
end
|
|
16
|
+
normalizer ||= ->(value) { value.nil? ? nil : value.to_s }
|
|
17
|
+
table_for_aad = table_name_for_aad || table_name
|
|
18
|
+
domain = key_domain || "#{table_for_aad}_#{field_name}"
|
|
19
|
+
|
|
20
|
+
define_method(name) do
|
|
21
|
+
envelope = public_send(encrypted)
|
|
22
|
+
return nil if envelope.nil?
|
|
23
|
+
|
|
24
|
+
key_id = SecureDBFields::Crypto.key_id(envelope)
|
|
25
|
+
key = keyring.encryption_key(key_id)
|
|
26
|
+
uid = public_send(row_uid)
|
|
27
|
+
SecureDBFields::Crypto.decrypt(envelope, key: key, aad: SecureDBFields::Crypto.aad(table_for_aad, field_name, uid))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
define_method("#{name}=") do |value|
|
|
31
|
+
uid = public_send(row_uid)
|
|
32
|
+
if uid.nil? || uid.to_s.b.bytesize != 16
|
|
33
|
+
raise ArgumentError, "#{row_uid} must be 16 bytes before encrypting #{field_name}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if value.nil?
|
|
37
|
+
public_send("#{encrypted}=", nil)
|
|
38
|
+
public_send("#{blind_index}=", nil)
|
|
39
|
+
next nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
normalized = normalizer.call(value)
|
|
43
|
+
key_id = keyring.active_key_id
|
|
44
|
+
enc_key = keyring.encryption_key(key_id)
|
|
45
|
+
bidx_key = keyring.blind_index_key(domain)
|
|
46
|
+
public_send("#{encrypted}=", SecureDBFields::Crypto.encrypt(normalized, key: enc_key, aad: SecureDBFields::Crypto.aad(table_for_aad, field_name, uid), key_id: key_id))
|
|
47
|
+
public_send("#{blind_index}=", SecureDBFields::Crypto.blind_index(normalized, key: bidx_key))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
define_singleton_method("#{name}_blind_index_for") do |value|
|
|
51
|
+
normalized = normalizer.call(value)
|
|
52
|
+
return nil if normalized.nil?
|
|
53
|
+
SecureDBFields::Crypto.blind_index(normalized, key: keyring.blind_index_key(domain))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|