* [tarantool-patches] [PATCH v3 0/4] crypto lib @ 2019-05-07 20:53 Vladislav Shpilevoy 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 1/4] crypto: move crypto business into a separate library Vladislav Shpilevoy ` (4 more replies) 0 siblings, 5 replies; 11+ messages in thread From: Vladislav Shpilevoy @ 2019-05-07 20:53 UTC (permalink / raw) To: tarantool-patches; +Cc: kostja, georgy SWIM needs encryption because it transmits packets affecting cluster state and topology, probably via public networks between datacenters. Tarantool hasn't had normal crypto library with useful C API on board until now. OpenSSL was used, but its API is far from simple, and before this patchset it was used in Lua only, via FFI. The patchset moves existing OpenSSL wrappers into a separate library, extends it with pretty API, and replaces some parts of crypto.lua module. It is going to be used by SWIM. Branch: http://github.com/tarantool/tarantool/tree/gerold103/crypto-lib Changes in V3: - Added crypto_stream to replace cipher in crypto.lua module; - Initial vector is passed explicitly to each encrypt(), and is not stored in codec object; - All the same standards are supported as in crypto.lua were. V2: https://www.freelists.org/post/tarantool-patches/PATCH-v2-03-swim-encryption-preparation Vladislav Shpilevoy (4): crypto: move crypto business into a separate library crypto: make exported methods conform code style crypto: implement crypto libary crypto: use crypto library in crypto.lua extra/exports | 16 +- src/CMakeLists.txt | 3 +- src/lib/CMakeLists.txt | 1 + src/lib/core/diag.h | 2 + src/lib/core/exception.cc | 25 +++ src/lib/core/exception.h | 7 + src/lib/crypto/CMakeLists.txt | 5 + src/lib/crypto/crypto.c | 391 ++++++++++++++++++++++++++++++++++ src/lib/crypto/crypto.h | 283 ++++++++++++++++++++++++ src/lua/crypto.c | 73 ------- src/lua/crypto.h | 54 ----- src/lua/crypto.lua | 263 ++++++++++++----------- src/main.cc | 3 + test/app/crypto.result | 114 +++++++--- test/app/crypto.test.lua | 32 ++- test/unit/CMakeLists.txt | 3 + test/unit/crypto.c | 302 ++++++++++++++++++++++++++ test/unit/crypto.result | 132 ++++++++++++ 18 files changed, 1418 insertions(+), 291 deletions(-) create mode 100644 src/lib/crypto/CMakeLists.txt create mode 100644 src/lib/crypto/crypto.c create mode 100644 src/lib/crypto/crypto.h delete mode 100644 src/lua/crypto.c delete mode 100644 src/lua/crypto.h create mode 100644 test/unit/crypto.c create mode 100644 test/unit/crypto.result -- 2.20.1 (Apple Git-117) ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] [PATCH v3 1/4] crypto: move crypto business into a separate library 2019-05-07 20:53 [tarantool-patches] [PATCH v3 0/4] crypto lib Vladislav Shpilevoy @ 2019-05-07 20:53 ` Vladislav Shpilevoy 2019-05-15 7:58 ` [tarantool-patches] " Георгий Кириченко 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 2/4] crypto: make exported methods conform code style Vladislav Shpilevoy ` (3 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: Vladislav Shpilevoy @ 2019-05-07 20:53 UTC (permalink / raw) To: tarantool-patches; +Cc: kostja, georgy Crypto in Tarantool core was implemented and used very poorly uintil now. It was just a one tiny file with one-line wrappers around OpenSSL API. Despite being small and simple, it provided a poweful interface to the Lua land used by Lua 'crypto' public and documented module. Now the time comes when OpenSSL crypto features are wanted on lower level and with richer API, in core library SWIM written in C. This patch moves crypto wrappers into a separate library in src/lib, and drops some methods from the header file because they are never used from C, and needed for exporting only. Needed for #3234 --- src/CMakeLists.txt | 3 +- src/lib/CMakeLists.txt | 1 + src/lib/crypto/CMakeLists.txt | 5 ++ src/lib/crypto/crypto.c | 103 +++++++++++++++++++++++++++++++ src/{lua => lib/crypto}/crypto.h | 18 ++---- src/lua/crypto.c | 73 ---------------------- 6 files changed, 114 insertions(+), 89 deletions(-) create mode 100644 src/lib/crypto/CMakeLists.txt create mode 100644 src/lib/crypto/crypto.c rename src/{lua => lib/crypto}/crypto.h (73%) delete mode 100644 src/lua/crypto.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7c2395517..2cbbf0dcd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -111,7 +111,6 @@ set (server_sources lua/socket.c lua/pickle.c lua/fio.c - lua/crypto.c lua/httpc.c lua/utf8.c lua/info.c @@ -163,7 +162,7 @@ endif() set_source_files_compile_flags(${server_sources}) add_library(server STATIC ${server_sources}) target_link_libraries(server core coll http_parser bit uri uuid swim swim_udp - swim_ev) + swim_ev crypto) # Rule of thumb: if exporting a symbol from a static library, list the # library here. diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index b7179d04f..b306634e7 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(http_parser) add_subdirectory(core) add_subdirectory(uuid) add_subdirectory(coll) +add_subdirectory(crypto) add_subdirectory(swim) if(ENABLE_BUNDLED_MSGPUCK) add_subdirectory(msgpuck EXCLUDE_FROM_ALL) diff --git a/src/lib/crypto/CMakeLists.txt b/src/lib/crypto/CMakeLists.txt new file mode 100644 index 000000000..7e2c6e1d3 --- /dev/null +++ b/src/lib/crypto/CMakeLists.txt @@ -0,0 +1,5 @@ +set(lib_sources crypto.c) + +set_source_files_compile_flags(${lib_sources}) +add_library(crypto STATIC ${lib_sources}) +target_link_libraries(crypto ${OPENSSL_LIBRARIES}) diff --git a/src/lib/crypto/crypto.c b/src/lib/crypto/crypto.c new file mode 100644 index 000000000..28dbc71dd --- /dev/null +++ b/src/lib/crypto/crypto.c @@ -0,0 +1,103 @@ +/* + * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "crypto.h" +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <openssl/hmac.h> + +int +tnt_openssl_init(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + OpenSSL_add_all_digests(); + OpenSSL_add_all_ciphers(); + ERR_load_crypto_strings(); +#else + OPENSSL_init_crypto(0, NULL); + OPENSSL_init_ssl(0, NULL); +#endif + return 0; +} + +int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) +{ + return EVP_CIPHER_key_length(cipher); +} + +int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) +{ + return EVP_CIPHER_iv_length(cipher); +} + +EVP_MD_CTX *tnt_EVP_MD_CTX_new(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + return EVP_MD_CTX_create(); +#else + return EVP_MD_CTX_new(); +#endif +}; + +void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + return EVP_MD_CTX_destroy(ctx); +#else + return EVP_MD_CTX_free(ctx); +#endif +} + +HMAC_CTX *tnt_HMAC_CTX_new(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + HMAC_CTX *ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX)); + if(!ctx){ + return NULL; + } + HMAC_CTX_init(ctx); + return ctx; +#else + return HMAC_CTX_new(); +#endif + +} + +void tnt_HMAC_CTX_free(HMAC_CTX *ctx) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + HMAC_cleanup(ctx); /* Remove key from memory */ + OPENSSL_free(ctx); +#else + HMAC_CTX_free(ctx); +#endif +} diff --git a/src/lua/crypto.h b/src/lib/crypto/crypto.h similarity index 73% rename from src/lua/crypto.h rename to src/lib/crypto/crypto.h index 9808db1e5..9026db322 100644 --- a/src/lua/crypto.h +++ b/src/lib/crypto/crypto.h @@ -1,7 +1,7 @@ -#ifndef INCLUDES_TARANTOOL_LUA_CRYPTO_H -#define INCLUDES_TARANTOOL_LUA_CRYPTO_H +#ifndef TARANTOOL_LIB_CRYPTO_H_INCLUDED +#define TARANTOOL_LIB_CRYPTO_H_INCLUDED /* - * Copyright 2010-2015, Tarantool AUTHORS, please see AUTHORS file. + * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following @@ -31,24 +31,14 @@ * SUCH DAMAGE. */ -#include <openssl/evp.h> -#include <openssl/hmac.h> - #if defined(__cplusplus) extern "C" { #endif -int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher); -int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher); int tnt_openssl_init(); -EVP_MD_CTX *tnt_EVP_MD_CTX_new(void); -void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx); - -HMAC_CTX *tnt_HMAC_CTX_new(void); -void tnt_HMAC_CTX_free(HMAC_CTX *ctx); #if defined(__cplusplus) } #endif -#endif /* INCLUDES_TARANTOOL_LUA_CRYPTO_H */ +#endif /* TARANTOOL_LIB_CRYPTO_H_INCLUDED */ diff --git a/src/lua/crypto.c b/src/lua/crypto.c deleted file mode 100644 index 80adaca78..000000000 --- a/src/lua/crypto.c +++ /dev/null @@ -1,73 +0,0 @@ -#include <openssl/crypto.h> -#include <openssl/evp.h> -#include <openssl/err.h> -#include <openssl/ssl.h> - -/* Helper function for openssl init */ -int tnt_openssl_init() -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - OpenSSL_add_all_digests(); - OpenSSL_add_all_ciphers(); - ERR_load_crypto_strings(); -#else - OPENSSL_init_crypto(0, NULL); - OPENSSL_init_ssl(0, NULL); -#endif - return 0; -} - -/* Helper functions for tarantool crypto api */ - -int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) -{ - return EVP_CIPHER_key_length(cipher); -} - -int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) -{ - return EVP_CIPHER_iv_length(cipher); -} - -EVP_MD_CTX *tnt_EVP_MD_CTX_new(void) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - return EVP_MD_CTX_create(); -#else - return EVP_MD_CTX_new(); -#endif -}; - -void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - return EVP_MD_CTX_destroy(ctx); -#else - return EVP_MD_CTX_free(ctx); -#endif -} - -HMAC_CTX *tnt_HMAC_CTX_new(void) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - HMAC_CTX *ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX)); - if(!ctx){ - return NULL; - } - HMAC_CTX_init(ctx); - return ctx; -#else - return HMAC_CTX_new(); -#endif - -} - -void tnt_HMAC_CTX_free(HMAC_CTX *ctx) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - HMAC_cleanup(ctx); /* Remove key from memory */ - OPENSSL_free(ctx); -#else - HMAC_CTX_free(ctx); -#endif -} -- 2.20.1 (Apple Git-117) ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH v3 1/4] crypto: move crypto business into a separate library 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 1/4] crypto: move crypto business into a separate library Vladislav Shpilevoy @ 2019-05-15 7:58 ` Георгий Кириченко 0 siblings, 0 replies; 11+ messages in thread From: Георгий Кириченко @ 2019-05-15 7:58 UTC (permalink / raw) To: tarantool-patches [-- Attachment #1: Type: text/plain, Size: 9477 bytes --] LGTM On Tuesday, May 7, 2019 11:53:56 PM MSK Vladislav Shpilevoy wrote: > Crypto in Tarantool core was implemented and used very poorly > uintil now. It was just a one tiny file with one-line wrappers > around OpenSSL API. Despite being small and simple, it provided a > poweful interface to the Lua land used by Lua 'crypto' public and > documented module. > > Now the time comes when OpenSSL crypto features are wanted on > lower level and with richer API, in core library SWIM written > in C. This patch moves crypto wrappers into a separate library > in src/lib, and drops some methods from the header file because > they are never used from C, and needed for exporting only. > > Needed for #3234 > --- > src/CMakeLists.txt | 3 +- > src/lib/CMakeLists.txt | 1 + > src/lib/crypto/CMakeLists.txt | 5 ++ > src/lib/crypto/crypto.c | 103 +++++++++++++++++++++++++++++++ > src/{lua => lib/crypto}/crypto.h | 18 ++---- > src/lua/crypto.c | 73 ---------------------- > 6 files changed, 114 insertions(+), 89 deletions(-) > create mode 100644 src/lib/crypto/CMakeLists.txt > create mode 100644 src/lib/crypto/crypto.c > rename src/{lua => lib/crypto}/crypto.h (73%) > delete mode 100644 src/lua/crypto.c > > diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt > index 7c2395517..2cbbf0dcd 100644 > --- a/src/CMakeLists.txt > +++ b/src/CMakeLists.txt > @@ -111,7 +111,6 @@ set (server_sources > lua/socket.c > lua/pickle.c > lua/fio.c > - lua/crypto.c > lua/httpc.c > lua/utf8.c > lua/info.c > @@ -163,7 +162,7 @@ endif() > set_source_files_compile_flags(${server_sources}) > add_library(server STATIC ${server_sources}) > target_link_libraries(server core coll http_parser bit uri uuid swim > swim_udp - swim_ev) > + swim_ev crypto) > > # Rule of thumb: if exporting a symbol from a static library, list the > # library here. > diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt > index b7179d04f..b306634e7 100644 > --- a/src/lib/CMakeLists.txt > +++ b/src/lib/CMakeLists.txt > @@ -10,6 +10,7 @@ add_subdirectory(http_parser) > add_subdirectory(core) > add_subdirectory(uuid) > add_subdirectory(coll) > +add_subdirectory(crypto) > add_subdirectory(swim) > if(ENABLE_BUNDLED_MSGPUCK) > add_subdirectory(msgpuck EXCLUDE_FROM_ALL) > diff --git a/src/lib/crypto/CMakeLists.txt b/src/lib/crypto/CMakeLists.txt > new file mode 100644 > index 000000000..7e2c6e1d3 > --- /dev/null > +++ b/src/lib/crypto/CMakeLists.txt > @@ -0,0 +1,5 @@ > +set(lib_sources crypto.c) > + > +set_source_files_compile_flags(${lib_sources}) > +add_library(crypto STATIC ${lib_sources}) > +target_link_libraries(crypto ${OPENSSL_LIBRARIES}) > diff --git a/src/lib/crypto/crypto.c b/src/lib/crypto/crypto.c > new file mode 100644 > index 000000000..28dbc71dd > --- /dev/null > +++ b/src/lib/crypto/crypto.c > @@ -0,0 +1,103 @@ > +/* > + * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file. > + * > + * Redistribution and use in source and binary forms, with or > + * without modification, are permitted provided that the following > + * conditions are met: > + * > + * 1. Redistributions of source code must retain the above > + * copyright notice, this list of conditions and the > + * following disclaimer. > + * > + * 2. Redistributions in binary form must reproduce the above > + * copyright notice, this list of conditions and the following > + * disclaimer in the documentation and/or other materials > + * provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED > + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL > + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, > + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR > + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF > + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + */ > +#include "crypto.h" > +#include <openssl/crypto.h> > +#include <openssl/evp.h> > +#include <openssl/err.h> > +#include <openssl/ssl.h> > +#include <openssl/hmac.h> > + > +int > +tnt_openssl_init(void) > +{ > +#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) + OpenSSL_add_all_digests(); > + OpenSSL_add_all_ciphers(); > + ERR_load_crypto_strings(); > +#else > + OPENSSL_init_crypto(0, NULL); > + OPENSSL_init_ssl(0, NULL); > +#endif > + return 0; > +} > + > +int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) > +{ > + return EVP_CIPHER_key_length(cipher); > +} > + > +int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) > +{ > + return EVP_CIPHER_iv_length(cipher); > +} > + > +EVP_MD_CTX *tnt_EVP_MD_CTX_new(void) > +{ > +#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) + return EVP_MD_CTX_create(); > +#else > + return EVP_MD_CTX_new(); > +#endif > +}; > + > +void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx) > +{ > +#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) + return EVP_MD_CTX_destroy(ctx); > +#else > + return EVP_MD_CTX_free(ctx); > +#endif > +} > + > +HMAC_CTX *tnt_HMAC_CTX_new(void) > +{ > +#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) + HMAC_CTX *ctx = (HMAC_CTX > *)OPENSSL_malloc(sizeof(HMAC_CTX)); > + if(!ctx){ > + return NULL; > + } > + HMAC_CTX_init(ctx); > + return ctx; > +#else > + return HMAC_CTX_new(); > +#endif > + > +} > + > +void tnt_HMAC_CTX_free(HMAC_CTX *ctx) > +{ > +#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) + HMAC_cleanup(ctx); /* Remove key from > memory */ > + OPENSSL_free(ctx); > +#else > + HMAC_CTX_free(ctx); > +#endif > +} > diff --git a/src/lua/crypto.h b/src/lib/crypto/crypto.h > similarity index 73% > rename from src/lua/crypto.h > rename to src/lib/crypto/crypto.h > index 9808db1e5..9026db322 100644 > --- a/src/lua/crypto.h > +++ b/src/lib/crypto/crypto.h > @@ -1,7 +1,7 @@ > -#ifndef INCLUDES_TARANTOOL_LUA_CRYPTO_H > -#define INCLUDES_TARANTOOL_LUA_CRYPTO_H > +#ifndef TARANTOOL_LIB_CRYPTO_H_INCLUDED > +#define TARANTOOL_LIB_CRYPTO_H_INCLUDED > /* > - * Copyright 2010-2015, Tarantool AUTHORS, please see AUTHORS file. > + * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file. > * > * Redistribution and use in source and binary forms, with or > * without modification, are permitted provided that the following > @@ -31,24 +31,14 @@ > * SUCH DAMAGE. > */ > > -#include <openssl/evp.h> > -#include <openssl/hmac.h> > - > #if defined(__cplusplus) > extern "C" { > #endif > > -int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher); > -int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher); > int tnt_openssl_init(); > -EVP_MD_CTX *tnt_EVP_MD_CTX_new(void); > -void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx); > - > -HMAC_CTX *tnt_HMAC_CTX_new(void); > -void tnt_HMAC_CTX_free(HMAC_CTX *ctx); > > #if defined(__cplusplus) > } > #endif > > -#endif /* INCLUDES_TARANTOOL_LUA_CRYPTO_H */ > +#endif /* TARANTOOL_LIB_CRYPTO_H_INCLUDED */ > diff --git a/src/lua/crypto.c b/src/lua/crypto.c > deleted file mode 100644 > index 80adaca78..000000000 > --- a/src/lua/crypto.c > +++ /dev/null > @@ -1,73 +0,0 @@ > -#include <openssl/crypto.h> > -#include <openssl/evp.h> > -#include <openssl/err.h> > -#include <openssl/ssl.h> > - > -/* Helper function for openssl init */ > -int tnt_openssl_init() > -{ > -#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) - OpenSSL_add_all_digests(); > - OpenSSL_add_all_ciphers(); > - ERR_load_crypto_strings(); > -#else > - OPENSSL_init_crypto(0, NULL); > - OPENSSL_init_ssl(0, NULL); > -#endif > - return 0; > -} > - > -/* Helper functions for tarantool crypto api */ > - > -int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) > -{ > - return EVP_CIPHER_key_length(cipher); > -} > - > -int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) > -{ > - return EVP_CIPHER_iv_length(cipher); > -} > - > -EVP_MD_CTX *tnt_EVP_MD_CTX_new(void) > -{ > -#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) - return EVP_MD_CTX_create(); > -#else > - return EVP_MD_CTX_new(); > -#endif > -}; > - > -void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx) > -{ > -#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) - return EVP_MD_CTX_destroy(ctx); > -#else > - return EVP_MD_CTX_free(ctx); > -#endif > -} > - > -HMAC_CTX *tnt_HMAC_CTX_new(void) > -{ > -#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) - HMAC_CTX *ctx = (HMAC_CTX > *)OPENSSL_malloc(sizeof(HMAC_CTX)); > - if(!ctx){ > - return NULL; > - } > - HMAC_CTX_init(ctx); > - return ctx; > -#else > - return HMAC_CTX_new(); > -#endif > - > -} > - > -void tnt_HMAC_CTX_free(HMAC_CTX *ctx) > -{ > -#if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) - HMAC_cleanup(ctx); /* Remove key from > memory */ > - OPENSSL_free(ctx); > -#else > - HMAC_CTX_free(ctx); > -#endif > -} [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] [PATCH v3 2/4] crypto: make exported methods conform code style 2019-05-07 20:53 [tarantool-patches] [PATCH v3 0/4] crypto lib Vladislav Shpilevoy 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 1/4] crypto: move crypto business into a separate library Vladislav Shpilevoy @ 2019-05-07 20:53 ` Vladislav Shpilevoy 2019-05-15 7:58 ` [tarantool-patches] " Георгий Кириченко 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 3/4] crypto: implement crypto libary Vladislav Shpilevoy ` (2 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: Vladislav Shpilevoy @ 2019-05-07 20:53 UTC (permalink / raw) To: tarantool-patches; +Cc: kostja, georgy Tarantool has a strict rule for naming methods of libraries - use the library name as a prefix. For crypto lib methods it should be 'crypto_', not 'tnt_'. --- extra/exports | 13 ++++++------- src/lib/crypto/crypto.c | 31 +++++++++++++++++++++--------- src/lib/crypto/crypto.h | 6 +++++- src/lua/crypto.lua | 42 ++++++++++++++++++++--------------------- src/main.cc | 3 +++ 5 files changed, 56 insertions(+), 39 deletions(-) diff --git a/extra/exports b/extra/exports index 4f41a17b3..562f8a164 100644 --- a/extra/exports +++ b/extra/exports @@ -77,13 +77,12 @@ space_run_triggers space_bsize box_schema_version -tnt_openssl_init -tnt_EVP_CIPHER_key_length -tnt_EVP_CIPHER_iv_length -tnt_EVP_MD_CTX_new -tnt_EVP_MD_CTX_free -tnt_HMAC_CTX_new -tnt_HMAC_CTX_free +crypto_EVP_CIPHER_key_length +crypto_EVP_CIPHER_iv_length +crypto_EVP_MD_CTX_new +crypto_EVP_MD_CTX_free +crypto_HMAC_CTX_new +crypto_HMAC_CTX_free # Module API diff --git a/src/lib/crypto/crypto.c b/src/lib/crypto/crypto.c index 28dbc71dd..4b192ba2d 100644 --- a/src/lib/crypto/crypto.c +++ b/src/lib/crypto/crypto.c @@ -35,8 +35,8 @@ #include <openssl/ssl.h> #include <openssl/hmac.h> -int -tnt_openssl_init(void) +void +crypto_init(void) { #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) OpenSSL_add_all_digests(); @@ -46,20 +46,30 @@ tnt_openssl_init(void) OPENSSL_init_crypto(0, NULL); OPENSSL_init_ssl(0, NULL); #endif - return 0; } -int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) +void +crypto_free(void) +{ +#ifdef OPENSSL_cleanup + OPENSSL_cleanup(); +#endif +} + +int +crypto_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) { return EVP_CIPHER_key_length(cipher); } -int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) +int +crypto_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) { return EVP_CIPHER_iv_length(cipher); } -EVP_MD_CTX *tnt_EVP_MD_CTX_new(void) +EVP_MD_CTX * +crypto_EVP_MD_CTX_new(void) { #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) return EVP_MD_CTX_create(); @@ -68,7 +78,8 @@ EVP_MD_CTX *tnt_EVP_MD_CTX_new(void) #endif }; -void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx) +void +crypto_EVP_MD_CTX_free(EVP_MD_CTX *ctx) { #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) return EVP_MD_CTX_destroy(ctx); @@ -77,7 +88,8 @@ void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx) #endif } -HMAC_CTX *tnt_HMAC_CTX_new(void) +HMAC_CTX * +crypto_HMAC_CTX_new(void) { #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) HMAC_CTX *ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX)); @@ -92,7 +104,8 @@ HMAC_CTX *tnt_HMAC_CTX_new(void) } -void tnt_HMAC_CTX_free(HMAC_CTX *ctx) +void +crypto_HMAC_CTX_free(HMAC_CTX *ctx) { #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) HMAC_cleanup(ctx); /* Remove key from memory */ diff --git a/src/lib/crypto/crypto.h b/src/lib/crypto/crypto.h index 9026db322..bc807e1a2 100644 --- a/src/lib/crypto/crypto.h +++ b/src/lib/crypto/crypto.h @@ -35,7 +35,11 @@ extern "C" { #endif -int tnt_openssl_init(); +void +crypto_init(void); + +void +crypto_free(void); #if defined(__cplusplus) } diff --git a/src/lua/crypto.lua b/src/lua/crypto.lua index e76370517..30504b1de 100644 --- a/src/lua/crypto.lua +++ b/src/lua/crypto.lua @@ -4,7 +4,6 @@ local ffi = require('ffi') local buffer = require('buffer') ffi.cdef[[ - int tnt_openssl_init(void); /* from openssl/err.h */ unsigned long ERR_get_error(void); char *ERR_error_string(unsigned long e, char *buf); @@ -14,16 +13,16 @@ ffi.cdef[[ typedef struct {} EVP_MD_CTX; typedef struct {} EVP_MD; - EVP_MD_CTX *tnt_EVP_MD_CTX_new(void); - void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx); + EVP_MD_CTX *crypto_EVP_MD_CTX_new(void); + void crypto_EVP_MD_CTX_free(EVP_MD_CTX *ctx); int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s); const EVP_MD *EVP_get_digestbyname(const char *name); typedef struct {} HMAC_CTX; - HMAC_CTX *tnt_HMAC_CTX_new(void); - void tnt_HMAC_CTX_free(HMAC_CTX *ctx); + HMAC_CTX *crypto_HMAC_CTX_new(void); + void crypto_HMAC_CTX_free(HMAC_CTX *ctx); int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len, const EVP_MD *md, ENGINE *impl); int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len); @@ -40,17 +39,14 @@ ffi.cdef[[ int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl); int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); - int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *ctx); - int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher); - int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher); + int crypto_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher); + int crypto_EVP_CIPHER_key_length(const EVP_CIPHER *cipher); int EVP_CIPHER_block_size(const EVP_CIPHER *cipher); const EVP_CIPHER *EVP_get_cipherbyname(const char *name); ]] -ffi.C.tnt_openssl_init(); - local function openssl_err_str() return ffi.string(ffi.C.ERR_error_string(ffi.C.ERR_get_error(), nil)) end @@ -70,11 +66,11 @@ end local digest_mt = {} local function digest_gc(ctx) - ffi.C.tnt_EVP_MD_CTX_free(ctx) + ffi.C.crypto_EVP_MD_CTX_free(ctx) end local function digest_new(digest) - local ctx = ffi.C.tnt_EVP_MD_CTX_new() + local ctx = ffi.C.crypto_EVP_MD_CTX_new() if ctx == nil then return error('Can\'t create digest ctx: ' .. openssl_err_str()) end @@ -121,7 +117,7 @@ local function digest_final(self) end local function digest_free(self) - ffi.C.tnt_EVP_MD_CTX_free(self.ctx) + ffi.C.crypto_EVP_MD_CTX_free(self.ctx) ffi.gc(self.ctx, nil) self.ctx = nil self.initialized = false @@ -141,14 +137,14 @@ local hmacs = digests local hmac_mt = {} local function hmac_gc(ctx) - ffi.C.tnt_HMAC_CTX_free(ctx) + ffi.C.crypto_HMAC_CTX_free(ctx) end local function hmac_new(digest, key) if key == nil then return error('Key should be specified for HMAC operations') end - local ctx = ffi.C.tnt_HMAC_CTX_new() + local ctx = ffi.C.crypto_HMAC_CTX_new() if ctx == nil then return error('Can\'t create HMAC ctx: ' .. openssl_err_str()) end @@ -195,7 +191,7 @@ local function hmac_final(self) end local function hmac_free(self) - ffi.C.tnt_HMAC_CTX_free(self.ctx) + ffi.C.crypto_HMAC_CTX_free(self.ctx) ffi.gc(self.ctx, nil) self.ctx = nil self.initialized = false @@ -234,13 +230,15 @@ local function cipher_gc(ctx) end local function cipher_new(cipher, key, iv, direction) - if key == nil or key:len() ~= ffi.C.tnt_EVP_CIPHER_key_length(cipher) then - return error('Key length should be equal to cipher key length (' - .. tostring(ffi.C.tnt_EVP_CIPHER_key_length(cipher)) .. ' bytes)') + local needed_len = ffi.C.crypto_EVP_CIPHER_key_length(cipher) + if key == nil or key:len() ~= needed_len then + return error('Key length should be equal to cipher key length ('.. + tostring(needed_len)..' bytes)') end - if iv == nil or iv:len() ~= ffi.C.tnt_EVP_CIPHER_iv_length(cipher) then - return error('Initial vector length should be equal to cipher iv length (' - .. tostring(ffi.C.tnt_EVP_CIPHER_iv_length(cipher)) .. ' bytes)') + needed_len = ffi.C.crypto_EVP_CIPHER_iv_length(cipher) + if iv == nil or iv:len() ~= needed_len then + return error('Initial vector length should be equal to cipher iv '.. + 'length ('..tostring(needed_len)..' bytes)') end local ctx = ffi.C.EVP_CIPHER_CTX_new() if ctx == nil then diff --git a/src/main.cc b/src/main.cc index 5ded83223..606c64c13 100644 --- a/src/main.cc +++ b/src/main.cc @@ -75,6 +75,7 @@ #include "box/lua/init.h" /* box_lua_init() */ #include "box/session.h" #include "systemd.h" +#include "crypto/crypto.h" static pid_t master_pid = getpid(); static struct pidfh *pid_file_handle; @@ -621,6 +622,7 @@ tarantool_free(void) memory_free(); random_free(); #endif + crypto_free(); coll_free(); systemd_free(); say_logger_free(); @@ -780,6 +782,7 @@ main(int argc, char **argv) signal_init(); cbus_init(); coll_init(); + crypto_init(); tarantool_lua_init(tarantool_bin, main_argc, main_argv); start_time = ev_monotonic_time(); -- 2.20.1 (Apple Git-117) ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH v3 2/4] crypto: make exported methods conform code style 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 2/4] crypto: make exported methods conform code style Vladislav Shpilevoy @ 2019-05-15 7:58 ` Георгий Кириченко 0 siblings, 0 replies; 11+ messages in thread From: Георгий Кириченко @ 2019-05-15 7:58 UTC (permalink / raw) To: tarantool-patches [-- Attachment #1: Type: text/plain, Size: 9267 bytes --] LGTM On Tuesday, May 7, 2019 11:53:57 PM MSK Vladislav Shpilevoy wrote: > Tarantool has a strict rule for naming methods of libraries - use > the library name as a prefix. For crypto lib methods it should be > 'crypto_', not 'tnt_'. > --- > extra/exports | 13 ++++++------- > src/lib/crypto/crypto.c | 31 +++++++++++++++++++++--------- > src/lib/crypto/crypto.h | 6 +++++- > src/lua/crypto.lua | 42 ++++++++++++++++++++--------------------- > src/main.cc | 3 +++ > 5 files changed, 56 insertions(+), 39 deletions(-) > > diff --git a/extra/exports b/extra/exports > index 4f41a17b3..562f8a164 100644 > --- a/extra/exports > +++ b/extra/exports > @@ -77,13 +77,12 @@ space_run_triggers > space_bsize > box_schema_version > > -tnt_openssl_init > -tnt_EVP_CIPHER_key_length > -tnt_EVP_CIPHER_iv_length > -tnt_EVP_MD_CTX_new > -tnt_EVP_MD_CTX_free > -tnt_HMAC_CTX_new > -tnt_HMAC_CTX_free > +crypto_EVP_CIPHER_key_length > +crypto_EVP_CIPHER_iv_length > +crypto_EVP_MD_CTX_new > +crypto_EVP_MD_CTX_free > +crypto_HMAC_CTX_new > +crypto_HMAC_CTX_free > > # Module API > > diff --git a/src/lib/crypto/crypto.c b/src/lib/crypto/crypto.c > index 28dbc71dd..4b192ba2d 100644 > --- a/src/lib/crypto/crypto.c > +++ b/src/lib/crypto/crypto.c > @@ -35,8 +35,8 @@ > #include <openssl/ssl.h> > #include <openssl/hmac.h> > > -int > -tnt_openssl_init(void) > +void > +crypto_init(void) > { > #if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) OpenSSL_add_all_digests(); > @@ -46,20 +46,30 @@ tnt_openssl_init(void) > OPENSSL_init_crypto(0, NULL); > OPENSSL_init_ssl(0, NULL); > #endif > - return 0; > } > > -int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) > +void > +crypto_free(void) > +{ > +#ifdef OPENSSL_cleanup > + OPENSSL_cleanup(); > +#endif > +} > + > +int > +crypto_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) > { > return EVP_CIPHER_key_length(cipher); > } > > -int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) > +int > +crypto_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) > { > return EVP_CIPHER_iv_length(cipher); > } > > -EVP_MD_CTX *tnt_EVP_MD_CTX_new(void) > +EVP_MD_CTX * > +crypto_EVP_MD_CTX_new(void) > { > #if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) return EVP_MD_CTX_create(); > @@ -68,7 +78,8 @@ EVP_MD_CTX *tnt_EVP_MD_CTX_new(void) > #endif > }; > > -void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx) > +void > +crypto_EVP_MD_CTX_free(EVP_MD_CTX *ctx) > { > #if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) return EVP_MD_CTX_destroy(ctx); > @@ -77,7 +88,8 @@ void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx) > #endif > } > > -HMAC_CTX *tnt_HMAC_CTX_new(void) > +HMAC_CTX * > +crypto_HMAC_CTX_new(void) > { > #if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) HMAC_CTX *ctx = (HMAC_CTX > *)OPENSSL_malloc(sizeof(HMAC_CTX)); > @@ -92,7 +104,8 @@ HMAC_CTX *tnt_HMAC_CTX_new(void) > > } > > -void tnt_HMAC_CTX_free(HMAC_CTX *ctx) > +void > +crypto_HMAC_CTX_free(HMAC_CTX *ctx) > { > #if OPENSSL_VERSION_NUMBER < 0x10100000L || > defined(LIBRESSL_VERSION_NUMBER) HMAC_cleanup(ctx); /* Remove key from > memory */ > diff --git a/src/lib/crypto/crypto.h b/src/lib/crypto/crypto.h > index 9026db322..bc807e1a2 100644 > --- a/src/lib/crypto/crypto.h > +++ b/src/lib/crypto/crypto.h > @@ -35,7 +35,11 @@ > extern "C" { > #endif > > -int tnt_openssl_init(); > +void > +crypto_init(void); > + > +void > +crypto_free(void); > > #if defined(__cplusplus) > } > diff --git a/src/lua/crypto.lua b/src/lua/crypto.lua > index e76370517..30504b1de 100644 > --- a/src/lua/crypto.lua > +++ b/src/lua/crypto.lua > @@ -4,7 +4,6 @@ local ffi = require('ffi') > local buffer = require('buffer') > > ffi.cdef[[ > - int tnt_openssl_init(void); > /* from openssl/err.h */ > unsigned long ERR_get_error(void); > char *ERR_error_string(unsigned long e, char *buf); > @@ -14,16 +13,16 @@ ffi.cdef[[ > > typedef struct {} EVP_MD_CTX; > typedef struct {} EVP_MD; > - EVP_MD_CTX *tnt_EVP_MD_CTX_new(void); > - void tnt_EVP_MD_CTX_free(EVP_MD_CTX *ctx); > + EVP_MD_CTX *crypto_EVP_MD_CTX_new(void); > + void crypto_EVP_MD_CTX_free(EVP_MD_CTX *ctx); > int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE > *impl); int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); > int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int > *s); const EVP_MD *EVP_get_digestbyname(const char *name); > > typedef struct {} HMAC_CTX; > - HMAC_CTX *tnt_HMAC_CTX_new(void); > - void tnt_HMAC_CTX_free(HMAC_CTX *ctx); > + HMAC_CTX *crypto_HMAC_CTX_new(void); > + void crypto_HMAC_CTX_free(HMAC_CTX *ctx); > int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len, > const EVP_MD *md, ENGINE *impl); > int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len); > @@ -40,17 +39,14 @@ ffi.cdef[[ > int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int > *outl, const unsigned char *in, int inl); > int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int > *outl); - int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *ctx); > > - int tnt_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher); > - int tnt_EVP_CIPHER_key_length(const EVP_CIPHER *cipher); > + int crypto_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher); > + int crypto_EVP_CIPHER_key_length(const EVP_CIPHER *cipher); > > int EVP_CIPHER_block_size(const EVP_CIPHER *cipher); > const EVP_CIPHER *EVP_get_cipherbyname(const char *name); > ]] > > -ffi.C.tnt_openssl_init(); > - > local function openssl_err_str() > return ffi.string(ffi.C.ERR_error_string(ffi.C.ERR_get_error(), nil)) > end > @@ -70,11 +66,11 @@ end > local digest_mt = {} > > local function digest_gc(ctx) > - ffi.C.tnt_EVP_MD_CTX_free(ctx) > + ffi.C.crypto_EVP_MD_CTX_free(ctx) > end > > local function digest_new(digest) > - local ctx = ffi.C.tnt_EVP_MD_CTX_new() > + local ctx = ffi.C.crypto_EVP_MD_CTX_new() > if ctx == nil then > return error('Can\'t create digest ctx: ' .. openssl_err_str()) > end > @@ -121,7 +117,7 @@ local function digest_final(self) > end > > local function digest_free(self) > - ffi.C.tnt_EVP_MD_CTX_free(self.ctx) > + ffi.C.crypto_EVP_MD_CTX_free(self.ctx) > ffi.gc(self.ctx, nil) > self.ctx = nil > self.initialized = false > @@ -141,14 +137,14 @@ local hmacs = digests > local hmac_mt = {} > > local function hmac_gc(ctx) > - ffi.C.tnt_HMAC_CTX_free(ctx) > + ffi.C.crypto_HMAC_CTX_free(ctx) > end > > local function hmac_new(digest, key) > if key == nil then > return error('Key should be specified for HMAC operations') > end > - local ctx = ffi.C.tnt_HMAC_CTX_new() > + local ctx = ffi.C.crypto_HMAC_CTX_new() > if ctx == nil then > return error('Can\'t create HMAC ctx: ' .. openssl_err_str()) > end > @@ -195,7 +191,7 @@ local function hmac_final(self) > end > > local function hmac_free(self) > - ffi.C.tnt_HMAC_CTX_free(self.ctx) > + ffi.C.crypto_HMAC_CTX_free(self.ctx) > ffi.gc(self.ctx, nil) > self.ctx = nil > self.initialized = false > @@ -234,13 +230,15 @@ local function cipher_gc(ctx) > end > > local function cipher_new(cipher, key, iv, direction) > - if key == nil or key:len() ~= ffi.C.tnt_EVP_CIPHER_key_length(cipher) > then - return error('Key length should be equal to cipher key length > (' - .. tostring(ffi.C.tnt_EVP_CIPHER_key_length(cipher)) .. ' > bytes)') + local needed_len = ffi.C.crypto_EVP_CIPHER_key_length(cipher) > + if key == nil or key:len() ~= needed_len then > + return error('Key length should be equal to cipher key length ('.. > + tostring(needed_len)..' bytes)') > end > - if iv == nil or iv:len() ~= ffi.C.tnt_EVP_CIPHER_iv_length(cipher) then > - return error('Initial vector length should be equal to cipher iv > length (' - .. tostring(ffi.C.tnt_EVP_CIPHER_iv_length(cipher)) > .. ' bytes)') + needed_len = ffi.C.crypto_EVP_CIPHER_iv_length(cipher) > + if iv == nil or iv:len() ~= needed_len then > + return error('Initial vector length should be equal to cipher iv > '.. + 'length ('..tostring(needed_len)..' bytes)') end > local ctx = ffi.C.EVP_CIPHER_CTX_new() > if ctx == nil then > diff --git a/src/main.cc b/src/main.cc > index 5ded83223..606c64c13 100644 > --- a/src/main.cc > +++ b/src/main.cc > @@ -75,6 +75,7 @@ > #include "box/lua/init.h" /* box_lua_init() */ > #include "box/session.h" > #include "systemd.h" > +#include "crypto/crypto.h" > > static pid_t master_pid = getpid(); > static struct pidfh *pid_file_handle; > @@ -621,6 +622,7 @@ tarantool_free(void) > memory_free(); > random_free(); > #endif > + crypto_free(); > coll_free(); > systemd_free(); > say_logger_free(); > @@ -780,6 +782,7 @@ main(int argc, char **argv) > signal_init(); > cbus_init(); > coll_init(); > + crypto_init(); > tarantool_lua_init(tarantool_bin, main_argc, main_argv); > > start_time = ev_monotonic_time(); [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] [PATCH v3 3/4] crypto: implement crypto libary 2019-05-07 20:53 [tarantool-patches] [PATCH v3 0/4] crypto lib Vladislav Shpilevoy 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 1/4] crypto: move crypto business into a separate library Vladislav Shpilevoy 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 2/4] crypto: make exported methods conform code style Vladislav Shpilevoy @ 2019-05-07 20:53 ` Vladislav Shpilevoy 2019-05-15 7:58 ` [tarantool-patches] " Георгий Кириченко 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 4/4] crypto: use crypto library in crypto.lua Vladislav Shpilevoy 2019-05-15 13:42 ` [tarantool-patches] Re: [PATCH v3 0/4] crypto lib Vladislav Shpilevoy 4 siblings, 1 reply; 11+ messages in thread From: Vladislav Shpilevoy @ 2019-05-07 20:53 UTC (permalink / raw) To: tarantool-patches; +Cc: kostja, georgy OpenSSL API is quite complex and hard to follow, additionally it is very unstable. Encoding/decoding via OpenSSL methods usually consists of multiple calls of a lot of functions. This patch wraps OpenSSL API with one more easy to use and conforming Tarantool code style in scope of crypto library. The traditional OpenSSL API is wrapped as well in a form of crypto_stream object, so OpenSSL API is not cut off. Besides struct crypto_stream the library provides struct crypto_codec which encapsulates all the steps of encryption logic in two short functions: crypto_codec_encrypt/decrypt(iv, in, in_size, out, out_size) A caller can create a needed codec via crypto_codec_new, which now supports all the same algorithms as crypto.lua module. Needed for #3234 --- src/lib/core/diag.h | 2 + src/lib/core/exception.cc | 25 +++ src/lib/core/exception.h | 7 + src/lib/crypto/CMakeLists.txt | 2 +- src/lib/crypto/crypto.c | 287 ++++++++++++++++++++++++++++++++ src/lib/crypto/crypto.h | 235 ++++++++++++++++++++++++++ test/unit/CMakeLists.txt | 3 + test/unit/crypto.c | 302 ++++++++++++++++++++++++++++++++++ test/unit/crypto.result | 132 +++++++++++++++ 9 files changed, 994 insertions(+), 1 deletion(-) create mode 100644 test/unit/crypto.c create mode 100644 test/unit/crypto.result diff --git a/src/lib/core/diag.h b/src/lib/core/diag.h index 78f0cdbdf..fd3831e66 100644 --- a/src/lib/core/diag.h +++ b/src/lib/core/diag.h @@ -251,6 +251,8 @@ struct error * BuildCollationError(const char *file, unsigned line, const char *format, ...); struct error * BuildSwimError(const char *file, unsigned line, const char *format, ...); +struct error * +BuildCryptoError(const char *file, unsigned line, const char *format, ...); struct index_def; diff --git a/src/lib/core/exception.cc b/src/lib/core/exception.cc index 6124c70d0..a6999af43 100644 --- a/src/lib/core/exception.cc +++ b/src/lib/core/exception.cc @@ -280,6 +280,19 @@ SwimError::SwimError(const char *file, unsigned line, const char *format, ...) va_end(ap); } +const struct type_info type_CryptoError = + make_type("CryptoError", &type_Exception); + +CryptoError::CryptoError(const char *file, unsigned line, + const char *format, ...) + : Exception(&type_CryptoError, file, line) +{ + va_list ap; + va_start(ap, format); + error_vformat_msg(this, format, ap); + va_end(ap); +} + #define BuildAlloc(type) \ void *p = malloc(sizeof(type)); \ if (p == NULL) \ @@ -371,6 +384,18 @@ BuildSwimError(const char *file, unsigned line, const char *format, ...) return e; } +struct error * +BuildCryptoError(const char *file, unsigned line, const char *format, ...) +{ + BuildAlloc(CryptoError); + CryptoError *e = new (p) CryptoError(file, line, ""); + va_list ap; + va_start(ap, format); + error_vformat_msg(e, format, ap); + va_end(ap); + return e; +} + struct error * BuildSocketError(const char *file, unsigned line, const char *socketname, const char *format, ...) diff --git a/src/lib/core/exception.h b/src/lib/core/exception.h index 4f5b66f2e..a29281427 100644 --- a/src/lib/core/exception.h +++ b/src/lib/core/exception.h @@ -51,6 +51,7 @@ extern const struct type_info type_IllegalParams; extern const struct type_info type_SystemError; extern const struct type_info type_CollationError; extern const struct type_info type_SwimError; +extern const struct type_info type_CryptoError; const char * exception_get_string(struct error *e, const struct method_info *method); @@ -166,6 +167,12 @@ public: virtual void raise() { throw this; } }; +class CryptoError: public Exception { +public: + CryptoError(const char *file, unsigned line, const char *format, ...); + virtual void raise() { throw this; } +}; + /** * Initialize the exception subsystem. */ diff --git a/src/lib/crypto/CMakeLists.txt b/src/lib/crypto/CMakeLists.txt index 7e2c6e1d3..4e2e5e403 100644 --- a/src/lib/crypto/CMakeLists.txt +++ b/src/lib/crypto/CMakeLists.txt @@ -2,4 +2,4 @@ set(lib_sources crypto.c) set_source_files_compile_flags(${lib_sources}) add_library(crypto STATIC ${lib_sources}) -target_link_libraries(crypto ${OPENSSL_LIBRARIES}) +target_link_libraries(crypto ${OPENSSL_LIBRARIES} core) diff --git a/src/lib/crypto/crypto.c b/src/lib/crypto/crypto.c index 4b192ba2d..c1c8c88ea 100644 --- a/src/lib/crypto/crypto.c +++ b/src/lib/crypto/crypto.c @@ -29,12 +29,299 @@ * SUCH DAMAGE. */ #include "crypto.h" +#include "diag.h" +#include "exception.h" +#include "core/random.h" #include <openssl/crypto.h> #include <openssl/evp.h> #include <openssl/err.h> #include <openssl/ssl.h> #include <openssl/hmac.h> +const char *crypto_algo_strs[] = { + "none", + "AES128", + "AES192", + "AES256", + "DES", +}; + +const char *crypto_mode_strs[] = { + "ECB", + "CBC", + "CFB", + "OFB", +}; + +/** + * Return a EVP_CIPHER object by a given algorithm name and a + * mode value. An algorithm name should be without quotes and with + * a format '<standard>[_<key_size>]', lowercase. For a list of + * supported algorithms see OpenSSL documentation. + */ +#define algo_cipher_by_mode(algo, mode) ({ \ + const EVP_CIPHER *type; \ + switch (mode) { \ + case CRYPTO_MODE_ECB: \ + type = EVP_##algo##_ecb(); \ + break; \ + case CRYPTO_MODE_CBC: \ + type = EVP_##algo##_cbc(); \ + break; \ + case CRYPTO_MODE_CFB: \ + type = EVP_##algo##_cfb(); \ + break; \ + case CRYPTO_MODE_OFB: \ + type = EVP_##algo##_ofb(); \ + break; \ + default: \ + type = NULL; \ + diag_set(CryptoError, "unknown mode"); \ + break; \ + } \ + type; \ +}) + +/** + * Find an OpenSSL cipher object by specified encryption + * algorithm and mode. + */ +static inline const EVP_CIPHER * +evp_cipher_find(enum crypto_algo algo, enum crypto_mode mode) +{ + switch (algo) { + case CRYPTO_ALGO_AES128: + return algo_cipher_by_mode(aes_128, mode); + case CRYPTO_ALGO_AES192: + return algo_cipher_by_mode(aes_192, mode); + case CRYPTO_ALGO_AES256: + return algo_cipher_by_mode(aes_256, mode); + case CRYPTO_ALGO_DES: + return algo_cipher_by_mode(des, mode); + case CRYPTO_ALGO_NONE: + return EVP_enc_null(); + default: + diag_set(CryptoError, "unknown crypto algorithm"); + return NULL; + } +} + +/** + * Set a diag error with the latest OpenSSL error message. It is a + * macro so as to keep untouched line number in the error message. + */ +#define diag_set_OpenSSL() \ + diag_set(CryptoError, "OpenSSL error: %s", \ + ERR_error_string(ERR_get_error(), NULL)) + +/** Stream to encrypt/decrypt data packets step by step. */ +struct crypto_stream { + /** Cipher type. Depends on algorithm and mode. */ + const EVP_CIPHER *cipher; + /** Encryption/decryption context. */ + EVP_CIPHER_CTX *ctx; + /** Stream direction. */ + enum crypto_direction dir; +}; + +struct crypto_stream * +crypto_stream_new(enum crypto_algo algo, enum crypto_mode mode, + enum crypto_direction dir) +{ + const EVP_CIPHER *cipher = evp_cipher_find(algo, mode); + if (cipher == NULL) + return NULL; + struct crypto_stream *s = (struct crypto_stream *) malloc(sizeof(*s)); + if (s == NULL) { + diag_set(OutOfMemory, sizeof(*s), "malloc", "s"); + return NULL; + } + s->ctx = EVP_CIPHER_CTX_new(); + if (s->ctx == NULL) { + free(s); + diag_set_OpenSSL(); + return NULL; + } + s->cipher = cipher; + s->dir = dir; + return s; +} + +int +crypto_stream_begin(struct crypto_stream *s, const char *key, int key_size, + const char *iv, int iv_size) +{ + int need_size = EVP_CIPHER_key_length(s->cipher); + if (key_size != need_size) { + diag_set(CryptoError, "key size expected %d, got %d", + need_size, key_size); + return -1; + } + need_size = EVP_CIPHER_iv_length(s->cipher); + if (iv_size != need_size) { + diag_set(CryptoError, "IV size expected %d, got %d", + need_size, iv_size); + return -1; + } + if (EVP_CIPHER_CTX_cleanup(s->ctx) == 1 && + EVP_CipherInit_ex(s->ctx, s->cipher, NULL, + (const unsigned char *) key, + (const unsigned char *) iv, s->dir) == 1) + return 0; + diag_set_OpenSSL(); + return -1; +} + +int +crypto_stream_append(struct crypto_stream *s, const char *in, int in_size, + char *out, int out_size) +{ + int len, need = in_size + EVP_CIPHER_block_size(s->cipher); + if (need > out_size) + return need; + if (EVP_CipherUpdate(s->ctx, (unsigned char *) out, &len, + (const unsigned char *) in, in_size) == 1) + return len; + diag_set_OpenSSL(); + return -1; +} + +int +crypto_stream_commit(struct crypto_stream *s, char *out, int out_size) +{ + int need = EVP_CIPHER_block_size(s->cipher); + if (need > out_size) + return need; + int len; + if (EVP_CipherFinal_ex(s->ctx, (unsigned char *) out, &len) == 1 && + EVP_CIPHER_CTX_cleanup(s->ctx) == 1) + return len; + diag_set_OpenSSL(); + return -1; +} + +void +crypto_stream_delete(struct crypto_stream *s) +{ + EVP_CIPHER_CTX_free(s->ctx); + free(s); +} + +/** + * OpenSSL codec. Has a constant secret key, provides API to + * generate public keys, keeps OpenSSL contexts cached. + */ +struct crypto_codec { + /** Cipher type. Depends on algorithm and mode. */ + const EVP_CIPHER *cipher; + /** Encryption context. */ + EVP_CIPHER_CTX *ctx; + /** + * Secret key, usually unchanged among multiple data + * packets. + */ + unsigned char key[CRYPTO_MAX_KEY_SIZE]; +}; + +struct crypto_codec * +crypto_codec_new(enum crypto_algo algo, enum crypto_mode mode, + const char *key, int key_size) +{ + const EVP_CIPHER *cipher = evp_cipher_find(algo, mode); + if (cipher == NULL) + return NULL; + int need = EVP_CIPHER_key_length(cipher); + if (key_size != need) { + diag_set(CryptoError, "key size expected %d, got %d", + need, key_size); + return NULL; + } + struct crypto_codec *c = (struct crypto_codec *) malloc(sizeof(*c)); + if (c == NULL) { + diag_set(OutOfMemory, sizeof(*c), "malloc", "c"); + return NULL; + } + c->ctx = EVP_CIPHER_CTX_new(); + if (c->ctx == NULL) { + free(c); + diag_set_OpenSSL(); + return NULL; + } + c->cipher = cipher; + memcpy(c->key, key, key_size); + return c; +} + +int +crypto_codec_gen_iv(struct crypto_codec *c, char *out, int out_size) +{ + int need = EVP_CIPHER_iv_length(c->cipher); + if (out_size >= need) + random_bytes(out, need); + return need; +} + +int +crypto_codec_iv_size(const struct crypto_codec *c) +{ + return EVP_CIPHER_iv_length(c->cipher); +} + +/** Generic implementation of encrypt/decrypt methods. */ +static int +crypto_codec_do_op(struct crypto_codec *c, const char *iv, + const char *in, int in_size, char *out, int out_size, + enum crypto_direction dir) +{ + const unsigned char *uin = (const unsigned char *) in; + const unsigned char *uiv = (const unsigned char *) iv; + unsigned char *uout = (unsigned char *) out; + /* + * Note, that even if in_size is already multiple of block + * size, additional block is still needed. Result is + * almost always bigger than input. OpenSSL API advises to + * provide buffer of one block bigger than the data to + * encode. + */ + int need = in_size + EVP_CIPHER_block_size(c->cipher); + if (need > out_size) + return need; + + int len1 = 0, len2 = 0; + if (EVP_CipherInit_ex(c->ctx, c->cipher, NULL, c->key, uiv, dir) == 1 && + EVP_CipherUpdate(c->ctx, uout, &len1, uin, in_size) == 1 && + EVP_CipherFinal_ex(c->ctx, uout + len1, &len2) == 1 && + EVP_CIPHER_CTX_cleanup(c->ctx) == 1) { + assert(len1 + len2 <= need); + return len1 + len2; + } + diag_set_OpenSSL(); + return -1; +} + +int +crypto_codec_encrypt(struct crypto_codec *c, const char *iv, + const char *in, int in_size, char *out, int out_size) +{ + return crypto_codec_do_op(c, iv, in, in_size, out, out_size, + CRYPTO_DIR_ENCRYPT); +} + +int +crypto_codec_decrypt(struct crypto_codec *c, const char *iv, + const char *in, int in_size, char *out, int out_size) +{ + return crypto_codec_do_op(c, iv, in, in_size, out, out_size, + CRYPTO_DIR_DECRYPT); +} + +void +crypto_codec_delete(struct crypto_codec *c) +{ + EVP_CIPHER_CTX_free(c->ctx); + free(c); +} + void crypto_init(void) { diff --git a/src/lib/crypto/crypto.h b/src/lib/crypto/crypto.h index bc807e1a2..061d67135 100644 --- a/src/lib/crypto/crypto.h +++ b/src/lib/crypto/crypto.h @@ -34,6 +34,241 @@ #if defined(__cplusplus) extern "C" { #endif +/** + * Cryptography library based on OpenSSL. It provides wrappers + * around OpenSSL functions encapsulating compatibility issues and + * problematic non-standard C API. + * + * Most of the cipher algorithms here are block-wise, with a + * secret key and sometimes with an additional public key. A + * secret key should be shared among communicating nodes and never + * transmitted explicitly. + * + * A public key in some algorithms is also called initial vector, + * and is an additional entropy factor. It prevents an attack when + * an attacker somehow learns a purpose or content of one data + * packet and immediately learns purpose of all the same looking + * packets. Initial vector should be generated randomly for each + * data packet, nonetheless it is not private and can be sent + * without encryption. + */ + +enum crypto_algo { + /** None to disable encryption. */ + CRYPTO_ALGO_NONE, + /** + * AES - Advanced Encryption Standard. It is a symmetric + * key block cipher algorithm. AES encodes data in blocks + * of 128 bits, for what it uses a key and an initial + * vector. Key size can be 128, 192 and 256 bits. AES + * still is considered strong cipher. + */ + CRYPTO_ALGO_AES128, + CRYPTO_ALGO_AES192, + CRYPTO_ALGO_AES256, + /** + * DES - Data Encryption Standard. It is a symmetric key + * block cipher algorithm. It has a fixed block size 64 + * bits, a key of size 56 working bits and 8 not used for + * encryption. Its initial vector size is 64 bits as well. + * Note, that DES is not considered secure because of too + * small key size. + */ + CRYPTO_ALGO_DES, + crypto_algo_MAX, +}; + +extern const char *crypto_algo_strs[]; + +enum crypto_mode { + /** + * Electronic CodeBook. The message is split into blocks, + * and each block is encrypted separately. Weak, because + * data patterns leak. But fast, can be parallelized. And + * does not require an initial vector. + * + * C_i = Encrypt(P_i) + */ + CRYPTO_MODE_ECB, + /** + * Cipher Block Chaining. Each block of plain data is + * XORed with the previous encrypted block before being + * encrypted. The most commonly used mode. Encryption + * can't be parallelized because of sequential dependency + * of blocks, but decryption *can be*, because each block + * in fact depends only on single previous encrypted + * block. A decoder does not need to decrypt C_i-1 to + * decrypt C_i. + * + * C_i = Encrypt(P_i ^ C_i-1). + * C_0 = IV + */ + CRYPTO_MODE_CBC, + /** + * Cipher FeedBack. Works similarly to CBC, but a bit + * different. Each block of plain data is encrypted as + * XOR of the plain data with second encryption of the + * previous block. This mode is able to encode a message + * byte by byte, without addition of a padding. Decryption + * can be parallelized. + * + * C_i = Encrypt(C_i-1) ^ P_i + * C_0 = IV + */ + CRYPTO_MODE_CFB, + /** + * Output FeedBack. The same as CFB, but encryption can be + * parallelized partially. + * + * C_i = P_i ^ Encrypt(I_i) + * I_i = Encrypt(I_i-1) + * I_0 = IV + */ + CRYPTO_MODE_OFB, + crypto_mode_MAX, +}; + +extern const char *crypto_mode_strs[]; + +/** + * Values obtained from EVP_CIPHER_*() API, but the constants are + * needed in some places at compilation time. For example, for + * statically sized buffers. + */ +enum { + CRYPTO_AES_BLOCK_SIZE = 16, + CRYPTO_AES_IV_SIZE = 16, + CRYPTO_AES128_KEY_SIZE = 16, + CRYPTO_AES192_KEY_SIZE = 24, + CRYPTO_AES256_KEY_SIZE = 32, + + CRYPTO_DES_BLOCK_SIZE = 8, + CRYPTO_DES_IV_SIZE = 8, + CRYPTO_DES_KEY_SIZE = 8, + + CRYPTO_MAX_KEY_SIZE = 32, + CRYPTO_MAX_IV_SIZE = 16, +}; + +/** + * OpenSSL API provides generic methods to do both encryption and + * decryption depending on one 'int' parameter passed into + * initialization function EVP_CipherInit_ex. Here these constants + * are assigned to more readable names. + */ +enum crypto_direction { + CRYPTO_DIR_DECRYPT = 0, + CRYPTO_DIR_ENCRYPT = 1, +}; + +struct crypto_stream; + +/** + * Crypto stream is an object allowing to encrypt/decrypt data + * packets with different public and secret keys step by step. + */ +struct crypto_stream * +crypto_stream_new(enum crypto_algo algo, enum crypto_mode mode, + enum crypto_direction dir); + +/** + * Start a new data packet. @a key and @a iv are secret and public + * keys respectively. + * @retval 0 Success. + * @retval -1 Error. Diag is set. + */ +int +crypto_stream_begin(struct crypto_stream *s, const char *key, int key_size, + const char *iv, int iv_size); + +/** + * Encrypt/decrypt next part of the current data packet. + * @retval -1 Error. Diag is set. + * @return Number of written bytes. If > @a out_size then nothing + * is written, needed number of bytes is returned. + */ +int +crypto_stream_append(struct crypto_stream *s, const char *in, int in_size, + char *out, int out_size); + +/** + * Finalize the current data packet. Note, a margin can be added + * to the result. + * @retval -1 Error. Diag is set. + * @return Number of written bytes. If > @a out_size then nothing + * is written, needed number of bytes is returned. + */ +int +crypto_stream_commit(struct crypto_stream *s, char *out, int out_size); + +/** Delete a stream, free its memory. */ +void +crypto_stream_delete(struct crypto_stream *s); + +struct crypto_codec; + +/** + * Create a new codec working with a specified @a algo algorithm + * in @a mode. It is remarkable that both algorithm and mode + * strongly affect secrecy and efficiency. Codec is similar to + * stream, but provides shorter and simpler API, and can both + * decrypt and encrypt without recreation. + */ +struct crypto_codec * +crypto_codec_new(enum crypto_algo algo, enum crypto_mode mode, + const char *key, int key_size); + +/** + * Generate a new initial vector, dump it into @a out buffer and + * return number of written (or wanted to be written) bytes. + */ +int +crypto_codec_gen_iv(struct crypto_codec *c, char *out, int out_size); + +/** + * Initial vector size. It is a constant depending on an + * algorithm and mode. + */ +int +crypto_codec_iv_size(const struct crypto_codec *c); + +/** + * Encrypt plain data by codec @a c. + * @param c Codec. + * @param iv Initial vector. + * @param in Plain input data. + * @param in_size Byte size of @a in. + * @param out Output buffer. + * @param out_size Byte size of @a out. + * + * @retval -1 Error. Diag is set. + * @return Number of written bytes. If > @a out_size then nothing + * is written, needed number of bytes is returned. + */ +int +crypto_codec_encrypt(struct crypto_codec *c, const char *iv, + const char *in, int in_size, char *out, int out_size); + +/** + * Decrypt cipher by codec @a c. + * @param c Codec. + * @param iv Initial vector used to encode @a in. + * @param in Cipher to decode. + * @param in_size Byte size of @a in. + * @param out Output buffer. + * @param out_size Byte size of @a out. + * + * @retval -1 Error. Diag is set. + * @return Number of written bytes. If > @a out_size then nothing + * is written, needed number of bytes is returned. + */ +int +crypto_codec_decrypt(struct crypto_codec *c, const char *iv, + const char *in, int in_size, char *out, int out_size); + +/** Delete a codec, free its memory. */ +void +crypto_codec_delete(struct crypto_codec *c); void crypto_init(void); diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 2c8340800..d3cced140 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -217,6 +217,9 @@ target_link_libraries(checkpoint_schedule.test m unit) add_executable(sio.test sio.c) target_link_libraries(sio.test unit core) +add_executable(crypto.test crypto.c) +target_link_libraries(crypto.test crypto unit) + add_executable(swim.test swim.c swim_test_transport.c swim_test_ev.c swim_test_utils.c ${PROJECT_SOURCE_DIR}/src/version.c) target_link_libraries(swim.test unit swim) diff --git a/test/unit/crypto.c b/test/unit/crypto.c new file mode 100644 index 000000000..fad1842f1 --- /dev/null +++ b/test/unit/crypto.c @@ -0,0 +1,302 @@ +/* + * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "crypto/crypto.h" +#include "core/random.h" +#include "unit.h" +#include "trivia/util.h" +#include "memory.h" +#include "fiber.h" + +static void +test_aes128_codec(void) +{ + header(); + plan(20); + + char key[CRYPTO_AES128_KEY_SIZE]; + char iv[CRYPTO_AES_IV_SIZE], iv2[CRYPTO_AES_IV_SIZE]; + random_bytes(key, sizeof(key)); + struct crypto_codec *c = + crypto_codec_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, + key, sizeof(key)); + + int rc = crypto_codec_encrypt(c, NULL, NULL, 10, NULL, 0); + is(rc, 26, "encrypt returns needed number of bytes"); + rc = crypto_codec_encrypt(c, NULL, NULL, 10, NULL, 15); + is(rc, 26, "encrypt does not write anything when too small "\ + "buffer"); + rc = crypto_codec_encrypt(c, NULL, NULL, 0, NULL, 0); + is(rc, 16, "encrypt does not allow 0 sized buffer"); + rc = crypto_codec_encrypt(c, NULL, NULL, 32, NULL, 0); + is(rc, 48, "encrypt requires additional block when buffer "\ + "size is multiple of block size"); + + const char *plain = "plain text"; + int plain_size = strlen(plain) + 1; + char buffer1[128], buffer2[128]; + memset(buffer1, 0, sizeof(buffer1)); + memset(buffer2, 0, sizeof(buffer2)); + int buffer_size = sizeof(buffer1); + int iv_size = crypto_codec_gen_iv(c, iv, sizeof(iv)); + is(iv_size, CRYPTO_AES_IV_SIZE, "AES 126 IV size is %d", + CRYPTO_AES_IV_SIZE); + + rc = crypto_codec_encrypt(c, iv, plain, plain_size, + buffer1, buffer_size); + is(rc, 16, "encrypt works when buffer is big enough"); + rc = crypto_codec_encrypt(c, iv, plain, plain_size, + buffer2, buffer_size); + is(rc, 16, "encrypt returns the same on second call"); + is(memcmp(buffer1, buffer2, rc), 0, "encrypted data is the same"); + isnt(memcmp(buffer1, plain, plain_size), 0, + "and it is not just copied from the plain text"); + + rc = crypto_codec_decrypt(c, iv, NULL, 16, NULL, 0); + is(rc, 32, "decrypt also checks length and returns needed number "\ + "of bytes"); + rc = crypto_codec_decrypt(c, iv, buffer1, 16, buffer2, buffer_size); + is(rc, plain_size, "decrypt returns correct number of bytes"); + is(memcmp(buffer2, plain, plain_size), 0, + "and correctly decrypts data"); + + rc = crypto_codec_decrypt(c, "false iv not meaning anything", + buffer1, 16, buffer2, buffer_size); + is(rc, -1, "decrypt can fail with wrong IV"); + ok(! diag_is_empty(diag_get()), "diag error is set"); + + crypto_codec_gen_iv(c, iv2, sizeof(iv2)); + rc = crypto_codec_encrypt(c, iv2, plain, plain_size, + buffer2, buffer_size); + is(rc, 16, "encrypt with different IV and the same number of written "\ + "bytes returned") + isnt(memcmp(buffer2, buffer1, rc), 0, + "the encrypted data looks different"); + rc = crypto_codec_decrypt(c, iv2, buffer2, 16, buffer1, buffer_size); + is(rc, plain_size, "decrypt works with correct but another IV"); + is(memcmp(buffer1, plain, plain_size), 0, "data is the same"); + + struct crypto_codec *c2 = + crypto_codec_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, + key, sizeof(key)); + rc = crypto_codec_encrypt(c, iv2, plain, plain_size, + buffer1, buffer_size); + memset(buffer2, 0, rc); + rc = crypto_codec_decrypt(c2, iv2, buffer1, rc, buffer2, buffer_size); + is(rc, plain_size, "encrypt with one codec, but decrypt with another "\ + "codec and the same key"); + is(memcmp(plain, buffer2, plain_size), 0, "data is the same"); + + crypto_codec_delete(c2); + crypto_codec_delete(c); + + check_plan(); + footer(); +} + +static void +test_aes128_stress(void) +{ + header(); + plan(1); + char key[CRYPTO_AES128_KEY_SIZE], iv[CRYPTO_AES_IV_SIZE]; + random_bytes(key, sizeof(key)); + struct crypto_codec *c = + crypto_codec_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, + key, sizeof(key)); + + char plain[515], cipher[1024], result[1024]; + int rc, iv_size, size = 10; + for (int size = 10; size < (int) sizeof(plain); size += 10) { + random_bytes(plain, size); + rc = crypto_codec_gen_iv(c, iv, sizeof(iv)); + fail_if(rc != sizeof(iv)); + rc = crypto_codec_encrypt(c, iv, plain, size, + cipher, sizeof(cipher)); + rc = crypto_codec_decrypt(c, iv, cipher, rc, + result, sizeof(result)); + fail_if(memcmp(result, plain, rc) != 0); + } + ok(true, "try encrypt/decrypt on a variety of sizes, keys, and ivs"); + + check_plan(); + crypto_codec_delete(c); + footer(); +} + +static void +test_algo_mode_key(enum crypto_algo algo, enum crypto_mode mode, int key_size) +{ + char key[CRYPTO_MAX_KEY_SIZE], buffer1[128], buffer2[128], plain[128]; + char iv[CRYPTO_MAX_IV_SIZE]; + int plain_size = rand() % 100; + random_bytes(plain, plain_size); + random_bytes(key, key_size); + int buffer_size = sizeof(buffer1); + struct crypto_codec *c = crypto_codec_new(algo, mode, key, key_size); + int iv_size = crypto_codec_gen_iv(c, iv, sizeof(iv)); + is(iv_size, crypto_codec_iv_size(c), "%s %d %s, create iv of size %d", + crypto_algo_strs[algo], key_size, crypto_mode_strs[mode], iv_size); + int encoded = crypto_codec_encrypt(c, iv, plain, plain_size, + buffer1, buffer_size); + ok(encoded >= 0, "encode"); + int decoded = crypto_codec_decrypt(c, iv, buffer1, encoded, + buffer2, buffer_size); + is(decoded, plain_size, "decode"); + is(memcmp(plain, buffer2, plain_size), 0, "data is correct"); + crypto_codec_delete(c); +} + +static inline void +test_algo_key(enum crypto_algo algo, int key_size) +{ + for (enum crypto_mode mode = 0; mode < crypto_mode_MAX; ++mode) + test_algo_mode_key(algo, mode, key_size); +} + +static void +test_each(void) +{ + header(); + plan(80); + + test_algo_key(CRYPTO_ALGO_NONE, 0); + test_algo_key(CRYPTO_ALGO_AES128, CRYPTO_AES128_KEY_SIZE); + test_algo_key(CRYPTO_ALGO_AES192, CRYPTO_AES192_KEY_SIZE); + test_algo_key(CRYPTO_ALGO_AES256, CRYPTO_AES256_KEY_SIZE); + test_algo_key(CRYPTO_ALGO_DES, CRYPTO_DES_KEY_SIZE); + + check_plan(); + footer(); +} + +static void +test_stream(void) +{ + header(); + plan(11); + + char key[CRYPTO_AES128_KEY_SIZE], iv[CRYPTO_AES_IV_SIZE]; + char buffer1[128], buffer2[128]; + random_bytes(key, sizeof(key)); + random_bytes(iv, sizeof(iv)); + struct crypto_stream *encoder = + crypto_stream_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, + CRYPTO_DIR_ENCRYPT); + is(crypto_stream_begin(encoder, key, 3, iv, sizeof(iv)), -1, + "stream begin checks key size"); + is(crypto_stream_begin(encoder, key, sizeof(key), iv, 3), -1, + "stream begin checks iv size"); + is(crypto_stream_begin(encoder, key, sizeof(key), iv, sizeof(iv)), 0, + "begin encryption"); + const char *plain = "long long long long long long long plain text"; + int plain_size = strlen(plain); + + const char *in = plain; + int in_size = plain_size; + char *out = buffer1; + int out_size = sizeof(buffer1); + int encoded = crypto_stream_append(encoder, in, in_size, NULL, 0); + is(encoded, in_size + CRYPTO_AES_BLOCK_SIZE, "append checks size"); + int chunk_size = 5; + int rc = crypto_stream_append(encoder, in, chunk_size, out, out_size); + ok(rc >= 0, "append %d", chunk_size); + in += chunk_size; + in_size -= chunk_size; + out += rc; + out_size -= rc; + encoded = rc; + + chunk_size = 10; + rc = crypto_stream_append(encoder, in, chunk_size, out, out_size); + ok(rc >= 0, "append %d", chunk_size); + in += chunk_size; + in_size -= chunk_size; + out += rc; + out_size -= rc; + encoded += rc; + + rc = crypto_stream_append(encoder, in, in_size, out, out_size); + ok(rc >= 0, "last append %d", in_size); + out += rc; + out_size -= rc; + encoded += rc; + + rc = crypto_stream_commit(encoder, NULL, 0); + is(rc, CRYPTO_AES_BLOCK_SIZE, "commit checks size"); + rc = crypto_stream_commit(encoder, out, out_size); + ok(rc >= 0, "commit %d", rc); + out += rc; + encoded += rc; + + struct crypto_stream *decoder = + crypto_stream_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, + CRYPTO_DIR_DECRYPT); + crypto_stream_begin(decoder, key, sizeof(key), iv, sizeof(iv)); + int decoded = crypto_stream_append(decoder, buffer1, encoded, + buffer2, sizeof(buffer2)); + decoded += crypto_stream_commit(decoder, buffer2 + decoded, + sizeof(buffer2) - decoded); + is(decoded, plain_size, "decoder returned correct size"); + is(memcmp(plain, buffer2, plain_size), 0, "data is decoded correctly"); + + crypto_stream_delete(encoder); + crypto_stream_delete(decoder); + + check_plan(); + footer(); +} + +int +main(void) +{ + header(); + plan(5); + random_init(); + crypto_init(); + memory_init(); + fiber_init(fiber_c_invoke); + + struct crypto_codec *c = crypto_codec_new(-1, -1, "1234", 4); + is(c, NULL, "crypto checks that algo argument is correct"); + + test_aes128_codec(); + test_aes128_stress(); + test_each(); + test_stream(); + + fiber_free(); + memory_free(); + crypto_free(); + random_free(); + int rc = check_plan(); + footer(); + return rc; +} diff --git a/test/unit/crypto.result b/test/unit/crypto.result new file mode 100644 index 000000000..d6ff327e6 --- /dev/null +++ b/test/unit/crypto.result @@ -0,0 +1,132 @@ + *** main *** +1..5 +ok 1 - crypto checks that algo argument is correct + *** test_aes128_codec *** + 1..20 + ok 1 - encrypt returns needed number of bytes + ok 2 - encrypt does not write anything when too small buffer + ok 3 - encrypt does not allow 0 sized buffer + ok 4 - encrypt requires additional block when buffer size is multiple of block size + ok 5 - AES 126 IV size is 16 + ok 6 - encrypt works when buffer is big enough + ok 7 - encrypt returns the same on second call + ok 8 - encrypted data is the same + ok 9 - and it is not just copied from the plain text + ok 10 - decrypt also checks length and returns needed number of bytes + ok 11 - decrypt returns correct number of bytes + ok 12 - and correctly decrypts data + ok 13 - decrypt can fail with wrong IV + ok 14 - diag error is set + ok 15 - encrypt with different IV and the same number of written bytes returned + ok 16 - the encrypted data looks different + ok 17 - decrypt works with correct but another IV + ok 18 - data is the same + ok 19 - encrypt with one codec, but decrypt with another codec and the same key + ok 20 - data is the same +ok 2 - subtests + *** test_aes128_codec: done *** + *** test_aes128_stress *** + 1..1 + ok 1 - try encrypt/decrypt on a variety of sizes, keys, and ivs +ok 3 - subtests + *** test_aes128_stress: done *** + *** test_each *** + 1..80 + ok 1 - none 0 ECB, create iv of size 0 + ok 2 - encode + ok 3 - decode + ok 4 - data is correct + ok 5 - none 0 CBC, create iv of size 0 + ok 6 - encode + ok 7 - decode + ok 8 - data is correct + ok 9 - none 0 CFB, create iv of size 0 + ok 10 - encode + ok 11 - decode + ok 12 - data is correct + ok 13 - none 0 OFB, create iv of size 0 + ok 14 - encode + ok 15 - decode + ok 16 - data is correct + ok 17 - AES128 16 ECB, create iv of size 0 + ok 18 - encode + ok 19 - decode + ok 20 - data is correct + ok 21 - AES128 16 CBC, create iv of size 16 + ok 22 - encode + ok 23 - decode + ok 24 - data is correct + ok 25 - AES128 16 CFB, create iv of size 16 + ok 26 - encode + ok 27 - decode + ok 28 - data is correct + ok 29 - AES128 16 OFB, create iv of size 16 + ok 30 - encode + ok 31 - decode + ok 32 - data is correct + ok 33 - AES192 24 ECB, create iv of size 0 + ok 34 - encode + ok 35 - decode + ok 36 - data is correct + ok 37 - AES192 24 CBC, create iv of size 16 + ok 38 - encode + ok 39 - decode + ok 40 - data is correct + ok 41 - AES192 24 CFB, create iv of size 16 + ok 42 - encode + ok 43 - decode + ok 44 - data is correct + ok 45 - AES192 24 OFB, create iv of size 16 + ok 46 - encode + ok 47 - decode + ok 48 - data is correct + ok 49 - AES256 32 ECB, create iv of size 0 + ok 50 - encode + ok 51 - decode + ok 52 - data is correct + ok 53 - AES256 32 CBC, create iv of size 16 + ok 54 - encode + ok 55 - decode + ok 56 - data is correct + ok 57 - AES256 32 CFB, create iv of size 16 + ok 58 - encode + ok 59 - decode + ok 60 - data is correct + ok 61 - AES256 32 OFB, create iv of size 16 + ok 62 - encode + ok 63 - decode + ok 64 - data is correct + ok 65 - DES 8 ECB, create iv of size 0 + ok 66 - encode + ok 67 - decode + ok 68 - data is correct + ok 69 - DES 8 CBC, create iv of size 8 + ok 70 - encode + ok 71 - decode + ok 72 - data is correct + ok 73 - DES 8 CFB, create iv of size 8 + ok 74 - encode + ok 75 - decode + ok 76 - data is correct + ok 77 - DES 8 OFB, create iv of size 8 + ok 78 - encode + ok 79 - decode + ok 80 - data is correct +ok 4 - subtests + *** test_each: done *** + *** test_stream *** + 1..11 + ok 1 - stream begin checks key size + ok 2 - stream begin checks iv size + ok 3 - begin encryption + ok 4 - append checks size + ok 5 - append 5 + ok 6 - append 10 + ok 7 - last append 30 + ok 8 - commit checks size + ok 9 - commit 16 + ok 10 - decoder returned correct size + ok 11 - data is decoded correctly +ok 5 - subtests + *** test_stream: done *** + *** main: done *** -- 2.20.1 (Apple Git-117) ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH v3 3/4] crypto: implement crypto libary 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 3/4] crypto: implement crypto libary Vladislav Shpilevoy @ 2019-05-15 7:58 ` Георгий Кириченко 0 siblings, 0 replies; 11+ messages in thread From: Георгий Кириченко @ 2019-05-15 7:58 UTC (permalink / raw) To: tarantool-patches [-- Attachment #1: Type: text/plain, Size: 37306 bytes --] LGTM On Tuesday, May 7, 2019 11:53:58 PM MSK Vladislav Shpilevoy wrote: > OpenSSL API is quite complex and hard to follow, additionally it > is very unstable. Encoding/decoding via OpenSSL methods usually > consists of multiple calls of a lot of functions. This patch > wraps OpenSSL API with one more easy to use and conforming > Tarantool code style in scope of crypto library. > > The traditional OpenSSL API is wrapped as well in a form of > crypto_stream object, so OpenSSL API is not cut off. > > Besides struct crypto_stream the library provides struct > crypto_codec which encapsulates all the steps of encryption logic > in two short functions: > > crypto_codec_encrypt/decrypt(iv, in, in_size, out, out_size) > > A caller can create a needed codec via crypto_codec_new, which > now supports all the same algorithms as crypto.lua module. > > Needed for #3234 > --- > src/lib/core/diag.h | 2 + > src/lib/core/exception.cc | 25 +++ > src/lib/core/exception.h | 7 + > src/lib/crypto/CMakeLists.txt | 2 +- > src/lib/crypto/crypto.c | 287 ++++++++++++++++++++++++++++++++ > src/lib/crypto/crypto.h | 235 ++++++++++++++++++++++++++ > test/unit/CMakeLists.txt | 3 + > test/unit/crypto.c | 302 ++++++++++++++++++++++++++++++++++ > test/unit/crypto.result | 132 +++++++++++++++ > 9 files changed, 994 insertions(+), 1 deletion(-) > create mode 100644 test/unit/crypto.c > create mode 100644 test/unit/crypto.result > > diff --git a/src/lib/core/diag.h b/src/lib/core/diag.h > index 78f0cdbdf..fd3831e66 100644 > --- a/src/lib/core/diag.h > +++ b/src/lib/core/diag.h > @@ -251,6 +251,8 @@ struct error * > BuildCollationError(const char *file, unsigned line, const char *format, > ...); struct error * > BuildSwimError(const char *file, unsigned line, const char *format, ...); > +struct error * > +BuildCryptoError(const char *file, unsigned line, const char *format, ...); > > struct index_def; > > diff --git a/src/lib/core/exception.cc b/src/lib/core/exception.cc > index 6124c70d0..a6999af43 100644 > --- a/src/lib/core/exception.cc > +++ b/src/lib/core/exception.cc > @@ -280,6 +280,19 @@ SwimError::SwimError(const char *file, unsigned line, > const char *format, ...) va_end(ap); > } > > +const struct type_info type_CryptoError = > + make_type("CryptoError", &type_Exception); > + > +CryptoError::CryptoError(const char *file, unsigned line, > + const char *format, ...) > + : Exception(&type_CryptoError, file, line) > +{ > + va_list ap; > + va_start(ap, format); > + error_vformat_msg(this, format, ap); > + va_end(ap); > +} > + > #define BuildAlloc(type) \ > void *p = malloc(sizeof(type)); \ > if (p == NULL) \ > @@ -371,6 +384,18 @@ BuildSwimError(const char *file, unsigned line, const > char *format, ...) return e; > } > > +struct error * > +BuildCryptoError(const char *file, unsigned line, const char *format, ...) > +{ > + BuildAlloc(CryptoError); > + CryptoError *e = new (p) CryptoError(file, line, ""); > + va_list ap; > + va_start(ap, format); > + error_vformat_msg(e, format, ap); > + va_end(ap); > + return e; > +} > + > struct error * > BuildSocketError(const char *file, unsigned line, const char *socketname, > const char *format, ...) > diff --git a/src/lib/core/exception.h b/src/lib/core/exception.h > index 4f5b66f2e..a29281427 100644 > --- a/src/lib/core/exception.h > +++ b/src/lib/core/exception.h > @@ -51,6 +51,7 @@ extern const struct type_info type_IllegalParams; > extern const struct type_info type_SystemError; > extern const struct type_info type_CollationError; > extern const struct type_info type_SwimError; > +extern const struct type_info type_CryptoError; > > const char * > exception_get_string(struct error *e, const struct method_info *method); > @@ -166,6 +167,12 @@ public: > virtual void raise() { throw this; } > }; > > +class CryptoError: public Exception { > +public: > + CryptoError(const char *file, unsigned line, const char *format, ...); > + virtual void raise() { throw this; } > +}; > + > /** > * Initialize the exception subsystem. > */ > diff --git a/src/lib/crypto/CMakeLists.txt b/src/lib/crypto/CMakeLists.txt > index 7e2c6e1d3..4e2e5e403 100644 > --- a/src/lib/crypto/CMakeLists.txt > +++ b/src/lib/crypto/CMakeLists.txt > @@ -2,4 +2,4 @@ set(lib_sources crypto.c) > > set_source_files_compile_flags(${lib_sources}) > add_library(crypto STATIC ${lib_sources}) > -target_link_libraries(crypto ${OPENSSL_LIBRARIES}) > +target_link_libraries(crypto ${OPENSSL_LIBRARIES} core) > diff --git a/src/lib/crypto/crypto.c b/src/lib/crypto/crypto.c > index 4b192ba2d..c1c8c88ea 100644 > --- a/src/lib/crypto/crypto.c > +++ b/src/lib/crypto/crypto.c > @@ -29,12 +29,299 @@ > * SUCH DAMAGE. > */ > #include "crypto.h" > +#include "diag.h" > +#include "exception.h" > +#include "core/random.h" > #include <openssl/crypto.h> > #include <openssl/evp.h> > #include <openssl/err.h> > #include <openssl/ssl.h> > #include <openssl/hmac.h> > > +const char *crypto_algo_strs[] = { > + "none", > + "AES128", > + "AES192", > + "AES256", > + "DES", > +}; > + > +const char *crypto_mode_strs[] = { > + "ECB", > + "CBC", > + "CFB", > + "OFB", > +}; > + > +/** > + * Return a EVP_CIPHER object by a given algorithm name and a > + * mode value. An algorithm name should be without quotes and with > + * a format '<standard>[_<key_size>]', lowercase. For a list of > + * supported algorithms see OpenSSL documentation. > + */ > +#define algo_cipher_by_mode(algo, mode) ({ \ > + const EVP_CIPHER *type; \ > + switch (mode) { \ > + case CRYPTO_MODE_ECB: \ > + type = EVP_##algo##_ecb(); \ > + break; \ > + case CRYPTO_MODE_CBC: \ > + type = EVP_##algo##_cbc(); \ > + break; \ > + case CRYPTO_MODE_CFB: \ > + type = EVP_##algo##_cfb(); \ > + break; \ > + case CRYPTO_MODE_OFB: \ > + type = EVP_##algo##_ofb(); \ > + break; \ > + default: \ > + type = NULL; \ > + diag_set(CryptoError, "unknown mode"); \ > + break; \ > + } \ > + type; \ > +}) > + > +/** > + * Find an OpenSSL cipher object by specified encryption > + * algorithm and mode. > + */ > +static inline const EVP_CIPHER * > +evp_cipher_find(enum crypto_algo algo, enum crypto_mode mode) > +{ > + switch (algo) { > + case CRYPTO_ALGO_AES128: > + return algo_cipher_by_mode(aes_128, mode); > + case CRYPTO_ALGO_AES192: > + return algo_cipher_by_mode(aes_192, mode); > + case CRYPTO_ALGO_AES256: > + return algo_cipher_by_mode(aes_256, mode); > + case CRYPTO_ALGO_DES: > + return algo_cipher_by_mode(des, mode); > + case CRYPTO_ALGO_NONE: > + return EVP_enc_null(); > + default: > + diag_set(CryptoError, "unknown crypto algorithm"); > + return NULL; > + } > +} > + > +/** > + * Set a diag error with the latest OpenSSL error message. It is a > + * macro so as to keep untouched line number in the error message. > + */ > +#define diag_set_OpenSSL() \ > + diag_set(CryptoError, "OpenSSL error: %s", \ > + ERR_error_string(ERR_get_error(), NULL)) > + > +/** Stream to encrypt/decrypt data packets step by step. */ > +struct crypto_stream { > + /** Cipher type. Depends on algorithm and mode. */ > + const EVP_CIPHER *cipher; > + /** Encryption/decryption context. */ > + EVP_CIPHER_CTX *ctx; > + /** Stream direction. */ > + enum crypto_direction dir; > +}; > + > +struct crypto_stream * > +crypto_stream_new(enum crypto_algo algo, enum crypto_mode mode, > + enum crypto_direction dir) > +{ > + const EVP_CIPHER *cipher = evp_cipher_find(algo, mode); > + if (cipher == NULL) > + return NULL; > + struct crypto_stream *s = (struct crypto_stream *) malloc(sizeof(*s)); > + if (s == NULL) { > + diag_set(OutOfMemory, sizeof(*s), "malloc", "s"); > + return NULL; > + } > + s->ctx = EVP_CIPHER_CTX_new(); > + if (s->ctx == NULL) { > + free(s); > + diag_set_OpenSSL(); > + return NULL; > + } > + s->cipher = cipher; > + s->dir = dir; > + return s; > +} > + > +int > +crypto_stream_begin(struct crypto_stream *s, const char *key, int key_size, > + const char *iv, int iv_size) > +{ > + int need_size = EVP_CIPHER_key_length(s->cipher); > + if (key_size != need_size) { > + diag_set(CryptoError, "key size expected %d, got %d", > + need_size, key_size); > + return -1; > + } > + need_size = EVP_CIPHER_iv_length(s->cipher); > + if (iv_size != need_size) { > + diag_set(CryptoError, "IV size expected %d, got %d", > + need_size, iv_size); > + return -1; > + } > + if (EVP_CIPHER_CTX_cleanup(s->ctx) == 1 && > + EVP_CipherInit_ex(s->ctx, s->cipher, NULL, > + (const unsigned char *) key, > + (const unsigned char *) iv, s->dir) == 1) > + return 0; > + diag_set_OpenSSL(); > + return -1; > +} > + > +int > +crypto_stream_append(struct crypto_stream *s, const char *in, int in_size, > + char *out, int out_size) > +{ > + int len, need = in_size + EVP_CIPHER_block_size(s->cipher); > + if (need > out_size) > + return need; > + if (EVP_CipherUpdate(s->ctx, (unsigned char *) out, &len, > + (const unsigned char *) in, in_size) == 1) > + return len; > + diag_set_OpenSSL(); > + return -1; > +} > + > +int > +crypto_stream_commit(struct crypto_stream *s, char *out, int out_size) > +{ > + int need = EVP_CIPHER_block_size(s->cipher); > + if (need > out_size) > + return need; > + int len; > + if (EVP_CipherFinal_ex(s->ctx, (unsigned char *) out, &len) == 1 && > + EVP_CIPHER_CTX_cleanup(s->ctx) == 1) > + return len; > + diag_set_OpenSSL(); > + return -1; > +} > + > +void > +crypto_stream_delete(struct crypto_stream *s) > +{ > + EVP_CIPHER_CTX_free(s->ctx); > + free(s); > +} > + > +/** > + * OpenSSL codec. Has a constant secret key, provides API to > + * generate public keys, keeps OpenSSL contexts cached. > + */ > +struct crypto_codec { > + /** Cipher type. Depends on algorithm and mode. */ > + const EVP_CIPHER *cipher; > + /** Encryption context. */ > + EVP_CIPHER_CTX *ctx; > + /** > + * Secret key, usually unchanged among multiple data > + * packets. > + */ > + unsigned char key[CRYPTO_MAX_KEY_SIZE]; > +}; > + > +struct crypto_codec * > +crypto_codec_new(enum crypto_algo algo, enum crypto_mode mode, > + const char *key, int key_size) > +{ > + const EVP_CIPHER *cipher = evp_cipher_find(algo, mode); > + if (cipher == NULL) > + return NULL; > + int need = EVP_CIPHER_key_length(cipher); > + if (key_size != need) { > + diag_set(CryptoError, "key size expected %d, got %d", > + need, key_size); > + return NULL; > + } > + struct crypto_codec *c = (struct crypto_codec *) malloc(sizeof(*c)); > + if (c == NULL) { > + diag_set(OutOfMemory, sizeof(*c), "malloc", "c"); > + return NULL; > + } > + c->ctx = EVP_CIPHER_CTX_new(); > + if (c->ctx == NULL) { > + free(c); > + diag_set_OpenSSL(); > + return NULL; > + } > + c->cipher = cipher; > + memcpy(c->key, key, key_size); > + return c; > +} > + > +int > +crypto_codec_gen_iv(struct crypto_codec *c, char *out, int out_size) > +{ > + int need = EVP_CIPHER_iv_length(c->cipher); > + if (out_size >= need) > + random_bytes(out, need); > + return need; > +} > + > +int > +crypto_codec_iv_size(const struct crypto_codec *c) > +{ > + return EVP_CIPHER_iv_length(c->cipher); > +} > + > +/** Generic implementation of encrypt/decrypt methods. */ > +static int > +crypto_codec_do_op(struct crypto_codec *c, const char *iv, > + const char *in, int in_size, char *out, int out_size, > + enum crypto_direction dir) > +{ > + const unsigned char *uin = (const unsigned char *) in; > + const unsigned char *uiv = (const unsigned char *) iv; > + unsigned char *uout = (unsigned char *) out; > + /* > + * Note, that even if in_size is already multiple of block > + * size, additional block is still needed. Result is > + * almost always bigger than input. OpenSSL API advises to > + * provide buffer of one block bigger than the data to > + * encode. > + */ > + int need = in_size + EVP_CIPHER_block_size(c->cipher); > + if (need > out_size) > + return need; > + > + int len1 = 0, len2 = 0; > + if (EVP_CipherInit_ex(c->ctx, c->cipher, NULL, c->key, uiv, dir) == 1 && > + EVP_CipherUpdate(c->ctx, uout, &len1, uin, in_size) == 1 && > + EVP_CipherFinal_ex(c->ctx, uout + len1, &len2) == 1 && > + EVP_CIPHER_CTX_cleanup(c->ctx) == 1) { > + assert(len1 + len2 <= need); > + return len1 + len2; > + } > + diag_set_OpenSSL(); > + return -1; > +} > + > +int > +crypto_codec_encrypt(struct crypto_codec *c, const char *iv, > + const char *in, int in_size, char *out, int out_size) > +{ > + return crypto_codec_do_op(c, iv, in, in_size, out, out_size, > + CRYPTO_DIR_ENCRYPT); > +} > + > +int > +crypto_codec_decrypt(struct crypto_codec *c, const char *iv, > + const char *in, int in_size, char *out, int out_size) > +{ > + return crypto_codec_do_op(c, iv, in, in_size, out, out_size, > + CRYPTO_DIR_DECRYPT); > +} > + > +void > +crypto_codec_delete(struct crypto_codec *c) > +{ > + EVP_CIPHER_CTX_free(c->ctx); > + free(c); > +} > + > void > crypto_init(void) > { > diff --git a/src/lib/crypto/crypto.h b/src/lib/crypto/crypto.h > index bc807e1a2..061d67135 100644 > --- a/src/lib/crypto/crypto.h > +++ b/src/lib/crypto/crypto.h > @@ -34,6 +34,241 @@ > #if defined(__cplusplus) > extern "C" { > #endif > +/** > + * Cryptography library based on OpenSSL. It provides wrappers > + * around OpenSSL functions encapsulating compatibility issues and > + * problematic non-standard C API. > + * > + * Most of the cipher algorithms here are block-wise, with a > + * secret key and sometimes with an additional public key. A > + * secret key should be shared among communicating nodes and never > + * transmitted explicitly. > + * > + * A public key in some algorithms is also called initial vector, > + * and is an additional entropy factor. It prevents an attack when > + * an attacker somehow learns a purpose or content of one data > + * packet and immediately learns purpose of all the same looking > + * packets. Initial vector should be generated randomly for each > + * data packet, nonetheless it is not private and can be sent > + * without encryption. > + */ > + > +enum crypto_algo { > + /** None to disable encryption. */ > + CRYPTO_ALGO_NONE, > + /** > + * AES - Advanced Encryption Standard. It is a symmetric > + * key block cipher algorithm. AES encodes data in blocks > + * of 128 bits, for what it uses a key and an initial > + * vector. Key size can be 128, 192 and 256 bits. AES > + * still is considered strong cipher. > + */ > + CRYPTO_ALGO_AES128, > + CRYPTO_ALGO_AES192, > + CRYPTO_ALGO_AES256, > + /** > + * DES - Data Encryption Standard. It is a symmetric key > + * block cipher algorithm. It has a fixed block size 64 > + * bits, a key of size 56 working bits and 8 not used for > + * encryption. Its initial vector size is 64 bits as well. > + * Note, that DES is not considered secure because of too > + * small key size. > + */ > + CRYPTO_ALGO_DES, > + crypto_algo_MAX, > +}; > + > +extern const char *crypto_algo_strs[]; > + > +enum crypto_mode { > + /** > + * Electronic CodeBook. The message is split into blocks, > + * and each block is encrypted separately. Weak, because > + * data patterns leak. But fast, can be parallelized. And > + * does not require an initial vector. > + * > + * C_i = Encrypt(P_i) > + */ > + CRYPTO_MODE_ECB, > + /** > + * Cipher Block Chaining. Each block of plain data is > + * XORed with the previous encrypted block before being > + * encrypted. The most commonly used mode. Encryption > + * can't be parallelized because of sequential dependency > + * of blocks, but decryption *can be*, because each block > + * in fact depends only on single previous encrypted > + * block. A decoder does not need to decrypt C_i-1 to > + * decrypt C_i. > + * > + * C_i = Encrypt(P_i ^ C_i-1). > + * C_0 = IV > + */ > + CRYPTO_MODE_CBC, > + /** > + * Cipher FeedBack. Works similarly to CBC, but a bit > + * different. Each block of plain data is encrypted as > + * XOR of the plain data with second encryption of the > + * previous block. This mode is able to encode a message > + * byte by byte, without addition of a padding. Decryption > + * can be parallelized. > + * > + * C_i = Encrypt(C_i-1) ^ P_i > + * C_0 = IV > + */ > + CRYPTO_MODE_CFB, > + /** > + * Output FeedBack. The same as CFB, but encryption can be > + * parallelized partially. > + * > + * C_i = P_i ^ Encrypt(I_i) > + * I_i = Encrypt(I_i-1) > + * I_0 = IV > + */ > + CRYPTO_MODE_OFB, > + crypto_mode_MAX, > +}; > + > +extern const char *crypto_mode_strs[]; > + > +/** > + * Values obtained from EVP_CIPHER_*() API, but the constants are > + * needed in some places at compilation time. For example, for > + * statically sized buffers. > + */ > +enum { > + CRYPTO_AES_BLOCK_SIZE = 16, > + CRYPTO_AES_IV_SIZE = 16, > + CRYPTO_AES128_KEY_SIZE = 16, > + CRYPTO_AES192_KEY_SIZE = 24, > + CRYPTO_AES256_KEY_SIZE = 32, > + > + CRYPTO_DES_BLOCK_SIZE = 8, > + CRYPTO_DES_IV_SIZE = 8, > + CRYPTO_DES_KEY_SIZE = 8, > + > + CRYPTO_MAX_KEY_SIZE = 32, > + CRYPTO_MAX_IV_SIZE = 16, > +}; > + > +/** > + * OpenSSL API provides generic methods to do both encryption and > + * decryption depending on one 'int' parameter passed into > + * initialization function EVP_CipherInit_ex. Here these constants > + * are assigned to more readable names. > + */ > +enum crypto_direction { > + CRYPTO_DIR_DECRYPT = 0, > + CRYPTO_DIR_ENCRYPT = 1, > +}; > + > +struct crypto_stream; > + > +/** > + * Crypto stream is an object allowing to encrypt/decrypt data > + * packets with different public and secret keys step by step. > + */ > +struct crypto_stream * > +crypto_stream_new(enum crypto_algo algo, enum crypto_mode mode, > + enum crypto_direction dir); > + > +/** > + * Start a new data packet. @a key and @a iv are secret and public > + * keys respectively. > + * @retval 0 Success. > + * @retval -1 Error. Diag is set. > + */ > +int > +crypto_stream_begin(struct crypto_stream *s, const char *key, int key_size, > + const char *iv, int iv_size); > + > +/** > + * Encrypt/decrypt next part of the current data packet. > + * @retval -1 Error. Diag is set. > + * @return Number of written bytes. If > @a out_size then nothing > + * is written, needed number of bytes is returned. > + */ > +int > +crypto_stream_append(struct crypto_stream *s, const char *in, int in_size, > + char *out, int out_size); > + > +/** > + * Finalize the current data packet. Note, a margin can be added > + * to the result. > + * @retval -1 Error. Diag is set. > + * @return Number of written bytes. If > @a out_size then nothing > + * is written, needed number of bytes is returned. > + */ > +int > +crypto_stream_commit(struct crypto_stream *s, char *out, int out_size); > + > +/** Delete a stream, free its memory. */ > +void > +crypto_stream_delete(struct crypto_stream *s); > + > +struct crypto_codec; > + > +/** > + * Create a new codec working with a specified @a algo algorithm > + * in @a mode. It is remarkable that both algorithm and mode > + * strongly affect secrecy and efficiency. Codec is similar to > + * stream, but provides shorter and simpler API, and can both > + * decrypt and encrypt without recreation. > + */ > +struct crypto_codec * > +crypto_codec_new(enum crypto_algo algo, enum crypto_mode mode, > + const char *key, int key_size); > + > +/** > + * Generate a new initial vector, dump it into @a out buffer and > + * return number of written (or wanted to be written) bytes. > + */ > +int > +crypto_codec_gen_iv(struct crypto_codec *c, char *out, int out_size); > + > +/** > + * Initial vector size. It is a constant depending on an > + * algorithm and mode. > + */ > +int > +crypto_codec_iv_size(const struct crypto_codec *c); > + > +/** > + * Encrypt plain data by codec @a c. > + * @param c Codec. > + * @param iv Initial vector. > + * @param in Plain input data. > + * @param in_size Byte size of @a in. > + * @param out Output buffer. > + * @param out_size Byte size of @a out. > + * > + * @retval -1 Error. Diag is set. > + * @return Number of written bytes. If > @a out_size then nothing > + * is written, needed number of bytes is returned. > + */ > +int > +crypto_codec_encrypt(struct crypto_codec *c, const char *iv, > + const char *in, int in_size, char *out, int out_size); > + > +/** > + * Decrypt cipher by codec @a c. > + * @param c Codec. > + * @param iv Initial vector used to encode @a in. > + * @param in Cipher to decode. > + * @param in_size Byte size of @a in. > + * @param out Output buffer. > + * @param out_size Byte size of @a out. > + * > + * @retval -1 Error. Diag is set. > + * @return Number of written bytes. If > @a out_size then nothing > + * is written, needed number of bytes is returned. > + */ > +int > +crypto_codec_decrypt(struct crypto_codec *c, const char *iv, > + const char *in, int in_size, char *out, int out_size); > + > +/** Delete a codec, free its memory. */ > +void > +crypto_codec_delete(struct crypto_codec *c); > > void > crypto_init(void); > diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt > index 2c8340800..d3cced140 100644 > --- a/test/unit/CMakeLists.txt > +++ b/test/unit/CMakeLists.txt > @@ -217,6 +217,9 @@ target_link_libraries(checkpoint_schedule.test m unit) > add_executable(sio.test sio.c) > target_link_libraries(sio.test unit core) > > +add_executable(crypto.test crypto.c) > +target_link_libraries(crypto.test crypto unit) > + > add_executable(swim.test swim.c swim_test_transport.c swim_test_ev.c > swim_test_utils.c ${PROJECT_SOURCE_DIR}/src/version.c) > target_link_libraries(swim.test unit swim) > diff --git a/test/unit/crypto.c b/test/unit/crypto.c > new file mode 100644 > index 000000000..fad1842f1 > --- /dev/null > +++ b/test/unit/crypto.c > @@ -0,0 +1,302 @@ > +/* > + * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file. > + * > + * Redistribution and use in source and binary forms, with or > + * without modification, are permitted provided that the following > + * conditions are met: > + * > + * 1. Redistributions of source code must retain the above > + * copyright notice, this list of conditions and the > + * following disclaimer. > + * > + * 2. Redistributions in binary form must reproduce the above > + * copyright notice, this list of conditions and the following > + * disclaimer in the documentation and/or other materials > + * provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED > + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL > + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, > + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR > + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF > + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF > + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + */ > +#include "crypto/crypto.h" > +#include "core/random.h" > +#include "unit.h" > +#include "trivia/util.h" > +#include "memory.h" > +#include "fiber.h" > + > +static void > +test_aes128_codec(void) > +{ > + header(); > + plan(20); > + > + char key[CRYPTO_AES128_KEY_SIZE]; > + char iv[CRYPTO_AES_IV_SIZE], iv2[CRYPTO_AES_IV_SIZE]; > + random_bytes(key, sizeof(key)); > + struct crypto_codec *c = > + crypto_codec_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, > + key, sizeof(key)); > + > + int rc = crypto_codec_encrypt(c, NULL, NULL, 10, NULL, 0); > + is(rc, 26, "encrypt returns needed number of bytes"); > + rc = crypto_codec_encrypt(c, NULL, NULL, 10, NULL, 15); > + is(rc, 26, "encrypt does not write anything when too small "\ > + "buffer"); > + rc = crypto_codec_encrypt(c, NULL, NULL, 0, NULL, 0); > + is(rc, 16, "encrypt does not allow 0 sized buffer"); > + rc = crypto_codec_encrypt(c, NULL, NULL, 32, NULL, 0); > + is(rc, 48, "encrypt requires additional block when buffer "\ > + "size is multiple of block size"); > + > + const char *plain = "plain text"; > + int plain_size = strlen(plain) + 1; > + char buffer1[128], buffer2[128]; > + memset(buffer1, 0, sizeof(buffer1)); > + memset(buffer2, 0, sizeof(buffer2)); > + int buffer_size = sizeof(buffer1); > + int iv_size = crypto_codec_gen_iv(c, iv, sizeof(iv)); > + is(iv_size, CRYPTO_AES_IV_SIZE, "AES 126 IV size is %d", > + CRYPTO_AES_IV_SIZE); > + > + rc = crypto_codec_encrypt(c, iv, plain, plain_size, > + buffer1, buffer_size); > + is(rc, 16, "encrypt works when buffer is big enough"); > + rc = crypto_codec_encrypt(c, iv, plain, plain_size, > + buffer2, buffer_size); > + is(rc, 16, "encrypt returns the same on second call"); > + is(memcmp(buffer1, buffer2, rc), 0, "encrypted data is the same"); > + isnt(memcmp(buffer1, plain, plain_size), 0, > + "and it is not just copied from the plain text"); > + > + rc = crypto_codec_decrypt(c, iv, NULL, 16, NULL, 0); > + is(rc, 32, "decrypt also checks length and returns needed number "\ > + "of bytes"); > + rc = crypto_codec_decrypt(c, iv, buffer1, 16, buffer2, buffer_size); > + is(rc, plain_size, "decrypt returns correct number of bytes"); > + is(memcmp(buffer2, plain, plain_size), 0, > + "and correctly decrypts data"); > + > + rc = crypto_codec_decrypt(c, "false iv not meaning anything", > + buffer1, 16, buffer2, buffer_size); > + is(rc, -1, "decrypt can fail with wrong IV"); > + ok(! diag_is_empty(diag_get()), "diag error is set"); > + > + crypto_codec_gen_iv(c, iv2, sizeof(iv2)); > + rc = crypto_codec_encrypt(c, iv2, plain, plain_size, > + buffer2, buffer_size); > + is(rc, 16, "encrypt with different IV and the same number of written "\ > + "bytes returned") > + isnt(memcmp(buffer2, buffer1, rc), 0, > + "the encrypted data looks different"); > + rc = crypto_codec_decrypt(c, iv2, buffer2, 16, buffer1, buffer_size); > + is(rc, plain_size, "decrypt works with correct but another IV"); > + is(memcmp(buffer1, plain, plain_size), 0, "data is the same"); > + > + struct crypto_codec *c2 = > + crypto_codec_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, > + key, sizeof(key)); > + rc = crypto_codec_encrypt(c, iv2, plain, plain_size, > + buffer1, buffer_size); > + memset(buffer2, 0, rc); > + rc = crypto_codec_decrypt(c2, iv2, buffer1, rc, buffer2, buffer_size); > + is(rc, plain_size, "encrypt with one codec, but decrypt with another "\ > + "codec and the same key"); > + is(memcmp(plain, buffer2, plain_size), 0, "data is the same"); > + > + crypto_codec_delete(c2); > + crypto_codec_delete(c); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_aes128_stress(void) > +{ > + header(); > + plan(1); > + char key[CRYPTO_AES128_KEY_SIZE], iv[CRYPTO_AES_IV_SIZE]; > + random_bytes(key, sizeof(key)); > + struct crypto_codec *c = > + crypto_codec_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, > + key, sizeof(key)); > + > + char plain[515], cipher[1024], result[1024]; > + int rc, iv_size, size = 10; > + for (int size = 10; size < (int) sizeof(plain); size += 10) { > + random_bytes(plain, size); > + rc = crypto_codec_gen_iv(c, iv, sizeof(iv)); > + fail_if(rc != sizeof(iv)); > + rc = crypto_codec_encrypt(c, iv, plain, size, > + cipher, sizeof(cipher)); > + rc = crypto_codec_decrypt(c, iv, cipher, rc, > + result, sizeof(result)); > + fail_if(memcmp(result, plain, rc) != 0); > + } > + ok(true, "try encrypt/decrypt on a variety of sizes, keys, and ivs"); > + > + check_plan(); > + crypto_codec_delete(c); > + footer(); > +} > + > +static void > +test_algo_mode_key(enum crypto_algo algo, enum crypto_mode mode, int > key_size) +{ > + char key[CRYPTO_MAX_KEY_SIZE], buffer1[128], buffer2[128], plain[128]; > + char iv[CRYPTO_MAX_IV_SIZE]; > + int plain_size = rand() % 100; > + random_bytes(plain, plain_size); > + random_bytes(key, key_size); > + int buffer_size = sizeof(buffer1); > + struct crypto_codec *c = crypto_codec_new(algo, mode, key, key_size); > + int iv_size = crypto_codec_gen_iv(c, iv, sizeof(iv)); > + is(iv_size, crypto_codec_iv_size(c), "%s %d %s, create iv of size %d", > + crypto_algo_strs[algo], key_size, crypto_mode_strs[mode], iv_size); > + int encoded = crypto_codec_encrypt(c, iv, plain, plain_size, > + buffer1, buffer_size); > + ok(encoded >= 0, "encode"); > + int decoded = crypto_codec_decrypt(c, iv, buffer1, encoded, > + buffer2, buffer_size); > + is(decoded, plain_size, "decode"); > + is(memcmp(plain, buffer2, plain_size), 0, "data is correct"); > + crypto_codec_delete(c); > +} > + > +static inline void > +test_algo_key(enum crypto_algo algo, int key_size) > +{ > + for (enum crypto_mode mode = 0; mode < crypto_mode_MAX; ++mode) > + test_algo_mode_key(algo, mode, key_size); > +} > + > +static void > +test_each(void) > +{ > + header(); > + plan(80); > + > + test_algo_key(CRYPTO_ALGO_NONE, 0); > + test_algo_key(CRYPTO_ALGO_AES128, CRYPTO_AES128_KEY_SIZE); > + test_algo_key(CRYPTO_ALGO_AES192, CRYPTO_AES192_KEY_SIZE); > + test_algo_key(CRYPTO_ALGO_AES256, CRYPTO_AES256_KEY_SIZE); > + test_algo_key(CRYPTO_ALGO_DES, CRYPTO_DES_KEY_SIZE); > + > + check_plan(); > + footer(); > +} > + > +static void > +test_stream(void) > +{ > + header(); > + plan(11); > + > + char key[CRYPTO_AES128_KEY_SIZE], iv[CRYPTO_AES_IV_SIZE]; > + char buffer1[128], buffer2[128]; > + random_bytes(key, sizeof(key)); > + random_bytes(iv, sizeof(iv)); > + struct crypto_stream *encoder = > + crypto_stream_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, > + CRYPTO_DIR_ENCRYPT); > + is(crypto_stream_begin(encoder, key, 3, iv, sizeof(iv)), -1, > + "stream begin checks key size"); > + is(crypto_stream_begin(encoder, key, sizeof(key), iv, 3), -1, > + "stream begin checks iv size"); > + is(crypto_stream_begin(encoder, key, sizeof(key), iv, sizeof(iv)), 0, > + "begin encryption"); > + const char *plain = "long long long long long long long plain text"; > + int plain_size = strlen(plain); > + > + const char *in = plain; > + int in_size = plain_size; > + char *out = buffer1; > + int out_size = sizeof(buffer1); > + int encoded = crypto_stream_append(encoder, in, in_size, NULL, 0); > + is(encoded, in_size + CRYPTO_AES_BLOCK_SIZE, "append checks size"); > + int chunk_size = 5; > + int rc = crypto_stream_append(encoder, in, chunk_size, out, out_size); > + ok(rc >= 0, "append %d", chunk_size); > + in += chunk_size; > + in_size -= chunk_size; > + out += rc; > + out_size -= rc; > + encoded = rc; > + > + chunk_size = 10; > + rc = crypto_stream_append(encoder, in, chunk_size, out, out_size); > + ok(rc >= 0, "append %d", chunk_size); > + in += chunk_size; > + in_size -= chunk_size; > + out += rc; > + out_size -= rc; > + encoded += rc; > + > + rc = crypto_stream_append(encoder, in, in_size, out, out_size); > + ok(rc >= 0, "last append %d", in_size); > + out += rc; > + out_size -= rc; > + encoded += rc; > + > + rc = crypto_stream_commit(encoder, NULL, 0); > + is(rc, CRYPTO_AES_BLOCK_SIZE, "commit checks size"); > + rc = crypto_stream_commit(encoder, out, out_size); > + ok(rc >= 0, "commit %d", rc); > + out += rc; > + encoded += rc; > + > + struct crypto_stream *decoder = > + crypto_stream_new(CRYPTO_ALGO_AES128, CRYPTO_MODE_CBC, > + CRYPTO_DIR_DECRYPT); > + crypto_stream_begin(decoder, key, sizeof(key), iv, sizeof(iv)); > + int decoded = crypto_stream_append(decoder, buffer1, encoded, > + buffer2, sizeof(buffer2)); > + decoded += crypto_stream_commit(decoder, buffer2 + decoded, > + sizeof(buffer2) - decoded); > + is(decoded, plain_size, "decoder returned correct size"); > + is(memcmp(plain, buffer2, plain_size), 0, "data is decoded correctly"); > + > + crypto_stream_delete(encoder); > + crypto_stream_delete(decoder); > + > + check_plan(); > + footer(); > +} > + > +int > +main(void) > +{ > + header(); > + plan(5); > + random_init(); > + crypto_init(); > + memory_init(); > + fiber_init(fiber_c_invoke); > + > + struct crypto_codec *c = crypto_codec_new(-1, -1, "1234", 4); > + is(c, NULL, "crypto checks that algo argument is correct"); > + > + test_aes128_codec(); > + test_aes128_stress(); > + test_each(); > + test_stream(); > + > + fiber_free(); > + memory_free(); > + crypto_free(); > + random_free(); > + int rc = check_plan(); > + footer(); > + return rc; > +} > diff --git a/test/unit/crypto.result b/test/unit/crypto.result > new file mode 100644 > index 000000000..d6ff327e6 > --- /dev/null > +++ b/test/unit/crypto.result > @@ -0,0 +1,132 @@ > + *** main *** > +1..5 > +ok 1 - crypto checks that algo argument is correct > + *** test_aes128_codec *** > + 1..20 > + ok 1 - encrypt returns needed number of bytes > + ok 2 - encrypt does not write anything when too small buffer > + ok 3 - encrypt does not allow 0 sized buffer > + ok 4 - encrypt requires additional block when buffer size is multiple > of block size + ok 5 - AES 126 IV size is 16 > + ok 6 - encrypt works when buffer is big enough > + ok 7 - encrypt returns the same on second call > + ok 8 - encrypted data is the same > + ok 9 - and it is not just copied from the plain text > + ok 10 - decrypt also checks length and returns needed number of bytes > + ok 11 - decrypt returns correct number of bytes > + ok 12 - and correctly decrypts data > + ok 13 - decrypt can fail with wrong IV > + ok 14 - diag error is set > + ok 15 - encrypt with different IV and the same number of written bytes > returned + ok 16 - the encrypted data looks different > + ok 17 - decrypt works with correct but another IV > + ok 18 - data is the same > + ok 19 - encrypt with one codec, but decrypt with another codec and the > same key + ok 20 - data is the same > +ok 2 - subtests > + *** test_aes128_codec: done *** > + *** test_aes128_stress *** > + 1..1 > + ok 1 - try encrypt/decrypt on a variety of sizes, keys, and ivs > +ok 3 - subtests > + *** test_aes128_stress: done *** > + *** test_each *** > + 1..80 > + ok 1 - none 0 ECB, create iv of size 0 > + ok 2 - encode > + ok 3 - decode > + ok 4 - data is correct > + ok 5 - none 0 CBC, create iv of size 0 > + ok 6 - encode > + ok 7 - decode > + ok 8 - data is correct > + ok 9 - none 0 CFB, create iv of size 0 > + ok 10 - encode > + ok 11 - decode > + ok 12 - data is correct > + ok 13 - none 0 OFB, create iv of size 0 > + ok 14 - encode > + ok 15 - decode > + ok 16 - data is correct > + ok 17 - AES128 16 ECB, create iv of size 0 > + ok 18 - encode > + ok 19 - decode > + ok 20 - data is correct > + ok 21 - AES128 16 CBC, create iv of size 16 > + ok 22 - encode > + ok 23 - decode > + ok 24 - data is correct > + ok 25 - AES128 16 CFB, create iv of size 16 > + ok 26 - encode > + ok 27 - decode > + ok 28 - data is correct > + ok 29 - AES128 16 OFB, create iv of size 16 > + ok 30 - encode > + ok 31 - decode > + ok 32 - data is correct > + ok 33 - AES192 24 ECB, create iv of size 0 > + ok 34 - encode > + ok 35 - decode > + ok 36 - data is correct > + ok 37 - AES192 24 CBC, create iv of size 16 > + ok 38 - encode > + ok 39 - decode > + ok 40 - data is correct > + ok 41 - AES192 24 CFB, create iv of size 16 > + ok 42 - encode > + ok 43 - decode > + ok 44 - data is correct > + ok 45 - AES192 24 OFB, create iv of size 16 > + ok 46 - encode > + ok 47 - decode > + ok 48 - data is correct > + ok 49 - AES256 32 ECB, create iv of size 0 > + ok 50 - encode > + ok 51 - decode > + ok 52 - data is correct > + ok 53 - AES256 32 CBC, create iv of size 16 > + ok 54 - encode > + ok 55 - decode > + ok 56 - data is correct > + ok 57 - AES256 32 CFB, create iv of size 16 > + ok 58 - encode > + ok 59 - decode > + ok 60 - data is correct > + ok 61 - AES256 32 OFB, create iv of size 16 > + ok 62 - encode > + ok 63 - decode > + ok 64 - data is correct > + ok 65 - DES 8 ECB, create iv of size 0 > + ok 66 - encode > + ok 67 - decode > + ok 68 - data is correct > + ok 69 - DES 8 CBC, create iv of size 8 > + ok 70 - encode > + ok 71 - decode > + ok 72 - data is correct > + ok 73 - DES 8 CFB, create iv of size 8 > + ok 74 - encode > + ok 75 - decode > + ok 76 - data is correct > + ok 77 - DES 8 OFB, create iv of size 8 > + ok 78 - encode > + ok 79 - decode > + ok 80 - data is correct > +ok 4 - subtests > + *** test_each: done *** > + *** test_stream *** > + 1..11 > + ok 1 - stream begin checks key size > + ok 2 - stream begin checks iv size > + ok 3 - begin encryption > + ok 4 - append checks size > + ok 5 - append 5 > + ok 6 - append 10 > + ok 7 - last append 30 > + ok 8 - commit checks size > + ok 9 - commit 16 > + ok 10 - decoder returned correct size > + ok 11 - data is decoded correctly > +ok 5 - subtests > + *** test_stream: done *** > + *** main: done *** [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] [PATCH v3 4/4] crypto: use crypto library in crypto.lua 2019-05-07 20:53 [tarantool-patches] [PATCH v3 0/4] crypto lib Vladislav Shpilevoy ` (2 preceding siblings ...) 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 3/4] crypto: implement crypto libary Vladislav Shpilevoy @ 2019-05-07 20:53 ` Vladislav Shpilevoy 2019-05-15 8:01 ` [tarantool-patches] " Георгий Кириченко 2019-05-15 13:42 ` [tarantool-patches] Re: [PATCH v3 0/4] crypto lib Vladislav Shpilevoy 4 siblings, 1 reply; 11+ messages in thread From: Vladislav Shpilevoy @ 2019-05-07 20:53 UTC (permalink / raw) To: tarantool-patches; +Cc: kostja, georgy crypto.lua is a public module using OpenSSL directly. But now lib/crypto encapsulates OpenSSL with additional checks and similar but more conforming API. It allows to replace OpenSSL cipher in crypto.lua with lib/crypto methods. --- extra/exports | 7 +- src/lib/crypto/crypto.c | 12 -- src/lua/crypto.lua | 241 +++++++++++++++++++++------------------ test/app/crypto.result | 114 +++++++++++++----- test/app/crypto.test.lua | 32 +++++- 5 files changed, 249 insertions(+), 157 deletions(-) diff --git a/extra/exports b/extra/exports index 562f8a164..269672fa4 100644 --- a/extra/exports +++ b/extra/exports @@ -77,12 +77,15 @@ space_run_triggers space_bsize box_schema_version -crypto_EVP_CIPHER_key_length -crypto_EVP_CIPHER_iv_length crypto_EVP_MD_CTX_new crypto_EVP_MD_CTX_free crypto_HMAC_CTX_new crypto_HMAC_CTX_free +crypto_stream_new +crypto_stream_begin +crypto_stream_append +crypto_stream_commit +crypto_stream_delete # Module API diff --git a/src/lib/crypto/crypto.c b/src/lib/crypto/crypto.c index c1c8c88ea..9b9d89b0b 100644 --- a/src/lib/crypto/crypto.c +++ b/src/lib/crypto/crypto.c @@ -343,18 +343,6 @@ crypto_free(void) #endif } -int -crypto_EVP_CIPHER_key_length(const EVP_CIPHER *cipher) -{ - return EVP_CIPHER_key_length(cipher); -} - -int -crypto_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher) -{ - return EVP_CIPHER_iv_length(cipher); -} - EVP_MD_CTX * crypto_EVP_MD_CTX_new(void) { diff --git a/src/lua/crypto.lua b/src/lua/crypto.lua index 30504b1de..88f562b45 100644 --- a/src/lua/crypto.lua +++ b/src/lua/crypto.lua @@ -28,23 +28,45 @@ ffi.cdef[[ int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len); int HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len); - typedef struct {} EVP_CIPHER_CTX; - typedef struct {} EVP_CIPHER; - EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(); - void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx); - - int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, - ENGINE *impl, const unsigned char *key, - const unsigned char *iv, int enc); - int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, - const unsigned char *in, int inl); - int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); - - int crypto_EVP_CIPHER_iv_length(const EVP_CIPHER *cipher); - int crypto_EVP_CIPHER_key_length(const EVP_CIPHER *cipher); - - int EVP_CIPHER_block_size(const EVP_CIPHER *cipher); - const EVP_CIPHER *EVP_get_cipherbyname(const char *name); + enum crypto_algo { + CRYPTO_ALGO_NONE, + CRYPTO_ALGO_AES128, + CRYPTO_ALGO_AES192, + CRYPTO_ALGO_AES256, + CRYPTO_ALGO_DES, + }; + + enum crypto_mode { + CRYPTO_MODE_ECB, + CRYPTO_MODE_CBC, + CRYPTO_MODE_CFB, + CRYPTO_MODE_OFB, + }; + + enum crypto_direction { + CRYPTO_DIR_DECRYPT = 0, + CRYPTO_DIR_ENCRYPT = 1, + }; + + struct crypto_stream; + + struct crypto_stream * + crypto_stream_new(enum crypto_algo algo, enum crypto_mode mode, + enum crypto_direction dir); + + int + crypto_stream_begin(struct crypto_stream *s, const char *key, int key_size, + const char *iv, int iv_size); + + int + crypto_stream_append(struct crypto_stream *s, const char *in, int in_size, + char *out, int out_size); + + int + crypto_stream_commit(struct crypto_stream *s, char *out, int out_size); + + void + crypto_stream_delete(struct crypto_stream *s); ]] local function openssl_err_str() @@ -206,110 +228,90 @@ hmac_mt = { } } -local ciphers = {} -for algo, algo_name in pairs({des = 'DES', aes128 = 'AES-128', - aes192 = 'AES-192', aes256 = 'AES-256'}) do - local algo_api = {} - for mode, mode_name in pairs({cfb = 'CFB', ofb = 'OFB', - cbc = 'CBC', ecb = 'ECB'}) do - local cipher = - ffi.C.EVP_get_cipherbyname(algo_name .. '-' .. mode_name) - if cipher ~= nil then - algo_api[mode] = cipher - end - end - if algo_api ~= {} then - ciphers[algo] = algo_api - end -end +local crypto_stream_mt = {} -local cipher_mt = {} - -local function cipher_gc(ctx) - ffi.C.EVP_CIPHER_CTX_free(ctx) +local function crypto_stream_gc(ctx) + ffi.C.crypto_stream_delete(ctx) end -local function cipher_new(cipher, key, iv, direction) - local needed_len = ffi.C.crypto_EVP_CIPHER_key_length(cipher) - if key == nil or key:len() ~= needed_len then - return error('Key length should be equal to cipher key length ('.. - tostring(needed_len)..' bytes)') - end - needed_len = ffi.C.crypto_EVP_CIPHER_iv_length(cipher) - if iv == nil or iv:len() ~= needed_len then - return error('Initial vector length should be equal to cipher iv '.. - 'length ('..tostring(needed_len)..' bytes)') - end - local ctx = ffi.C.EVP_CIPHER_CTX_new() +local function crypto_stream_new(algo, mode, key, iv, direction) + local ctx = ffi.C.crypto_stream_new(algo, mode, direction) if ctx == nil then - return error('Can\'t create cipher ctx: ' .. openssl_err_str()) + box.error() end - ffi.gc(ctx, cipher_gc) + ffi.gc(ctx, crypto_stream_gc) local self = setmetatable({ ctx = ctx, - cipher = cipher, - block_size = ffi.C.EVP_CIPHER_block_size(cipher), - direction = direction, buf = buffer.ibuf(), - initialized = false, - outl = ffi.new('int[1]') - }, cipher_mt) + is_initialized = false, + }, crypto_stream_mt) self:init(key, iv) return self end -local function cipher_init(self, key, iv) - if self.ctx == nil then +local function crypto_stream_begin(self, key, iv) + local ctx = self.ctx + if not ctx then return error('Cipher context isn\'t usable') end - if ffi.C.EVP_CipherInit_ex(self.ctx, self.cipher, nil, - key, iv, self.direction) ~= 1 then - return error('Can\'t init cipher:' .. openssl_err_str()) + self.key = key or self.key + self.iv = iv or self.iv + if self.key and self.iv then + if ffi.C.crypto_stream_begin(ctx, self.key, self.key:len(), + self.iv, self.iv:len()) ~= 0 then + box.error() + end + self.is_initialized = true end - self.initialized = true end -local function cipher_update(self, input) - if not self.initialized then +local function crypto_stream_append(self, input) + if not self.is_initialized then return error('Cipher not initialized') end if type(input) ~= 'string' then error("Usage: cipher:update(string)") end - local wpos = self.buf:reserve(input:len() + self.block_size - 1) - if ffi.C.EVP_CipherUpdate(self.ctx, wpos, self.outl, input, input:len()) ~= 1 then - return error('Can\'t update cipher:' .. openssl_err_str()) + local append = ffi.C.crypto_stream_append + local out_size = append(self.ctx, input, input:len(), nil, 0) + local wpos = self.buf:reserve(out_size) + out_size = append(self.ctx, input, input:len(), wpos, out_size) + if out_size < 0 then + box.error() end - return ffi.string(wpos, self.outl[0]) + return ffi.string(wpos, out_size) end -local function cipher_final(self) - if not self.initialized then +local function crypto_stream_commit(self) + if not self.is_initialized then return error('Cipher not initialized') end - self.initialized = false - local wpos = self.buf:reserve(self.block_size - 1) - if ffi.C.EVP_CipherFinal_ex(self.ctx, wpos, self.outl) ~= 1 then - return error('Can\'t finalize cipher:' .. openssl_err_str()) + local commit = ffi.C.crypto_stream_commit + local out_size = commit(self.ctx, nil, 0) + local wpos = self.buf:reserve(out_size) + out_size = commit(self.ctx, wpos, out_size) + if out_size < 0 then + box.error() end - self.initialized = false - return ffi.string(wpos, self.outl[0]) + self.is_initialized = false + return ffi.string(wpos, out_size) end -local function cipher_free(self) - ffi.C.EVP_CIPHER_CTX_free(self.ctx) +local function crypto_stream_free(self) ffi.gc(self.ctx, nil) + crypto_stream_gc(self.ctx) self.ctx = nil - self.initialized = false - self.buf:reset() + self.key = nil + self.iv = nil + self.is_initialized = false end -cipher_mt = { +crypto_stream_mt = { __index = { - init = cipher_init, - update = cipher_update, - result = cipher_final, - free = cipher_free + init = crypto_stream_begin, + update = crypto_stream_append, + result = crypto_stream_commit, + free = crypto_stream_free } } @@ -365,23 +367,51 @@ hmac_api = setmetatable(hmac_api, return error('HMAC method "' .. digest .. '" is not supported') end }) -local function cipher_mode_error(self, mode) - error('Cipher mode ' .. mode .. ' is not supported') -end +local crypto_algos = { + aes128 = ffi.C.CRYPTO_ALGO_AES128, + aes192 = ffi.C.CRYPTO_ALGO_AES192, + aes256 = ffi.C.CRYPTO_ALGO_AES256, + des = ffi.C.CRYPTO_ALGO_DES +} +local crypto_modes = { + ecb = ffi.C.CRYPTO_MODE_ECB, + cbc = ffi.C.CRYPTO_MODE_CBC, + cfb = ffi.C.CRYPTO_MODE_CFB, + ofb = ffi.C.CRYPTO_MODE_OFB +} +local crypto_dirs = { + encrypt = ffi.C.CRYPTO_DIR_ENCRYPT, + decrypt = ffi.C.CRYPTO_DIR_DECRYPT +} -local cipher_api = {} -for class, subclass in pairs(ciphers) do - local class_api = {} - for subclass, cipher in pairs(subclass) do - class_api[subclass] = {} - for direction, param in pairs({encrypt = 1, decrypt = 0}) do - class_api[subclass][direction] = setmetatable({ - new = function (key, iv) - return cipher_new(cipher, key, iv, param) +local algo_api_mt = { + __index = function(self, mode) + error('Cipher mode ' .. mode .. ' is not supported') + end +} +local crypto_api_mt = { + __index = function(self, cipher) + return error('Cipher method "' .. cipher .. '" is not supported') + end +} + +local crypto_api = setmetatable({}, crypto_api_mt) +for algo_name, algo_value in pairs(crypto_algos) do + local algo_api = setmetatable({}, algo_api_mt) + crypto_api[algo_name] = algo_api + for mode_name, mode_value in pairs(crypto_modes) do + local mode_api = {} + algo_api[mode_name] = mode_api + for dir_name, dir_value in pairs(crypto_dirs) do + mode_api[dir_name] = setmetatable({ + new = function(key, iv) + return crypto_stream_new(algo_value, mode_value, key, iv, + dir_value) end }, { - __call = function (self, str, key, iv) - local ctx = cipher_new(cipher, key, iv, param) + __call = function(self, str, key, iv) + local ctx = crypto_stream_new(algo_value, mode_value, key, + iv, dir_value) local res = ctx:update(str) res = res .. ctx:result() ctx:free() @@ -390,19 +420,10 @@ for class, subclass in pairs(ciphers) do }) end end - class_api = setmetatable(class_api, {__index = cipher_mode_error}) - if class_api ~= {} then - cipher_api[class] = class_api - end end -cipher_api = setmetatable(cipher_api, - {__index = function(self, cipher) - return error('Cipher method "' .. cipher .. '" is not supported') - end }) - return { digest = digest_api, hmac = hmac_api, - cipher = cipher_api, + cipher = crypto_api, } diff --git a/test/app/crypto.result b/test/app/crypto.result index 2d939b6fc..7bfb4d198 100644 --- a/test/app/crypto.result +++ b/test/app/crypto.result @@ -32,62 +32,102 @@ ciph.decrypt(enc, pass, iv) --- - test ... ---Failing scenaries +-- Failing scenarios. crypto.cipher.aes128.cbc.encrypt('a') --- -- error: 'builtin/crypto.lua:<line>"]: Key length should be equal to cipher key length - (16 bytes)' +- error: 'builtin/crypto.lua:<line>"]: Cipher not initialized' ... crypto.cipher.aes128.cbc.encrypt('a', '123456', '435') --- -- error: 'builtin/crypto.lua:<line>"]: Key length should be equal to cipher key length - (16 bytes)' +- error: key size expected 16, got 6 ... crypto.cipher.aes128.cbc.encrypt('a', '1234567887654321') --- -- error: 'builtin/crypto.lua:<line>"]: Initial vector length should be equal to cipher - iv length (16 bytes)' +- error: 'builtin/crypto.lua:<line>"]: Cipher not initialized' ... crypto.cipher.aes128.cbc.encrypt('a', '1234567887654321', '12') --- -- error: 'builtin/crypto.lua:<line>"]: Initial vector length should be equal to cipher - iv length (16 bytes)' +- error: IV size expected 16, got 2 ... crypto.cipher.aes256.cbc.decrypt('a') --- -- error: 'builtin/crypto.lua:<line>"]: Key length should be equal to cipher key length - (32 bytes)' +- error: 'builtin/crypto.lua:<line>"]: Cipher not initialized' ... crypto.cipher.aes256.cbc.decrypt('a', '123456', '435') --- -- error: 'builtin/crypto.lua:<line>"]: Key length should be equal to cipher key length - (32 bytes)' +- error: key size expected 32, got 6 ... crypto.cipher.aes256.cbc.decrypt('a', '12345678876543211234567887654321') --- -- error: 'builtin/crypto.lua:<line>"]: Initial vector length should be equal to cipher - iv length (16 bytes)' +- error: 'builtin/crypto.lua:<line>"]: Cipher not initialized' ... crypto.cipher.aes256.cbc.decrypt('12', '12345678876543211234567887654321', '12') --- -- error: 'builtin/crypto.lua:<line>"]: Initial vector length should be equal to cipher - iv length (16 bytes)' +- error: IV size expected 16, got 2 ... -crypto.cipher.aes192.cbc.encrypt.new() +crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678', '12345') --- -- error: Key length should be equal to cipher key length (24 bytes) +- error: IV size expected 16, got 5 ... -crypto.cipher.aes192.cbc.encrypt.new('123321') +-- Set key after codec creation. +c = crypto.cipher.aes128.cbc.encrypt.new() --- -- error: Key length should be equal to cipher key length (24 bytes) ... -crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678') +key = '1234567812345678' --- -- error: Initial vector length should be equal to cipher iv length (16 bytes) ... -crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678', '12345') +iv = key +--- +... +c:init(key) +--- +... +c:update('plain') +--- +- error: Cipher not initialized +... +c:result() +--- +- error: Cipher not initialized +... +c:init(nil, iv) +--- +... +cipher = c:update('plain ') +--- +... +cipher = cipher..c:update('next plain') +--- +... +cipher = cipher..c:result() +--- +... +crypto.cipher.aes128.cbc.decrypt(cipher, key, iv) +--- +- plain next plain +... +-- Reuse. +key2 = '8765432187654321' --- -- error: Initial vector length should be equal to cipher iv length (16 bytes) +... +iv2 = key2 +--- +... +c:init(key2, iv2) +--- +... +cipher = c:update('new plain ') +--- +... +cipher = cipher..c:update('next new plain') +--- +... +cipher = cipher..c:result() +--- +... +crypto.cipher.aes128.cbc.decrypt(cipher, key2, iv2) +--- +- new plain next new plain ... crypto.cipher.aes100.efb --- @@ -103,6 +143,22 @@ crypto.digest.nodigest - error: '[string "return crypto.digest.nodigest "]:1: Digest method "nodigest" is not supported' ... +-- Check that GC really drops unused codecs and streams, and +-- nothing crashes. +weak = setmetatable({obj = c}, {__mode = 'v'}) +--- +... +c = nil +--- +... +collectgarbage('collect') +--- +- 0 +... +weak.obj +--- +- null +... bad_pass = '8765432112345678' --- ... @@ -111,13 +167,13 @@ bad_iv = '123456abcdefghij' ... ciph.decrypt(enc, bad_pass, iv) --- -- error: 'builtin/crypto.lua:<line>"]: Can''t finalize cipher:error:06065064:digital envelope - routines:EVP_DecryptFinal_ex:bad decrypt' +- error: 'OpenSSL error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad + decrypt' ... ciph.decrypt(enc, pass, bad_iv) --- -- error: 'builtin/crypto.lua:<line>"]: Can''t finalize cipher:error:06065064:digital envelope - routines:EVP_DecryptFinal_ex:bad decrypt' +- error: 'OpenSSL error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad + decrypt' ... test_run:cmd("clear filter") --- diff --git a/test/app/crypto.test.lua b/test/app/crypto.test.lua index 94f594bcc..16d8ce2a7 100644 --- a/test/app/crypto.test.lua +++ b/test/app/crypto.test.lua @@ -12,7 +12,7 @@ enc ciph.decrypt(enc, pass, iv) ---Failing scenaries +-- Failing scenarios. crypto.cipher.aes128.cbc.encrypt('a') crypto.cipher.aes128.cbc.encrypt('a', '123456', '435') crypto.cipher.aes128.cbc.encrypt('a', '1234567887654321') @@ -23,16 +23,40 @@ crypto.cipher.aes256.cbc.decrypt('a', '123456', '435') crypto.cipher.aes256.cbc.decrypt('a', '12345678876543211234567887654321') crypto.cipher.aes256.cbc.decrypt('12', '12345678876543211234567887654321', '12') -crypto.cipher.aes192.cbc.encrypt.new() -crypto.cipher.aes192.cbc.encrypt.new('123321') -crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678') crypto.cipher.aes192.cbc.decrypt.new('123456788765432112345678', '12345') +-- Set key after codec creation. +c = crypto.cipher.aes128.cbc.encrypt.new() +key = '1234567812345678' +iv = key +c:init(key) +c:update('plain') +c:result() +c:init(nil, iv) +cipher = c:update('plain ') +cipher = cipher..c:update('next plain') +cipher = cipher..c:result() +crypto.cipher.aes128.cbc.decrypt(cipher, key, iv) +-- Reuse. +key2 = '8765432187654321' +iv2 = key2 +c:init(key2, iv2) +cipher = c:update('new plain ') +cipher = cipher..c:update('next new plain') +cipher = cipher..c:result() +crypto.cipher.aes128.cbc.decrypt(cipher, key2, iv2) + crypto.cipher.aes100.efb crypto.cipher.aes256.nomode crypto.digest.nodigest +-- Check that GC really drops unused codecs and streams, and +-- nothing crashes. +weak = setmetatable({obj = c}, {__mode = 'v'}) +c = nil +collectgarbage('collect') +weak.obj bad_pass = '8765432112345678' bad_iv = '123456abcdefghij' -- 2.20.1 (Apple Git-117) ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH v3 4/4] crypto: use crypto library in crypto.lua 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 4/4] crypto: use crypto library in crypto.lua Vladislav Shpilevoy @ 2019-05-15 8:01 ` Георгий Кириченко 2019-05-15 13:42 ` Vladislav Shpilevoy 0 siblings, 1 reply; 11+ messages in thread From: Георгий Кириченко @ 2019-05-15 8:01 UTC (permalink / raw) To: tarantool-patches [-- Attachment #1: Type: text/plain, Size: 242 bytes --] Looks good except this: (two points near cipher..c) > +c:init(nil, iv) > +cipher = c:update('plain ') > +cipher = cipher..c:update('next plain') > +cipher = cipher..c:result() > +crypto.cipher.aes128.cbc.decrypt(cipher, key, iv) > +-- Reuse. [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH v3 4/4] crypto: use crypto library in crypto.lua 2019-05-15 8:01 ` [tarantool-patches] " Георгий Кириченко @ 2019-05-15 13:42 ` Vladislav Shpilevoy 0 siblings, 0 replies; 11+ messages in thread From: Vladislav Shpilevoy @ 2019-05-15 13:42 UTC (permalink / raw) To: tarantool-patches, Георгий Кириченко On 15/05/2019 11:01, Георгий Кириченко wrote: > Looks good except this: (two points near cipher..c) It is not a typo - it is a concatenation operator in Lua. >> +c:init(nil, iv) >> +cipher = c:update('plain ') >> +cipher = cipher..c:update('next plain') >> +cipher = cipher..c:result() >> +crypto.cipher.aes128.cbc.decrypt(cipher, key, iv) >> +-- Reuse. ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH v3 0/4] crypto lib 2019-05-07 20:53 [tarantool-patches] [PATCH v3 0/4] crypto lib Vladislav Shpilevoy ` (3 preceding siblings ...) 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 4/4] crypto: use crypto library in crypto.lua Vladislav Shpilevoy @ 2019-05-15 13:42 ` Vladislav Shpilevoy 4 siblings, 0 replies; 11+ messages in thread From: Vladislav Shpilevoy @ 2019-05-15 13:42 UTC (permalink / raw) To: tarantool-patches; +Cc: kostja, georgy Pushed into the master. On 07/05/2019 23:53, Vladislav Shpilevoy wrote: > SWIM needs encryption because it transmits packets affecting cluster state and > topology, probably via public networks between datacenters. Tarantool hasn't had > normal crypto library with useful C API on board until now. OpenSSL was used, > but its API is far from simple, and before this patchset it was used in Lua > only, via FFI. > > The patchset moves existing OpenSSL wrappers into a separate library, extends > it with pretty API, and replaces some parts of crypto.lua module. It is going to > be used by SWIM. > > Branch: http://github.com/tarantool/tarantool/tree/gerold103/crypto-lib > > Changes in V3: > - Added crypto_stream to replace cipher in crypto.lua module; > - Initial vector is passed explicitly to each encrypt(), and is not stored in > codec object; > - All the same standards are supported as in crypto.lua were. > > V2: https://www.freelists.org/post/tarantool-patches/PATCH-v2-03-swim-encryption-preparation > > Vladislav Shpilevoy (4): > crypto: move crypto business into a separate library > crypto: make exported methods conform code style > crypto: implement crypto libary > crypto: use crypto library in crypto.lua > > extra/exports | 16 +- > src/CMakeLists.txt | 3 +- > src/lib/CMakeLists.txt | 1 + > src/lib/core/diag.h | 2 + > src/lib/core/exception.cc | 25 +++ > src/lib/core/exception.h | 7 + > src/lib/crypto/CMakeLists.txt | 5 + > src/lib/crypto/crypto.c | 391 ++++++++++++++++++++++++++++++++++ > src/lib/crypto/crypto.h | 283 ++++++++++++++++++++++++ > src/lua/crypto.c | 73 ------- > src/lua/crypto.h | 54 ----- > src/lua/crypto.lua | 263 ++++++++++++----------- > src/main.cc | 3 + > test/app/crypto.result | 114 +++++++--- > test/app/crypto.test.lua | 32 ++- > test/unit/CMakeLists.txt | 3 + > test/unit/crypto.c | 302 ++++++++++++++++++++++++++ > test/unit/crypto.result | 132 ++++++++++++ > 18 files changed, 1418 insertions(+), 291 deletions(-) > create mode 100644 src/lib/crypto/CMakeLists.txt > create mode 100644 src/lib/crypto/crypto.c > create mode 100644 src/lib/crypto/crypto.h > delete mode 100644 src/lua/crypto.c > delete mode 100644 src/lua/crypto.h > create mode 100644 test/unit/crypto.c > create mode 100644 test/unit/crypto.result > > -- > 2.20.1 (Apple Git-117) > > ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2019-05-15 13:42 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2019-05-07 20:53 [tarantool-patches] [PATCH v3 0/4] crypto lib Vladislav Shpilevoy 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 1/4] crypto: move crypto business into a separate library Vladislav Shpilevoy 2019-05-15 7:58 ` [tarantool-patches] " Георгий Кириченко 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 2/4] crypto: make exported methods conform code style Vladislav Shpilevoy 2019-05-15 7:58 ` [tarantool-patches] " Георгий Кириченко 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 3/4] crypto: implement crypto libary Vladislav Shpilevoy 2019-05-15 7:58 ` [tarantool-patches] " Георгий Кириченко 2019-05-07 20:53 ` [tarantool-patches] [PATCH v3 4/4] crypto: use crypto library in crypto.lua Vladislav Shpilevoy 2019-05-15 8:01 ` [tarantool-patches] " Георгий Кириченко 2019-05-15 13:42 ` Vladislav Shpilevoy 2019-05-15 13:42 ` [tarantool-patches] Re: [PATCH v3 0/4] crypto lib Vladislav Shpilevoy
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox