[tarantool-patches] [PATCH v2 3/3] crypto: implement crypto codec API and AES 128 encryption

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Mon Apr 29 14:07:39 MSK 2019


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 API provides struct crypto_codec which encapsulates all the
encryption logic and exposes quite simple API like this:

    crypto_codec_encode/decode(in, in_size, out, out_size)

A caller can create a needed codec via crypto_codec_new, which
now supports only AES 128 algorithm. The reason behind such a
choice is simple - it is needed in SWIM now.

The API is flexible in terms of extending and adding new
algorithms in future.

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       | 144 +++++++++++++++++++++++++
 src/lib/crypto/crypto.h       |  94 +++++++++++++++++
 test/unit/CMakeLists.txt      |   3 +
 test/unit/crypto.c            | 191 ++++++++++++++++++++++++++++++++++
 test/unit/crypto.result       |  40 +++++++
 9 files changed, 507 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..a96963d6f 100644
--- a/src/lib/crypto/crypto.c
+++ b/src/lib/crypto/crypto.c
@@ -29,12 +29,156 @@
  * 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>
 
+/** Set a diag error with the latest OpenSSL error message. */
+static inline void
+diag_set_OpenSSL(void)
+{
+	diag_set(CryptoError, "OpenSSL error: %s",
+		 ERR_error_string(ERR_get_error(), NULL));
+}
+
+/**
+ * OpenSSL codec. It contains only key, initial vector, and a
+ * constant cipher type, because OpenSSL does not allow
+ * long-living EVP_CIPHER_CTX contexts.
+ */
+struct crypto_codec {
+	/**
+	 * Secret key, usually is unchanged among multiple data
+	 * packets. Only AES 128 is supported now, so the buffer
+	 * is of 16 byte size.
+	 */
+	unsigned char key[CRYPTO_AES128_KEY_SIZE];
+	/**
+	 * Initial vector, and in fact a public key. It should be
+	 * regenerated randomly for each data packet.
+	 */
+	unsigned char iv[CRYPTO_AES_IV_SIZE];
+	/** Size of block by which the algorithm operates. */
+	uint8_t block_size;
+	/** Cipher type. Depends on algorithm and key size. */
+	const EVP_CIPHER *type;
+};
+
+struct crypto_codec *
+crypto_codec_new(enum crypto_algo algo, const char *key)
+{
+	struct crypto_codec *c = (struct crypto_codec *) malloc(sizeof(*c));
+	if (c == NULL) {
+		diag_set(OutOfMemory, sizeof(*c), "malloc", "c");
+		return NULL;
+	}
+	switch (algo) {
+	case CRYPTO_AES128:
+		memcpy(c->key, key, sizeof(c->key));
+		memset(c->iv, 0, sizeof(c->iv));
+		c->type = EVP_aes_128_cbc();
+		break;
+	case CRYPTO_NONE:
+		c->type = EVP_enc_null();
+		break;
+	default:
+		free(c);
+		diag_set(CryptoError, "only 'none' and AES 128 are supported");
+		return NULL;
+	}
+	c->block_size = EVP_CIPHER_block_size(c->type);
+	return c;
+}
+
+const char *
+crypto_codec_gen_iv(struct crypto_codec *c)
+{
+	random_bytes((char *) c->iv, sizeof(c->iv));
+	return (char *) c->iv;
+}
+
+int
+crypto_codec_encrypt(struct crypto_codec *c, const char *in, int in_size,
+		     char *out, int out_size)
+{
+	const unsigned char *uin = (const unsigned char *) in;
+	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 len, result, need = in_size + c->block_size;
+	if (need > out_size)
+		return need;
+
+	EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+	if (ctx == NULL)
+		goto error;
+	if (EVP_EncryptInit_ex(ctx, c->type, NULL, c->key, c->iv) != 1)
+		goto error;
+	if (EVP_EncryptUpdate(ctx, uout, &len, uin, in_size) != 1)
+		goto error;
+	result = len;
+	assert(result <= need);
+	if (EVP_EncryptFinal_ex(ctx, uout + result, &len) != 1)
+		goto error;
+	result += len;
+	assert(result <= need);
+	EVP_CIPHER_CTX_free(ctx);
+	return result;
+
+error:
+	diag_set_OpenSSL();
+	if (ctx != NULL)
+		EVP_CIPHER_CTX_free(ctx);
+	return -1;
+}
+
+int
+crypto_codec_decrypt(struct crypto_codec *c, const char *iv,
+		     const char *in, int in_size, char *out, int out_size)
+{
+	if (in_size > out_size)
+		return in_size;
+	const unsigned char *uin = (const unsigned char *) in;
+	const unsigned char *uiv = (const unsigned char *) iv;
+	unsigned char *uout = (unsigned char *) out;
+	int len, result;
+
+	EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+	if (ctx == NULL)
+		goto error;
+	if (EVP_DecryptInit_ex(ctx, c->type, NULL, c->key, uiv) != 1)
+		goto error;
+	if (EVP_DecryptUpdate(ctx, uout, &len, uin, in_size) != 1)
+		goto error;
+	result = len;
+	if (EVP_DecryptFinal_ex(ctx, uout + result, &len) != 1)
+		goto error;
+	EVP_CIPHER_CTX_free(ctx);
+	return result + len;
+
+error:
+	diag_set_OpenSSL();
+	if (ctx != NULL)
+		EVP_CIPHER_CTX_free(ctx);
+	return -1;
+}
+
+void
+crypto_codec_delete(struct crypto_codec *c)
+{
+	free(c);
+}
+
 void
 crypto_init(void)
 {
diff --git a/src/lib/crypto/crypto.h b/src/lib/crypto/crypto.h
index bc807e1a2..c679c39f7 100644
--- a/src/lib/crypto/crypto.h
+++ b/src/lib/crypto/crypto.h
@@ -35,6 +35,100 @@
 extern "C" {
 #endif
 
+enum crypto_algo {
+	/** None to disable encryption. */
+	CRYPTO_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. The key is a secret information which needs to
+	 * be shared among communicating nodes. Key size can be
+	 * 128, 192 and 256 bits. Initial vector is an additional
+	 * entropy factor to prevent 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.
+	 */
+	CRYPTO_AES128,
+};
+
+/**
+ * Values obtained from EVP_CIPHER_*() API, but the constants are
+ * needed in some places at compilation time. For example, for
+ * statically sized buffers.
+ */
+enum {
+	/**
+	 * Block size of AES operation. Encrypted data size is
+	 * always divisible by this value.
+	 */
+	CRYPTO_AES_BLOCK_SIZE = 16,
+	/**
+	 * Size of AES initial vector. It does not depend on key
+	 * size.
+	 */
+	CRYPTO_AES_IV_SIZE = 16,
+	/**
+	 * Obviously, 128 bits = 16 bytes always. It is just
+	 * syntax sugar so as not to hardcode 16 everywhere.
+	 */
+	CRYPTO_AES128_KEY_SIZE = 16,
+};
+
+struct crypto_codec;
+
+/**
+ * Create a new codec working with a specified @a algo algorithm
+ * and a secret @a key. Size of @a key depends on the chosen
+ * algorithm.
+ */
+struct crypto_codec *
+crypto_codec_new(enum crypto_algo algo, const char *key);
+
+/** Generate a new initial vector and return it. */
+const char *
+crypto_codec_gen_iv(struct crypto_codec *c);
+
+/**
+ * Encrypt plain data by codec @a c.
+ * @param c Codec.
+ * @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 *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 codec. */
+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 bb88b9c9b..368ceb649 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -213,6 +213,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..092650543
--- /dev/null
+++ b/test/unit/crypto.c
@@ -0,0 +1,191 @@
+/*
+ * 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(19);
+
+	char key[CRYPTO_AES128_KEY_SIZE];
+	random_bytes(key, sizeof(key));
+	struct crypto_codec *c = crypto_codec_new(CRYPTO_AES128, key);
+
+	int rc = crypto_codec_encrypt(c, NULL, 10, NULL, 0);
+	is(rc, 26, "encrypt returns needed number of bytes");
+	rc = crypto_codec_encrypt(c, NULL, 10, NULL, 15);
+	is(rc, 26, "encrypt does not write anything when too small "\
+	   "buffer");
+	rc = crypto_codec_encrypt(c, NULL, 0, NULL, 0);
+	is(rc, 16, "encrypt does not allow 0 sized buffer");
+	rc = crypto_codec_encrypt(c, 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);
+	const char *iv = crypto_codec_gen_iv(c);
+
+	rc = crypto_codec_encrypt(c, plain, plain_size, buffer1, buffer_size);
+	is(rc, 16, "encrypt works when buffer is big enough");
+	rc = crypto_codec_encrypt(c, 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, 16, "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");
+
+	const char *iv2 = crypto_codec_gen_iv(c);
+	rc = crypto_codec_encrypt(c, 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_AES128, key);
+	rc = crypto_codec_encrypt(c, 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];
+	random_bytes(key, sizeof(key));
+	struct crypto_codec *c = crypto_codec_new(CRYPTO_AES128, key);
+
+	char plain[515], cipher[1024], result[1024];
+	int rc, size = 10;
+	for (int size = 10; size < (int) sizeof(plain); size += 10) {
+		random_bytes(plain, size);
+		const char *iv = crypto_codec_gen_iv(c);
+		rc = crypto_codec_encrypt(c, 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_none_codec(void)
+{
+	header();
+	plan(4);
+
+	struct crypto_codec *c = crypto_codec_new(CRYPTO_NONE, NULL);
+	const char *plain = "plain text";
+	int plain_size = strlen(plain) + 1;
+	char buffer1[128], buffer2[128];
+	const char *iv = crypto_codec_gen_iv(c);
+	int rc = crypto_codec_encrypt(c, plain, plain_size,
+				      buffer1, sizeof(buffer1));
+	is(rc, plain_size, "'none' codec does not require more space "\
+	   "than the data");
+	is(memcmp(buffer1, plain, plain_size), 0, "data is just copied");
+	rc = crypto_codec_decrypt(c, iv, buffer1, rc, buffer2, sizeof(buffer2));
+	is(rc, plain_size, "'decrypt' does the same");
+	is(memcmp(buffer2, plain, plain_size), 0, "data is copied back");
+
+	crypto_codec_delete(c);
+
+	check_plan();
+	footer();
+}
+
+int
+main(void)
+{
+	header();
+	plan(4);
+	random_init();
+	crypto_init();
+	memory_init();
+	fiber_init(fiber_c_invoke);
+
+	struct crypto_codec *c = crypto_codec_new(-1, "1234");
+	is(c, NULL, "crypto checks that algo argument is correct");
+	crypto_codec_delete(c);
+
+	test_aes128_codec();
+	test_aes128_stress();
+	test_none_codec();
+
+	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..de55fd2e4
--- /dev/null
+++ b/test/unit/crypto.result
@@ -0,0 +1,40 @@
+	*** main ***
+1..4
+ok 1 - crypto checks that algo argument is correct
+	*** test_aes128_codec ***
+    1..19
+    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 - encrypt works when buffer is big enough
+    ok 6 - encrypt returns the same on second call
+    ok 7 - encrypted data is the same
+    ok 8 - and it is not just copied from the plain text
+    ok 9 - decrypt also checks length and returns needed number of bytes
+    ok 10 - decrypt returns correct number of bytes
+    ok 11 - and correctly decrypts data
+    ok 12 - decrypt can fail with wrong IV
+    ok 13 - diag error is set
+    ok 14 - encrypt with different IV and the same number of written bytes returned
+    ok 15 - the encrypted data looks different
+    ok 16 - decrypt works with correct but another IV
+    ok 17 - data is the same
+    ok 18 - encrypt with one codec, but decrypt with another codec and the same key
+    ok 19 - 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_none_codec ***
+    1..4
+    ok 1 - 'none' codec does not require more space than the data
+    ok 2 - data is just copied
+    ok 3 - 'decrypt' does the same
+    ok 4 - data is copied back
+ok 4 - subtests
+	*** test_none_codec: done ***
+	*** main: done ***
-- 
2.20.1 (Apple Git-117)





More information about the Tarantool-patches mailing list