#include <errno.h>
#include <wiredtiger.h>
#include <wiredtiger_ext.h>
#include <sodium.h>
#define CHECK_LEN crypto_aead_xchacha20poly1305_ietf_ABYTES
#define KEY_LEN crypto_aead_xchacha20poly1305_ietf_KEYBYTES
#define NONCE_LEN crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
#define HEADER_BYTE_FORMATVERSION 0
#define HEADER_BYTE_CONSTRUCTION 1
#define HEADER_BYTE_ZERO_0 2
#define HEADER_BYTE_ZERO_1 3
#define HEADER_LEN 4
#define ONDISK_CIPHER_XCHACHA20POLY1305 1
#define ONDISK_VERSION_CURRENT 1
#define HEADER_LOCATION 0
#define NONCE_LOCATION HEADER_LEN
#define CIPHERTEXT_LOCATION (HEADER_LEN + NONCE_LEN)
typedef struct {
uint8_t *secretkey;
} SODIUM_ENCRYPTOR;
static int
sodium_error(SODIUM_ENCRYPTOR *encryptor,
WT_SESSION *session,
int err,
const char *msg)
{
wt_api = encryptor->wt_api;
wt_api, session,
"sodium encryption: %s: %s", msg, wt_api->
strerror(wt_api, NULL, err));
return (err);
}
static void
create_nonce(uint8_t *dst, size_t len)
{
randombytes_buf(dst, len);
}
static int
uint8_t *ciphertext, size_t cipher_maxlen, size_t *result_lenp)
{
SODIUM_ENCRYPTOR *sodium_encryptor = (SODIUM_ENCRYPTOR *)encryptor;
unsigned long long cipher_len;
int ret;
(void)session;
if (cipher_maxlen < HEADER_LEN + NONCE_LEN + clear_len + CHECK_LEN)
return (sodium_error(sodium_encryptor, session, ENOMEM, "encrypt buffer not big enough"));
ciphertext[HEADER_LOCATION + HEADER_BYTE_FORMATVERSION] = ONDISK_VERSION_CURRENT;
ciphertext[HEADER_LOCATION + HEADER_BYTE_CONSTRUCTION] = ONDISK_CIPHER_XCHACHA20POLY1305;
ciphertext[HEADER_LOCATION + HEADER_BYTE_ZERO_0] = 0;
ciphertext[HEADER_LOCATION + HEADER_BYTE_ZERO_1] = 0;
create_nonce(&ciphertext[NONCE_LOCATION], NONCE_LEN);
ret = crypto_aead_xchacha20poly1305_ietf_encrypt(&ciphertext[CIPHERTEXT_LOCATION], &cipher_len,
cleartext, clear_len, &ciphertext[HEADER_LOCATION], HEADER_LEN, NULL,
&ciphertext[NONCE_LOCATION], sodium_encryptor->secretkey);
if (ret < 0)
return (sodium_error(sodium_encryptor, session,
WT_ERROR,
"encryption failed"));
*result_lenp = HEADER_LEN + NONCE_LEN + cipher_len;
return (0);
}
static int
uint8_t *cleartext, size_t clear_maxlen, size_t *result_lenp)
{
SODIUM_ENCRYPTOR *sodium_encryptor = (SODIUM_ENCRYPTOR *)encryptor;
size_t cipher_check_only_len;
unsigned long long clear_len;
int ret;
if (HEADER_LEN + NONCE_LEN + clear_maxlen + CHECK_LEN < cipher_len)
return (sodium_error(sodium_encryptor, session, ENOMEM, "decrypt buffer not big enough"));
cipher_check_only_len = cipher_len - HEADER_LEN - NONCE_LEN;
ret = crypto_aead_xchacha20poly1305_ietf_decrypt(cleartext, &clear_len, NULL,
&ciphertext[CIPHERTEXT_LOCATION], cipher_check_only_len, &ciphertext[HEADER_LOCATION],
HEADER_LEN, &ciphertext[NONCE_LOCATION], sodium_encryptor->secretkey);
if (ret < 0)
return (sodium_error(sodium_encryptor, session,
WT_ERROR,
"decryption failed"));
*result_lenp = clear_len;
return (0);
}
static int
{
(void)encryptor;
(void)session;
*expansion_constantp = HEADER_LEN + NONCE_LEN + CHECK_LEN;
return (0);
}
static int
{
const SODIUM_ENCRYPTOR *orig;
SODIUM_ENCRYPTOR *new;
size_t keylen;
int ret;
orig = (const SODIUM_ENCRYPTOR *)encryptor;
wt_api = orig->wt_api;
if ((new = calloc(1, sizeof(*new))) == NULL)
return (errno);
*new = *orig;
new->secretkey = NULL;
ret = wt_api->
config_get(wt_api, session, encrypt_config,
"keyid", &keyid);
if (ret != 0)
ret = wt_api->
config_get(wt_api, session, encrypt_config,
"secretkey", &secretkey);
if (ret != 0)
if (keyid.
len != 0 && secretkey.
len != 0) {
ret = sodium_error(
new, NULL, EINVAL, "sodium_customize: keys specified with both keyid= and secretkey=");
goto err;
}
if (keyid.
len == 0 && secretkey.
len == 0) {
ret = sodium_error(
new, NULL, EINVAL, "sodium_customize: no key given with either keyid= or secretkey=");
goto err;
}
new->secretkey = sodium_malloc(KEY_LEN);
if (new->secretkey == NULL) {
ret = errno;
goto err;
}
ret = sodium_error(new, NULL, EINVAL, "sodium_customize: keyids not supported yet");
goto err;
}
if (secretkey.
len != 0) {
ret = sodium_hex2bin(
new->secretkey, KEY_LEN, secretkey.
str, secretkey.
len, NULL, &keylen, NULL);
if (ret < 0) {
ret = sodium_error(new, NULL, EINVAL, "sodium_customize: secret key not hex");
goto err;
}
if (keylen != KEY_LEN) {
ret = sodium_error(new, NULL, EINVAL, "sodium_customize: wrong secret key length");
goto err;
}
}
return (0);
err:
sodium_free(new->secretkey);
free(new);
return (ret);
}
static int
{
SODIUM_ENCRYPTOR *sodium_encryptor = (SODIUM_ENCRYPTOR *)encryptor;
(void)session;
sodium_free(sodium_encryptor->secretkey);
free(sodium_encryptor);
return (0);
}
static int
sodium_configure(SODIUM_ENCRYPTOR *sodium_encryptor,
WT_CONFIG_ARG *config)
{
wt_api = sodium_encryptor->wt_api;
(void)config;
(void)wt_api;
return (0);
}
int
{
SODIUM_ENCRYPTOR *sodium_encryptor;
int ret;
if ((sodium_encryptor = calloc(1, sizeof(SODIUM_ENCRYPTOR))) == NULL)
return (errno);
sodium_encryptor->encryptor.encrypt = sodium_encrypt;
sodium_encryptor->encryptor.decrypt = sodium_decrypt;
sodium_encryptor->encryptor.sizing = sodium_sizing;
sodium_encryptor->encryptor.customize = sodium_customize;
sodium_encryptor->encryptor.terminate = sodium_terminate;
if (sodium_init() < 0)
if ((ret = sodium_configure(sodium_encryptor, config)) != 0) {
free(sodium_encryptor);
return (ret);
}
connection,
"sodium", (
WT_ENCRYPTOR *)sodium_encryptor, NULL)) != 0) {
free(sodium_encryptor);
return (ret);
}
return (0);
}