diff options
Diffstat (limited to 'src/third_party/wiredtiger/ext')
7 files changed, 846 insertions, 87 deletions
diff --git a/src/third_party/wiredtiger/ext/compressors/bzip2/bzip2_compress.c b/src/third_party/wiredtiger/ext/compressors/bzip2/bzip2_compress.c index 3f2e09de2f6..6cd53aba5d2 100644 --- a/src/third_party/wiredtiger/ext/compressors/bzip2/bzip2_compress.c +++ b/src/third_party/wiredtiger/ext/compressors/bzip2/bzip2_compress.c @@ -314,8 +314,15 @@ bzip2_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, if ((ret = BZ2_bzDecompress(&bz)) == BZ_STREAM_END) { *result_lenp = dst_len - bz.avail_out; ret = 0; - } else + } else { + /* + * If BZ2_bzDecompress returns 0, it expects there to be more + * data available. There isn't, so treat this as an error. + */ + if (ret == 0) + ret = BZ_DATA_ERROR; (void)bzip2_error(compressor, session, "BZ2_bzDecompress", ret); + } if ((tret = BZ2_bzDecompressEnd(&bz)) != BZ_OK) return (bzip2_error( diff --git a/src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c b/src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c index 9939a4f8a04..0906e1d131d 100644 --- a/src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c +++ b/src/third_party/wiredtiger/ext/compressors/lz4/lz4_compress.c @@ -48,19 +48,41 @@ typedef struct { } LZ4_COMPRESSOR; /* + * LZ4 decompression requires the exact compressed byte count returned by the + * LZ4_compress and LZ4_compress_destSize functions. WiredTiger doesn't track + * that value, store it in the destination buffer. + * + * Additionally, LZ4_compress_destSize may compress into the middle of a record, + * and after decompression we return the length to the last record successfully + * decompressed, not the number of bytes decompressed; store that value in the + * destination buffer as well. + * + * Use fixed-size, 4B values (WiredTiger never writes buffers larger than 4GB). + * + * The unused field is available for a mode flag if one is needed in the future, + * we guarantee it's 0. + */ +typedef struct { + uint32_t compressed_len; /* True compressed length */ + uint32_t uncompressed_len; /* True uncompressed source length */ + uint32_t useful_len; /* Decompression return value */ + uint32_t unused; /* Guaranteed to be 0 */ +} LZ4_PREFIX; + +/* * lz4_error -- * Output an error message, and return a standard error code. */ static int lz4_error( - WT_COMPRESSOR *compressor, WT_SESSION *session, const char *call, int zret) + WT_COMPRESSOR *compressor, WT_SESSION *session, const char *call, int error) { WT_EXTENSION_API *wt_api; wt_api = ((LZ4_COMPRESSOR *)compressor)->wt_api; (void)wt_api->err_printf(wt_api, - session, "lz4 error: %s: %d", call, zret); + session, "lz4 error: %s: %d", call, error); return (WT_ERROR); } @@ -74,39 +96,34 @@ lz4_compress(WT_COMPRESSOR *compressor, WT_SESSION *session, uint8_t *dst, size_t dst_len, size_t *result_lenp, int *compression_failed) { - char *lz4buf; - size_t lz4_len; + LZ4_PREFIX prefix; + int lz4_len; - /* - * The buffer should always be large enough due to the lz4_pre_size - * call, but be paranoid and error if it isn't. - */ - if (dst_len < src_len + sizeof(size_t)) - return (lz4_error(compressor, session, - "LZ4 compress buffer too small", 0)); + (void)compressor; /* Unused parameters */ + (void)session; + (void)dst_len; - /* Store the length of the compressed block in the first 8 bytes. */ - lz4buf = (char *)dst + sizeof(size_t); - lz4_len = (size_t)LZ4_compress((const char *)src, lz4buf, (int)src_len); + /* Compress, starting after the prefix bytes. */ + lz4_len = LZ4_compress( + (const char *)src, (char *)dst + sizeof(LZ4_PREFIX), (int)src_len); /* - * Flag no-compression if the result was larger than the original - * size or compression failed. + * If compression succeeded and the compressed length is smaller than + * the original size, return success. */ - if (lz4_len == 0 || lz4_len + sizeof(size_t) >= src_len) - *compression_failed = 1; - else { - /* - * On decompression, lz4 requires the exact compressed byte - * count (the current value of lz4_len). WiredTiger does not - * preserve that value, so save lz4_len at the beginning of the - * destination buffer. - */ - *(size_t *)dst = lz4_len; - *result_lenp = lz4_len + sizeof(size_t); + if (lz4_len != 0 && (size_t)lz4_len + sizeof(LZ4_PREFIX) < src_len) { + prefix.compressed_len = (uint32_t)lz4_len; + prefix.uncompressed_len = (uint32_t)src_len; + prefix.useful_len = (uint32_t)src_len; + prefix.unused = 0; + memcpy(dst, &prefix, sizeof(LZ4_PREFIX)); + + *result_lenp = (size_t)lz4_len + sizeof(LZ4_PREFIX); *compression_failed = 0; + return (0); } + *compression_failed = 1; return (0); } @@ -121,40 +138,143 @@ lz4_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, size_t *result_lenp) { WT_EXTENSION_API *wt_api; - char *compressed_data; + LZ4_PREFIX prefix; int decoded; - size_t src_data_len; + uint8_t *dst_tmp; + + (void)src_len; /* Unused parameters */ wt_api = ((LZ4_COMPRESSOR *)compressor)->wt_api; - /* Retrieve compressed length from start of the data buffer. */ - src_data_len = *(size_t *)src; - if (src_data_len + sizeof(size_t) > src_len) { - (void)wt_api->err_printf(wt_api, - session, - "lz4_decompress: stored size exceeds buffer size"); - return (WT_ERROR); + /* + * Retrieve the true length of the compressed block and source and the + * decompressed bytes to return from the start of the source buffer. + */ + memcpy(&prefix, src, sizeof(LZ4_PREFIX)); + + /* + * Decompress, starting after the prefix bytes. Use safe decompression: + * we rely on decompression to detect corruption. + * + * Two code paths, one with and one without a bounce buffer. When doing + * raw compression, we compress to a target size irrespective of row + * boundaries, and return to our caller a "useful" compression length + * based on the last complete row that was compressed. Our caller stores + * that length, not the length of bytes actually compressed by LZ4. In + * other words, our caller doesn't know how many bytes will result from + * decompression, likely hasn't provided us a large enough buffer, and + * we have to allocate a scratch buffer. + */ + if (dst_len < prefix.uncompressed_len) { + if ((dst_tmp = wt_api->scr_alloc( + wt_api, session, (size_t)prefix.uncompressed_len)) == NULL) + return (ENOMEM); + + decoded = LZ4_decompress_safe( + (const char *)src + sizeof(LZ4_PREFIX), (char *)dst_tmp, + (int)prefix.compressed_len, (int)prefix.uncompressed_len); + + if (decoded >= 0) + memcpy(dst, dst_tmp, dst_len); + wt_api->scr_free(wt_api, session, dst_tmp); + } else + decoded = LZ4_decompress_safe( + (const char *)src + sizeof(LZ4_PREFIX), + (char *)dst, (int)prefix.compressed_len, (int)dst_len); + + if (decoded >= 0) { + *result_lenp = prefix.useful_len; + return (0); } - /* Skip over the data size to the start of compressed data. */ - compressed_data = (char *)src + sizeof(size_t); + return ( + lz4_error(compressor, session, "LZ4 decompress error", decoded)); +} + +/* + * lz4_find_slot -- + * Find the slot containing the target offset (binary search). + */ +static inline uint32_t +lz4_find_slot(int target_arg, uint32_t *offsets, uint32_t slots) +{ + uint32_t base, indx, limit, target; + + indx = 1; /* -Wuninitialized */ + + target = (uint32_t)target_arg; /* Type conversion */ + + /* Fast check if we consumed it all, it's a likely result. */ + if (target >= offsets[slots]) + return (slots); /* - * The destination buffer length should always be sufficient because - * wiredtiger keeps track of the byte count before compression. Use - * safe decompression: we may be relying on decompression to detect - * corruption. + * Figure out which slot we got to: binary search. Note the test of + * offset (slot + 1), that's (end-byte + 1) for slot. */ - decoded = LZ4_decompress_safe( - compressed_data, (char *)dst, (int)src_data_len, (int)dst_len); + for (base = 0, limit = slots; limit != 0; limit >>= 1) { + indx = base + (limit >> 1); + if (target > offsets[indx + 1]) { + base = indx + 1; + --limit; + } + } + + return (indx); +} + +/* + * lz4_compress_raw -- + * Pack records into a specified on-disk page size. + */ +static int +lz4_compress_raw(WT_COMPRESSOR *compressor, WT_SESSION *session, + size_t page_max, int split_pct, size_t extra, + uint8_t *src, uint32_t *offsets, uint32_t slots, + uint8_t *dst, size_t dst_len, int final, + size_t *result_lenp, uint32_t *result_slotsp) +{ + LZ4_PREFIX prefix; + int lz4_len; + uint32_t slot; + int sourceSize, targetDestSize; + + (void)compressor; /* Unused parameters */ + (void)session; + (void)split_pct; + (void)final; + + sourceSize = (int)offsets[slots]; /* Type conversion */ + targetDestSize = + (int)((dst_len < page_max ? dst_len : page_max) - extra); - if (decoded < 0) - return (lz4_error(compressor, session, - "LZ4 decompress error", decoded)); + /* Compress, starting after the prefix bytes. */ + lz4_len = LZ4_compress_destSize((const char *)src, + (char *)dst + sizeof(LZ4_PREFIX), &sourceSize, targetDestSize); - /* return the uncompressed data length */ - *result_lenp = dst_len; + /* + * If compression succeeded and the compressed length is smaller than + * the original size, return success. + */ + if (lz4_len != 0) { + /* Find the first slot we didn't compress. */ + slot = lz4_find_slot(sourceSize, offsets, slots); + + if ((size_t)lz4_len + sizeof(LZ4_PREFIX) < offsets[slot]) { + prefix.compressed_len = (uint32_t)lz4_len; + prefix.uncompressed_len = (uint32_t)sourceSize; + prefix.useful_len = offsets[slot]; + prefix.unused = 0; + memcpy(dst, &prefix, sizeof(LZ4_PREFIX)); + + *result_slotsp = slot; + *result_lenp = (size_t)lz4_len + sizeof(LZ4_PREFIX); + return (0); + } + } + *result_slotsp = 0; + *result_lenp = 1; return (0); } @@ -164,18 +284,18 @@ lz4_decompress(WT_COMPRESSOR *compressor, WT_SESSION *session, */ static int lz4_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session, - uint8_t *src, size_t src_len, - size_t *result_lenp) + uint8_t *src, size_t src_len, size_t *result_lenp) { - (void)compressor; + (void)compressor; /* Unused parameters */ (void)session; (void)src; /* - * LZ4 can use more space than the input data size, use the library - * calculation of that overhead (plus our overhead) to be safe. + * In block mode, LZ4 can use more space than the input data size, use + * the library calculation of that overhead (plus our overhead) to be + * safe. */ - *result_lenp = LZ4_COMPRESSBOUND(src_len) + sizeof(size_t); + *result_lenp = LZ4_COMPRESSBOUND(src_len) + sizeof(LZ4_PREFIX); return (0); } @@ -186,44 +306,30 @@ lz4_pre_size(WT_COMPRESSOR *compressor, WT_SESSION *session, static int lz4_terminate(WT_COMPRESSOR *compressor, WT_SESSION *session) { - (void)session; + (void)session; /* Unused parameters */ - /* Free the allocated memory. */ free(compressor); - return (0); } -int lz4_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *); - /* - * lz4_extension_init -- - * A simple shared library compression example. + * lz4_add_compressor -- + * Add a LZ4 compressor. */ -int -lz4_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) +static int +lz_add_compressor(WT_CONNECTION *connection, int raw, const char *name) { LZ4_COMPRESSOR *lz4_compressor; - (void)config; /* Unused parameters */ - + /* + * There are two almost identical LZ4 compressors: one using raw + * compression to target a specific block size, and one without. + */ if ((lz4_compressor = calloc(1, sizeof(LZ4_COMPRESSOR))) == NULL) return (errno); - /* - * Allocate a local compressor structure, with a WT_COMPRESSOR structure - * as the first field, allowing us to treat references to either type of - * structure as a reference to the other type. - * - * This could be simplified if only a single database is opened in the - * application, we could use a static WT_COMPRESSOR structure, and a - * static reference to the WT_EXTENSION_API methods, then we don't need - * to allocate memory when the compressor is initialized or free it when - * the compressor is terminated. However, this approach is more general - * purpose and supports multiple databases per application. - */ lz4_compressor->compressor.compress = lz4_compress; - lz4_compressor->compressor.compress_raw = NULL; + lz4_compressor->compressor.compress_raw = raw ? lz4_compress_raw : NULL; lz4_compressor->compressor.decompress = lz4_decompress; lz4_compressor->compressor.pre_size = lz4_pre_size; lz4_compressor->compressor.terminate = lz4_terminate; @@ -232,7 +338,29 @@ lz4_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) /* Load the compressor */ return (connection->add_compressor( - connection, "lz4", (WT_COMPRESSOR *)lz4_compressor, NULL)); + connection, name, (WT_COMPRESSOR *)lz4_compressor, NULL)); +} + +int lz4_extension_init(WT_CONNECTION *, WT_CONFIG_ARG *); + +/* + * lz4_extension_init -- + * WiredTiger LZ4 compression extension - called directly when LZ4 support + * is built in, or via wiredtiger_extension_init when LZ4 support is included + * via extension loading. + */ +int +lz4_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) +{ + int ret; + + (void)config; /* Unused parameters */ + + if ((ret = lz_add_compressor(connection, 1, "lz4")) != 0) + return (ret); + if ((ret = lz_add_compressor(connection, 0, "lz4-noraw")) != 0) + return (ret); + return (0); } /* diff --git a/src/third_party/wiredtiger/ext/compressors/nop/nop_compress.c b/src/third_party/wiredtiger/ext/compressors/nop/nop_compress.c index 6b640adf777..ce6964be85d 100644 --- a/src/third_party/wiredtiger/ext/compressors/nop/nop_compress.c +++ b/src/third_party/wiredtiger/ext/compressors/nop/nop_compress.c @@ -166,12 +166,7 @@ wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) * as the first field, allowing us to treat references to either type of * structure as a reference to the other type. * - * This could be simplified if only a single database is opened in the - * application, we could use a static WT_COMPRESSOR structure, and a - * static reference to the WT_EXTENSION_API methods, then we don't need - * to allocate memory when the compressor is initialized or free it when - * the compressor is terminated. However, this approach is more general - * purpose and supports multiple databases per application. + * Heap memory (not static), because it can support multiple databases. */ nop_compressor->compressor.compress = nop_compress; nop_compressor->compressor.compress_raw = NULL; diff --git a/src/third_party/wiredtiger/ext/encryptors/nop/Makefile.am b/src/third_party/wiredtiger/ext/encryptors/nop/Makefile.am new file mode 100644 index 00000000000..189f764b04c --- /dev/null +++ b/src/third_party/wiredtiger/ext/encryptors/nop/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)/src/include + +noinst_LTLIBRARIES = libwiredtiger_nop.la +libwiredtiger_nop_la_SOURCES = nop_encrypt.c + +# libtool hack: noinst_LTLIBRARIES turns off building shared libraries as well +# as installation, it will only build static libraries. As far as I can tell, +# the "approved" libtool way to turn them back on is by adding -rpath. +libwiredtiger_nop_la_LDFLAGS = -avoid-version -module -rpath /nowhere diff --git a/src/third_party/wiredtiger/ext/encryptors/nop/nop_encrypt.c b/src/third_party/wiredtiger/ext/encryptors/nop/nop_encrypt.c new file mode 100644 index 00000000000..e3f693ad37d --- /dev/null +++ b/src/third_party/wiredtiger/ext/encryptors/nop/nop_encrypt.c @@ -0,0 +1,178 @@ +/*- + * Public Domain 2014-2015 MongoDB, Inc. + * Public Domain 2008-2014 WiredTiger, Inc. + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <wiredtiger.h> +#include <wiredtiger_ext.h> + +/*! [WT_ENCRYPTOR initialization structure] */ +/* Local encryptor structure. */ +typedef struct { + WT_ENCRYPTOR encryptor; /* Must come first */ + + WT_EXTENSION_API *wt_api; /* Extension API */ + + unsigned long nop_calls; /* Count of calls */ + +} NOP_ENCRYPTOR; +/*! [WT_ENCRYPTOR initialization structure] */ + +/*! [WT_ENCRYPTOR encrypt] */ +/* + * nop_encrypt -- + * A simple encryption example that passes data through unchanged. + */ +static int +nop_encrypt(WT_ENCRYPTOR *encryptor, WT_SESSION *session, + uint8_t *src, size_t src_len, + uint8_t *dst, size_t dst_len, + size_t *result_lenp) +{ + NOP_ENCRYPTOR *nop_encryptor = (NOP_ENCRYPTOR *)encryptor; + + (void)session; /* Unused parameters */ + + ++nop_encryptor->nop_calls; /* Call count */ + + if (dst_len < src_len) + return (ENOMEM); + + memcpy(dst, src, src_len); + *result_lenp = src_len; + + return (0); +} +/*! [WT_ENCRYPTOR encrypt] */ + +/*! [WT_ENCRYPTOR decrypt] */ +/* + * nop_decrypt -- + * A simple decryption example that passes data through unchanged. + */ +static int +nop_decrypt(WT_ENCRYPTOR *encryptor, WT_SESSION *session, + uint8_t *src, size_t src_len, + uint8_t *dst, size_t dst_len, + size_t *result_lenp) +{ + NOP_ENCRYPTOR *nop_encryptor = (NOP_ENCRYPTOR *)encryptor; + + (void)session; /* Unused parameters */ + (void)src_len; + + ++nop_encryptor->nop_calls; /* Call count */ + + /* + * The destination length is the number of unencrypted bytes we're + * expected to return. + */ + memcpy(dst, src, dst_len); + *result_lenp = dst_len; + return (0); +} +/*! [WT_ENCRYPTOR decrypt] */ + +/*! [WT_ENCRYPTOR sizing] */ +/* + * nop_sizing -- + * A simple sizing example that tells wiredtiger that the + * encrypted buffer is always the same as the source buffer. + */ +static int +nop_sizing(WT_ENCRYPTOR *encryptor, WT_SESSION *session, + size_t *expansion_constantp) +{ + NOP_ENCRYPTOR *nop_encryptor = (NOP_ENCRYPTOR *)encryptor; + + (void)session; /* Unused parameters */ + + ++nop_encryptor->nop_calls; /* Call count */ + + *expansion_constantp = 0; + return (0); +} +/*! [WT_ENCRYPTOR sizing] */ + +/*! [WT_ENCRYPTOR terminate] */ +/* + * nop_terminate -- + * WiredTiger no-op encryption termination. + */ +static int +nop_terminate(WT_ENCRYPTOR *encryptor, WT_SESSION *session) +{ + NOP_ENCRYPTOR *nop_encryptor = (NOP_ENCRYPTOR *)encryptor; + + (void)session; /* Unused parameters */ + + ++nop_encryptor->nop_calls; /* Call count */ + + /* Free the allocated memory. */ + free(encryptor); + + return (0); +} +/*! [WT_ENCRYPTOR terminate] */ + +/*! [WT_ENCRYPTOR initialization function] */ +/* + * wiredtiger_extension_init -- + * A simple shared library encryption example. + */ +int +wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) +{ + NOP_ENCRYPTOR *nop_encryptor; + + (void)config; /* Unused parameters */ + + if ((nop_encryptor = calloc(1, sizeof(NOP_ENCRYPTOR))) == NULL) + return (errno); + + /* + * Allocate a local encryptor structure, with a WT_ENCRYPTOR structure + * as the first field, allowing us to treat references to either type of + * structure as a reference to the other type. + * + * Heap memory (not static), because it can support multiple databases. + */ + nop_encryptor->encryptor.encrypt = nop_encrypt; + nop_encryptor->encryptor.decrypt = nop_decrypt; + nop_encryptor->encryptor.sizing = nop_sizing; + nop_encryptor->encryptor.terminate = nop_terminate; + + nop_encryptor->wt_api = connection->get_extension_api(connection); + + /* Load the encryptor */ + return (connection->add_encryptor( + connection, "nop", (WT_ENCRYPTOR *)nop_encryptor, NULL)); +} +/*! [WT_ENCRYPTOR initialization function] */ diff --git a/src/third_party/wiredtiger/ext/encryptors/rotn/Makefile.am b/src/third_party/wiredtiger/ext/encryptors/rotn/Makefile.am new file mode 100644 index 00000000000..fec4ed0f861 --- /dev/null +++ b/src/third_party/wiredtiger/ext/encryptors/rotn/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)/src/include + +noinst_LTLIBRARIES = libwiredtiger_rotn.la +libwiredtiger_rotn_la_SOURCES = rotn_encrypt.c + +# libtool hack: noinst_LTLIBRARIES turns off building shared libraries as well +# as installation, it will only build static libraries. As far as I can tell, +# the "approved" libtool way to turn them back on is by adding -rpath. +libwiredtiger_rotn_la_LDFLAGS = -avoid-version -module -rpath /nowhere diff --git a/src/third_party/wiredtiger/ext/encryptors/rotn/rotn_encrypt.c b/src/third_party/wiredtiger/ext/encryptors/rotn/rotn_encrypt.c new file mode 100644 index 00000000000..503dcae83a7 --- /dev/null +++ b/src/third_party/wiredtiger/ext/encryptors/rotn/rotn_encrypt.c @@ -0,0 +1,433 @@ +/*- + * Public Domain 2014-2015 MongoDB, Inc. + * Public Domain 2008-2014 WiredTiger, Inc. + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <wiredtiger.h> +#include <wiredtiger_ext.h> + +/*! [WT_ENCRYPTOR initialization structure] */ + +/* + * This encryptor is used for testing and demonstration only. + * + * IT IS TRIVIAL TO BREAK AND DOES NOT OFFER ANY SECURITY! + * + * There are two configuration parameters that control it: the keyid and the + * secretkey (which may be thought of as a password). The keyid is expected + * to be a digits giving a number between 0 and 25. The secretkey, when + * present, must be composed of alphabetic characters. + * + * When there is no secretkey, the encryptor acts as a ROT(N) encryptor (a + * "Caesar cipher"), where N is the value of keyid. Thus, with keyid=13, + * text "Hello" maps to "Uryyb", as we preserve case. Only the alphabetic + * characters in the input text are changed. + * + * When there is a secretkey we are implementing a Vigenere cipher. + * Each byte is rotated the distance from 'A' for each letter in the + * (repeating) secretkey. The distance is increased by the value of + * the keyid. Thus, with secretkey "ABC" and keyid "2", we show how + * we map the input "MySecret". + * secretkey = ABC + * distances from 'A' = 012 + * add keyid (2) = 234 + * repeated = 23423423 + * input = MySecret + * output = ObWgfvgw + * In this case, we transform all bytes in the input. + */ + +/* Local encryptor structure. */ +typedef struct { + WT_ENCRYPTOR encryptor; /* Must come first */ + + WT_EXTENSION_API *wt_api; /* Extension API */ + + int rot_N; /* rotN value */ + char *keyid; /* Saved keyid */ + char *secretkey; /* Saved secretkey */ + u_char *shift_forw; /* Encrypt shift data from secretkey */ + u_char *shift_back; /* Decrypt shift data from secretkey */ + size_t shift_len; /* Length of shift* byte arrays */ + +} ROTN_ENCRYPTOR; +/*! [WT_ENCRYPTOR initialization structure] */ + +#define CHKSUM_LEN 4 +#define IV_LEN 16 + +/* + * make_cksum -- + * This is where one would call a checksum function on the encrypted + * buffer. Here we just put a constant value in it. + */ +static void +make_cksum(uint8_t *dst) +{ + int i; + /* + * Assume array is big enough for the checksum. + */ + for (i = 0; i < CHKSUM_LEN; i++) + dst[i] = 'C'; +} + +/* + * make_iv -- + * This is where one would generate the initialization vector. + * Here we just put a constant value in it. + */ +static void +make_iv(uint8_t *dst) +{ + int i; + /* + * Assume array is big enough for the initialization vector. + */ + for (i = 0; i < IV_LEN; i++) + dst[i] = 'I'; +} + +/* + * Rotate encryption functions. + */ +/* + * do_rotate -- + * Perform rot-N on the buffer given. + */ +static void +do_rotate(char *buf, size_t len, int rotn) +{ + uint32_t i; + /* + * Now rotate. + * + * Avoid ctype functions because they behave in unexpected ways, + * particularly when the locale is not "C". + */ + for (i = 0; i < len; i++) { + if ('a' <= buf[i] && buf[i] <= 'z') + buf[i] = ((buf[i] - 'a') + rotn) % 26 + 'a'; + else if ('A' <= buf[i] && buf[i] <= 'Z') + buf[i] = ((buf[i] - 'A') + rotn) % 26 + 'A'; + } +} + +/* + * do_shift -- + * Perform a Vigenere cipher + */ +static void +do_shift(uint8_t *buf, size_t len, u_char *shift, size_t shiftlen) +{ + uint32_t i; + /* + * Now shift. + */ + for (i = 0; i < len; i++) + buf[i] += shift[i % shiftlen]; +} + +/*! [WT_ENCRYPTOR encrypt] */ +/* + * rotn_encrypt -- + * A simple encryption example that passes data through unchanged. + */ +static int +rotn_encrypt(WT_ENCRYPTOR *encryptor, WT_SESSION *session, + uint8_t *src, size_t src_len, + uint8_t *dst, size_t dst_len, + size_t *result_lenp) +{ + ROTN_ENCRYPTOR *rotn_encryptor = (ROTN_ENCRYPTOR *)encryptor; + uint32_t i; + + (void)session; /* Unused */ + + if (dst_len < src_len + CHKSUM_LEN + IV_LEN) + return (ENOMEM); + + /* + * !!! Most implementations would verify any needed + * checksum and initialize the IV here. + */ + i = CHKSUM_LEN + IV_LEN; + memcpy(&dst[i], &src[0], src_len); + /* + * Depending on whether we have a secret key or not, + * call the common rotate or shift function on the text portion + * of the destination buffer. Send in src_len as the length of + * the text. + */ + if (rotn_encryptor->shift_len == 0) + do_rotate((char *)dst + i, src_len, rotn_encryptor->rot_N); + else + do_shift(&dst[i], src_len, + rotn_encryptor->shift_forw, rotn_encryptor->shift_len); + /* + * Checksum the encrypted buffer and add the IV. + */ + i = 0; + make_cksum(&dst[i]); + i += CHKSUM_LEN; + make_iv(&dst[i]); + *result_lenp = dst_len; + return (0); +} +/*! [WT_ENCRYPTOR encrypt] */ + +/*! [WT_ENCRYPTOR decrypt] */ +/* + * rotn_decrypt -- + * A simple decryption example that passes data through unchanged. + */ +static int +rotn_decrypt(WT_ENCRYPTOR *encryptor, WT_SESSION *session, + uint8_t *src, size_t src_len, + uint8_t *dst, size_t dst_len, + size_t *result_lenp) +{ + ROTN_ENCRYPTOR *rotn_encryptor = (ROTN_ENCRYPTOR *)encryptor; + size_t mylen; + uint32_t i; + + (void)session; /* Unused */ + + /* + * Make sure it is big enough. + */ + mylen = src_len - (CHKSUM_LEN + IV_LEN); + if (dst_len < mylen) { + fprintf(stderr, "Rotate: ENOMEM ERROR\n"); + return (ENOMEM); + } + + /* + * !!! Most implementations would verify the checksum here. + */ + /* + * Copy the encrypted data to the destination buffer and then + * decrypt the destination buffer. + */ + i = CHKSUM_LEN + IV_LEN; + memcpy(&dst[0], &src[i], mylen); + /* + * Depending on whether we have a secret key or not, + * call the common rotate or shift function on the text portion + * of the destination buffer. Send in dst_len as the length of + * the text. + */ + /* + * !!! Most implementations would need the IV too. + */ + if (rotn_encryptor->shift_len == 0) + do_rotate((char *)dst, mylen, 26 - rotn_encryptor->rot_N); + else + do_shift(&dst[0], mylen, + rotn_encryptor->shift_back, rotn_encryptor->shift_len); + *result_lenp = mylen; + return (0); +} +/*! [WT_ENCRYPTOR decrypt] */ + +/*! [WT_ENCRYPTOR postsize] */ +/* + * rotn_sizing -- + * A sizing example that returns the header size needed. + */ +static int +rotn_sizing(WT_ENCRYPTOR *encryptor, WT_SESSION *session, + size_t *expansion_constantp) +{ + (void)encryptor; /* Unused parameters */ + (void)session; /* Unused parameters */ + + *expansion_constantp = CHKSUM_LEN + IV_LEN; + return (0); +} +/*! [WT_ENCRYPTOR postsize] */ + +/*! [WT_ENCRYPTOR customize] */ +/* + * rotn_customize -- + * The customize function creates a customized encryptor + */ +static int +rotn_customize(WT_ENCRYPTOR *encryptor, WT_SESSION *session, + WT_CONFIG_ARG *encrypt_config, WT_ENCRYPTOR **customp) +{ + const ROTN_ENCRYPTOR *orig; + ROTN_ENCRYPTOR *rotn_encryptor; + WT_CONFIG_ITEM keyid, secret; + WT_EXTENSION_API *wt_api; + size_t i, len; + int ret, keyid_val; + u_char base; + + ret = 0; + keyid_val = 0; + + orig = (const ROTN_ENCRYPTOR *)encryptor; + wt_api = orig->wt_api; + + if ((rotn_encryptor = calloc(1, sizeof(ROTN_ENCRYPTOR))) == NULL) + return (errno); + *rotn_encryptor = *orig; + rotn_encryptor->keyid = rotn_encryptor->secretkey = NULL; + + /* + * Stash the keyid from the configuration string. + */ + if ((ret = wt_api->config_get(wt_api, session, encrypt_config, + "keyid", &keyid)) == 0 && keyid.len != 0) { + /* + * In this demonstration, we expect keyid to be a number. + */ + if ((keyid_val = atoi(keyid.str)) < 0) { + ret = EINVAL; + goto err; + } + if ((rotn_encryptor->keyid = malloc(keyid.len + 1)) == NULL) { + ret = errno; + goto err; + } + strncpy(rotn_encryptor->keyid, keyid.str, keyid.len + 1); + rotn_encryptor->keyid[keyid.len] = '\0'; + } + + /* + * In this demonstration, the secret key must be alphabetic characters. + * We stash the secret key from the configuration string + * and build some shift bytes to make encryption/decryption easy. + */ + if ((ret = wt_api->config_get(wt_api, session, encrypt_config, + "secretkey", &secret)) == 0 && secret.len != 0) { + len = secret.len; + if ((rotn_encryptor->secretkey = malloc(len + 1)) == NULL || + (rotn_encryptor->shift_forw = malloc(len)) == NULL || + (rotn_encryptor->shift_back = malloc(len)) == NULL) { + ret = errno; + goto err; + } + for (i = 0; i < len; i++) { + if ('a' <= secret.str[i] && secret.str[i] <= 'z') + base = 'a'; + else if ('A' <= secret.str[i] && secret.str[i] <= 'Z') + base = 'A'; + else { + ret = EINVAL; + goto err; + } + base -= (u_char)keyid_val; + rotn_encryptor->shift_forw[i] = + (u_char)secret.str[i] - base; + rotn_encryptor->shift_back[i] = + base - (u_char)secret.str[i]; + } + rotn_encryptor->shift_len = len; + strncpy(rotn_encryptor->secretkey, secret.str, secret.len + 1); + rotn_encryptor->secretkey[secret.len] = '\0'; + } + + /* + * In a real encryptor, we could use some sophisticated key management + * here to map the keyid onto a secret key. + */ + rotn_encryptor->rot_N = keyid_val; + + *customp = (WT_ENCRYPTOR *)rotn_encryptor; + return (0); + +err: free(rotn_encryptor->keyid); + free(rotn_encryptor->secretkey); + free(rotn_encryptor->shift_forw); + free(rotn_encryptor->shift_back); + free(rotn_encryptor); + return (ret); +} +/*! [WT_ENCRYPTOR presize] */ + +/*! [WT_ENCRYPTOR terminate] */ +/* + * rotn_terminate -- + * WiredTiger no-op encryption termination. + */ +static int +rotn_terminate(WT_ENCRYPTOR *encryptor, WT_SESSION *session) +{ + ROTN_ENCRYPTOR *rotn_encryptor = (ROTN_ENCRYPTOR *)encryptor; + + (void)session; /* Unused parameters */ + + /* Free the allocated memory. */ + free(rotn_encryptor->secretkey); + free(rotn_encryptor->keyid); + free(rotn_encryptor->shift_forw); + free(rotn_encryptor->shift_back); + free(encryptor); + return (0); +} +/*! [WT_ENCRYPTOR terminate] */ + +/*! [WT_ENCRYPTOR initialization function] */ +/* + * wiredtiger_extension_init -- + * A simple shared library encryption example. + */ +int +wiredtiger_extension_init(WT_CONNECTION *connection, WT_CONFIG_ARG *config) +{ + ROTN_ENCRYPTOR *rotn_encryptor; + + (void)config; /* Unused parameters */ + + if ((rotn_encryptor = calloc(1, sizeof(ROTN_ENCRYPTOR))) == NULL) + return (errno); + + /* + * Allocate a local encryptor structure, with a WT_ENCRYPTOR structure + * as the first field, allowing us to treat references to either type of + * structure as a reference to the other type. + * + * Heap memory (not static), because it can support multiple databases. + */ + rotn_encryptor->encryptor.encrypt = rotn_encrypt; + rotn_encryptor->encryptor.decrypt = rotn_decrypt; + rotn_encryptor->encryptor.sizing = rotn_sizing; + rotn_encryptor->encryptor.customize = rotn_customize; + rotn_encryptor->encryptor.terminate = rotn_terminate; + + rotn_encryptor->wt_api = connection->get_extension_api(connection); + + /* Load the encryptor */ + return (connection->add_encryptor( + connection, "rotn", (WT_ENCRYPTOR *)rotn_encryptor, NULL)); +} +/*! [WT_ENCRYPTOR initialization function] */ |