Shows how to extend WiredTiger with a simple encryption algorithm.
#include <test_util.h>
#ifdef _WIN32
__declspec(dllexport)
#endif
static const char *home;
#define SYS_KEYID "system"
#define SYS_PW "system_password"
#define USER1_KEYID "user1"
#define USER2_KEYID "user2"
#define USERBAD_KEYID "userbad"
#define ITEM_MATCHES(config_item, s) \
(strlen(s) == (config_item).len && strncmp((config_item).str, s, (config_item).len) == 0)
typedef struct {
int rot_N;
uint32_t num_calls;
char *keyid;
char *password;
} MY_CRYPTO;
#define CHKSUM_LEN 4
#define IV_LEN 16
static void
make_checksum(uint8_t *dst)
{
int i;
for (i = 0; i < CHKSUM_LEN; i++)
dst[i] = 'C';
}
static void
make_iv(uint8_t *dst)
{
int i;
for (i = 0; i < IV_LEN; i++)
dst[i] = 'I';
}
static void
do_rotate(char *buf, size_t len, int rotn)
{
uint32_t i;
for (i = 0; i < len; i++)
if (isalpha((unsigned char)buf[i])) {
if (islower((unsigned char)buf[i]))
buf[i] = ((buf[i] - 'a') + rotn) % 26 + 'a';
else
buf[i] = ((buf[i] - 'A') + rotn) % 26 + 'A';
}
}
static int
uint8_t *dst, size_t dst_len, size_t *result_lenp)
{
MY_CRYPTO *my_crypto = (MY_CRYPTO *)encryptor;
size_t mylen;
uint32_t i;
(void)session;
++my_crypto->num_calls;
if (src == NULL)
return (0);
mylen = src_len - (CHKSUM_LEN + IV_LEN);
if (dst_len < mylen) {
fprintf(stderr, "Rotate: ENOMEM ERROR: dst_len %zu src_len %zu\n", dst_len, src_len);
return (ENOMEM);
}
i = CHKSUM_LEN + IV_LEN;
memcpy(&dst[0], &src[i], mylen);
do_rotate((char *)dst, mylen, 26 - my_crypto->rot_N);
*result_lenp = mylen;
return (0);
}
static int
uint8_t *dst, size_t dst_len, size_t *result_lenp)
{
MY_CRYPTO *my_crypto = (MY_CRYPTO *)encryptor;
uint32_t i;
(void)session;
++my_crypto->num_calls;
if (src == NULL)
return (0);
if (dst_len < src_len + CHKSUM_LEN + IV_LEN)
return (ENOMEM);
i = CHKSUM_LEN + IV_LEN;
memcpy(&dst[i], &src[0], src_len);
do_rotate((char *)dst + i, src_len, my_crypto->rot_N);
i = 0;
make_checksum(&dst[i]);
i += CHKSUM_LEN;
make_iv(&dst[i]);
*result_lenp = dst_len;
return (0);
}
static int
{
MY_CRYPTO *my_crypto = (MY_CRYPTO *)encryptor;
(void)session;
++my_crypto->num_calls;
*expansion_constantp = CHKSUM_LEN + IV_LEN;
return (0);
}
static int
{
MY_CRYPTO *my_crypto;
int ret;
const MY_CRYPTO *orig_crypto;
orig_crypto = (const MY_CRYPTO *)encryptor;
if ((my_crypto = calloc(1, sizeof(MY_CRYPTO))) == NULL) {
ret = errno;
goto err;
}
*my_crypto = *orig_crypto;
my_crypto->keyid = my_crypto->password = NULL;
error_check(extapi->
config_get(extapi, session, encrypt_config,
"keyid", &keyid));
if ((my_crypto->keyid = malloc(keyid.
len + 1)) == NULL) {
ret = errno;
goto err;
}
strncpy(my_crypto->keyid, keyid.
str, keyid.
len + 1);
my_crypto->keyid[keyid.
len] =
'\0';
}
ret = extapi->
config_get(extapi, session, encrypt_config,
"secretkey", &secret);
if (ret == 0 && secret.
len != 0) {
if ((my_crypto->password = malloc(secret.
len + 1)) == NULL) {
ret = errno;
goto err;
}
strncpy(my_crypto->password, secret.
str, secret.
len + 1);
my_crypto->password[secret.
len] =
'\0';
}
if (ITEM_MATCHES(keyid, "system")) {
if (my_crypto->password == NULL || strcmp(my_crypto->password, SYS_PW) != 0) {
ret = EPERM;
goto err;
}
my_crypto->rot_N = 13;
} else if (ITEM_MATCHES(keyid, USER1_KEYID))
my_crypto->rot_N = 4;
else if (ITEM_MATCHES(keyid, USER2_KEYID))
my_crypto->rot_N = 19;
else {
ret = EINVAL;
goto err;
}
++my_crypto->num_calls;
return (0);
err:
free(my_crypto->keyid);
free(my_crypto->password);
free(my_crypto);
return (ret);
}
static int
{
MY_CRYPTO *my_crypto = (MY_CRYPTO *)encryptor;
(void)session;
++my_crypto->num_calls;
free(my_crypto->password);
my_crypto->password = NULL;
free(my_crypto->keyid);
my_crypto->keyid = NULL;
free(encryptor);
return (0);
}
int
{
MY_CRYPTO *m;
if ((m = calloc(1, sizeof(MY_CRYPTO))) == NULL)
return (errno);
m->num_calls = 0;
return (0);
}
static void
{
uint64_t txnid;
uint32_t fileid, log_file, log_offset, opcount, optype, rectype;
int found, ret;
error_check(session->
open_cursor(session,
"log:", NULL, NULL, &cursor));
found = 0;
while ((ret = cursor->
next(cursor)) == 0) {
error_check(cursor->
get_key(cursor, &log_file, &log_offset, &opcount));
cursor, &txnid, &rectype, &optype, &fileid, &logrec_key, &logrec_value));
found = 1;
printf(
"Application Log Record: %s\n", (
char *)logrec_value.
data);
}
}
error_check(cursor->
close(cursor));
if (found == 0) {
fprintf(stderr, "Did not find log messages.\n");
exit(EXIT_FAILURE);
}
}
#define MAX_KEYS 20
#define EXTENSION_NAME "local=(entry=add_my_encryptors)"
#define WT_OPEN_CONFIG_COMMON \
"create,cache_size=100MB,extensions=[" EXTENSION_NAME \
"]," \
"log=(archive=false,enabled=true),"
#define WT_OPEN_CONFIG_GOOD \
WT_OPEN_CONFIG_COMMON \
"encryption=(name=rotn,keyid=" SYS_KEYID ",secretkey=" SYS_PW ")"
#define COMP_A "AAAAAAAAAAAAAAAAAA"
#define COMP_B "BBBBBBBBBBBBBBBBBB"
#define COMP_C "CCCCCCCCCCCCCCCCCC"
int
main(int argc, char *argv[])
{
int i, ret;
char keybuf[32], valbuf[32];
char *key1, *key2, *key3, *val1, *val2, *val3;
home = example_setup(argc, argv);
error_check(conn->
open_session(conn, NULL, NULL, &session));
COMP_A COMP_B COMP_C COMP_A COMP_B COMP_C COMP_A COMP_B COMP_C COMP_A COMP_B COMP_C
"The quick brown fox jumps over the lazy dog "));
simple_walk_log(session);
error_check(
session->
create(session,
"table:crypto1",
"encryption=(name=rotn,keyid=" USER1_KEYID
")," "columns=(key0,value0),"
"key_format=S,value_format=S"));
error_check(session->
create(session,
"index:crypto1:byvalue", "encryption=(name=rotn,keyid=" USER1_KEYID "),"
"columns=(value0,key0)"));
error_check(
session->
create(session,
"table:crypto2",
"encryption=(name=rotn,keyid=" USER2_KEYID
")," "key_format=S,value_format=S"));
error_check(session->
create(session,
"table:nocrypto",
"key_format=S,value_format=S"));
ret = session->
create(session,
"table:cryptobad",
"encryption=(name=rotn,keyid=" USERBAD_KEYID
"),"
"key_format=S,value_format=S");
if (ret == 0) {
fprintf(stderr, "Did not detect bad/unknown keyid error\n");
exit(EXIT_FAILURE);
}
error_check(session->
open_cursor(session,
"table:crypto1", NULL, NULL, &c1));
error_check(session->
open_cursor(session,
"table:crypto2", NULL, NULL, &c2));
error_check(session->
open_cursor(session,
"table:nocrypto", NULL, NULL, &nc));
for (i = 0; i < MAX_KEYS; i++) {
(void)snprintf(keybuf, sizeof(keybuf), "key%d", i);
(void)snprintf(valbuf, sizeof(valbuf), "value%d", i);
if (i % 5 == 0)
error_check(session->
log_printf(session,
"Wrote %d records", i));
}
error_check(session->
log_printf(session,
"Done. Wrote %d total records", i));
while (c1->
next(c1) == 0) {
error_check(c1->
get_key(c1, &key1));
printf("Read key %s; value %s\n", key1, val1);
}
simple_walk_log(session);
printf("CLOSE\n");
error_check(conn->
close(conn, NULL));
printf("REOPEN and VERIFY encrypted data\n");
error_check(conn->
open_session(conn, NULL, NULL, &session));
simple_walk_log(session);
error_check(session->
open_cursor(session,
"table:crypto1", NULL, NULL, &c1));
error_check(session->
open_cursor(session,
"table:crypto2", NULL, NULL, &c2));
error_check(session->
open_cursor(session,
"table:nocrypto", NULL, NULL, &nc));
while (c1->
next(c1) == 0) {
error_check(c2->
next(c2));
error_check(nc->
next(nc));
error_check(c1->
get_key(c1, &key1));
error_check(c2->
get_key(c2, &key2));
error_check(nc->
get_key(nc, &key3));
if (strcmp(key1, key2) != 0)
fprintf(stderr, "Key1 %s and Key2 %s do not match\n", key1, key2);
if (strcmp(key1, key3) != 0)
fprintf(stderr, "Key1 %s and Key3 %s do not match\n", key1, key3);
if (strcmp(key2, key3) != 0)
fprintf(stderr, "Key2 %s and Key3 %s do not match\n", key2, key3);
if (strcmp(val1, val2) != 0)
fprintf(stderr, "Val1 %s and Val2 %s do not match\n", val1, val2);
if (strcmp(val1, val3) != 0)
fprintf(stderr, "Val1 %s and Val3 %s do not match\n", val1, val3);
if (strcmp(val2, val3) != 0)
fprintf(stderr, "Val2 %s and Val3 %s do not match\n", val2, val3);
printf("Verified key %s; value %s\n", key1, val1);
}
error_check(conn->
close(conn, NULL));
return (EXIT_SUCCESS);
}