mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
5e5c3c7cb6
* remove duplicate test file * move all uuidv7 tests into plugin/type_uuid/mysql-test/type_uuid/ * remove mysys/ changes * auto my_random_bytes() fallback - removes duplicate code from uuid, and fixes all other users of my_random_bytes() that don't check the return value (because, perhaps, they don't need crypto-strong random bytes) * End of 11.6 -> 11.7 in tests * clarify the warning text * UUID_VERSION_MASK()/UUID_VARIANT_MASK() must not depend on the version * allow 4x more monotonic uuidv7 per millisecond - instead of stretching 1000 microseconds over 12 bits, let's use extra 2 bits as a counter * rename for compatibility with Percona Server (uuid_v4, uuid_v7)
385 lines
10 KiB
C++
385 lines
10 KiB
C++
/*
|
|
Copyright (c) 2014 Google Inc.
|
|
Copyright (c) 2014, 2019, MariaDB Corporation.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
|
|
|
|
#include <my_global.h>
|
|
#include <string.h>
|
|
|
|
#define template _template /* bug in WolfSSL 4.4.0, see also violite.h */
|
|
#include <openssl/evp.h>
|
|
#undef template
|
|
#include <openssl/aes.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/rand.h>
|
|
|
|
#include <my_crypt.h>
|
|
#include <ssl_compat.h>
|
|
|
|
#include <random>
|
|
|
|
#define CTX_ALIGN 16
|
|
|
|
class MyCTX
|
|
{
|
|
public:
|
|
char ctx_buf[EVP_CIPHER_CTX_SIZE + CTX_ALIGN];
|
|
EVP_CIPHER_CTX* ctx;
|
|
MyCTX()
|
|
{
|
|
#if CTX_ALIGN > 0
|
|
uintptr_t p= ((uintptr_t)ctx_buf + (CTX_ALIGN - 1)) & ~(CTX_ALIGN - 1);
|
|
ctx = reinterpret_cast<EVP_CIPHER_CTX*>(p);
|
|
#else
|
|
ctx = (EVP_CIPHER_CTX*)ctx_buf;
|
|
#endif
|
|
|
|
EVP_CIPHER_CTX_init(ctx);
|
|
}
|
|
virtual ~MyCTX()
|
|
{
|
|
EVP_CIPHER_CTX_reset(ctx);
|
|
ERR_remove_state(0);
|
|
}
|
|
|
|
virtual int init(const EVP_CIPHER *cipher, int encrypt, const uchar *key,
|
|
uint klen, const uchar *iv, uint ivlen)
|
|
{
|
|
compile_time_assert(MY_AES_CTX_SIZE >= sizeof(MyCTX));
|
|
if (unlikely(!cipher))
|
|
return MY_AES_BAD_KEYSIZE;
|
|
|
|
if (EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, encrypt) != 1)
|
|
return MY_AES_OPENSSL_ERROR;
|
|
|
|
DBUG_ASSERT(EVP_CIPHER_CTX_key_length(ctx) == (int)klen);
|
|
DBUG_ASSERT(EVP_CIPHER_CTX_iv_length(ctx) <= (int)ivlen);
|
|
|
|
return MY_AES_OK;
|
|
}
|
|
virtual int update(const uchar *src, uint slen, uchar *dst, uint *dlen)
|
|
{
|
|
#ifdef HAVE_WOLFSSL
|
|
// WolfSSL checks parameters and does not like NULL pointers to be passed to function below.
|
|
if (!src)
|
|
{
|
|
static uchar dummy[MY_AES_BLOCK_SIZE];
|
|
DBUG_ASSERT(!slen);
|
|
src=dummy;
|
|
}
|
|
#endif
|
|
|
|
if (EVP_CipherUpdate(ctx, dst, (int*)dlen, src, slen) != 1)
|
|
return MY_AES_OPENSSL_ERROR;
|
|
return MY_AES_OK;
|
|
}
|
|
virtual int finish(uchar *dst, uint *dlen)
|
|
{
|
|
if (EVP_CipherFinal_ex(ctx, dst, (int*)dlen) != 1)
|
|
return MY_AES_BAD_DATA;
|
|
return MY_AES_OK;
|
|
}
|
|
};
|
|
|
|
class MyCTX_nopad : public MyCTX
|
|
{
|
|
public:
|
|
const uchar *key;
|
|
uint klen, source_tail_len;
|
|
uchar oiv[MY_AES_BLOCK_SIZE];
|
|
uchar source_tail[MY_AES_BLOCK_SIZE];
|
|
|
|
MyCTX_nopad() : MyCTX() { }
|
|
~MyCTX_nopad() override = default;
|
|
|
|
int init(const EVP_CIPHER *cipher, int encrypt, const uchar *key, uint klen,
|
|
const uchar *iv, uint ivlen) override
|
|
{
|
|
compile_time_assert(MY_AES_CTX_SIZE >= sizeof(MyCTX_nopad));
|
|
this->key= key;
|
|
this->klen= klen;
|
|
this->source_tail_len= 0;
|
|
if (ivlen)
|
|
memcpy(oiv, iv, ivlen);
|
|
DBUG_ASSERT(ivlen == 0 || ivlen == sizeof(oiv));
|
|
|
|
int res= MyCTX::init(cipher, encrypt, key, klen, iv, ivlen);
|
|
|
|
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
|
return res;
|
|
}
|
|
|
|
/** Update last partial source block, stored in source_tail array. */
|
|
void update_source_tail(const uchar* src, uint slen)
|
|
{
|
|
if (!slen)
|
|
return;
|
|
uint new_tail_len= (source_tail_len + slen) % MY_AES_BLOCK_SIZE;
|
|
if (new_tail_len)
|
|
{
|
|
if (slen + source_tail_len < MY_AES_BLOCK_SIZE)
|
|
{
|
|
memcpy(source_tail + source_tail_len, src, slen);
|
|
}
|
|
else
|
|
{
|
|
DBUG_ASSERT(slen > new_tail_len);
|
|
memcpy(source_tail, src + slen - new_tail_len, new_tail_len);
|
|
}
|
|
}
|
|
source_tail_len= new_tail_len;
|
|
}
|
|
|
|
int update(const uchar *src, uint slen, uchar *dst, uint *dlen) override
|
|
{
|
|
update_source_tail(src, slen);
|
|
return MyCTX::update(src, slen, dst, dlen);
|
|
}
|
|
|
|
int finish(uchar *dst, uint *dlen) override
|
|
{
|
|
if (source_tail_len)
|
|
{
|
|
/*
|
|
Not much we can do, block ciphers cannot encrypt data that aren't
|
|
a multiple of the block length. At least not without padding.
|
|
Let's do something CTR-like for the last partial block.
|
|
*/
|
|
uchar mask[MY_AES_BLOCK_SIZE];
|
|
uint mlen;
|
|
|
|
int rc= my_aes_crypt(MY_AES_ECB, ENCRYPTION_FLAG_ENCRYPT | ENCRYPTION_FLAG_NOPAD,
|
|
oiv, sizeof(mask), mask, &mlen, key, klen, 0, 0);
|
|
DBUG_ASSERT(rc == MY_AES_OK);
|
|
if (rc)
|
|
return rc;
|
|
DBUG_ASSERT(mlen == sizeof(mask));
|
|
|
|
for (uint i=0; i < source_tail_len; i++)
|
|
dst[i]= source_tail[i] ^ mask[i];
|
|
}
|
|
*dlen= source_tail_len;
|
|
return MY_AES_OK;
|
|
}
|
|
};
|
|
|
|
#define make_aes_dispatcher(mode) \
|
|
static inline const EVP_CIPHER *aes_ ## mode(uint klen) \
|
|
{ \
|
|
switch (klen) { \
|
|
case 16: return EVP_aes_128_ ## mode(); \
|
|
case 24: return EVP_aes_192_ ## mode(); \
|
|
case 32: return EVP_aes_256_ ## mode(); \
|
|
default: return 0; \
|
|
} \
|
|
}
|
|
|
|
make_aes_dispatcher(ecb)
|
|
make_aes_dispatcher(cbc)
|
|
#ifdef HAVE_EncryptAes128Ctr
|
|
make_aes_dispatcher(ctr)
|
|
#endif /* HAVE_EncryptAes128Ctr */
|
|
#ifdef HAVE_EncryptAes128Gcm
|
|
make_aes_dispatcher(gcm)
|
|
|
|
/*
|
|
special implementation for GCM; to fit OpenSSL AES-GCM into the
|
|
existing my_aes_* API it does the following:
|
|
- IV tail (over 12 bytes) goes to AAD
|
|
- the tag is appended to the ciphertext
|
|
*/
|
|
|
|
class MyCTX_gcm : public MyCTX
|
|
{
|
|
public:
|
|
const uchar *aad;
|
|
int aadlen;
|
|
MyCTX_gcm() : MyCTX() { }
|
|
~MyCTX_gcm() override { }
|
|
|
|
int init(const EVP_CIPHER *cipher, int encrypt, const uchar *key, uint klen,
|
|
const uchar *iv, uint ivlen) override
|
|
{
|
|
compile_time_assert(MY_AES_CTX_SIZE >= sizeof(MyCTX_gcm));
|
|
int res= MyCTX::init(cipher, encrypt, key, klen, iv, ivlen);
|
|
int real_ivlen= EVP_CIPHER_CTX_iv_length(ctx);
|
|
aad= iv + real_ivlen;
|
|
aadlen= ivlen - real_ivlen;
|
|
return res;
|
|
}
|
|
|
|
int update(const uchar *src, uint slen, uchar *dst, uint *dlen) override
|
|
{
|
|
/*
|
|
note that this GCM class cannot do streaming decryption, because
|
|
it needs the tag (which is located at the end of encrypted data)
|
|
before decrypting the data. it can encrypt data piecewise, like, first
|
|
half, then the second half, but it must decrypt all at once
|
|
*/
|
|
if (!EVP_CIPHER_CTX_encrypting(ctx))
|
|
{
|
|
/* encrypted string must contain authenticaton tag (see MDEV-11174) */
|
|
if (slen < MY_AES_BLOCK_SIZE)
|
|
return MY_AES_BAD_DATA;
|
|
slen-= MY_AES_BLOCK_SIZE;
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, MY_AES_BLOCK_SIZE,
|
|
(void*)(src + slen)))
|
|
return MY_AES_OPENSSL_ERROR;
|
|
}
|
|
int unused;
|
|
if (aadlen && !EVP_CipherUpdate(ctx, NULL, &unused, aad, aadlen))
|
|
return MY_AES_OPENSSL_ERROR;
|
|
aadlen= 0;
|
|
return MyCTX::update(src, slen, dst, dlen);
|
|
}
|
|
|
|
int finish(uchar *dst, uint *dlen) override
|
|
{
|
|
int fin;
|
|
if (!EVP_CipherFinal_ex(ctx, dst, &fin))
|
|
return MY_AES_BAD_DATA;
|
|
DBUG_ASSERT(fin == 0);
|
|
|
|
if (EVP_CIPHER_CTX_encrypting(ctx))
|
|
{
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, MY_AES_BLOCK_SIZE, dst))
|
|
return MY_AES_OPENSSL_ERROR;
|
|
*dlen= MY_AES_BLOCK_SIZE;
|
|
}
|
|
else
|
|
*dlen= 0;
|
|
return MY_AES_OK;
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
const EVP_CIPHER *(*ciphers[])(uint)= {
|
|
aes_ecb, aes_cbc
|
|
#ifdef HAVE_EncryptAes128Ctr
|
|
, aes_ctr
|
|
#ifdef HAVE_EncryptAes128Gcm
|
|
, aes_gcm
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
extern "C" {
|
|
|
|
int my_aes_crypt_init(void *ctx, enum my_aes_mode mode, int flags,
|
|
const unsigned char* key, unsigned int klen,
|
|
const unsigned char* iv, unsigned int ivlen)
|
|
{
|
|
#ifdef HAVE_EncryptAes128Ctr
|
|
#ifdef HAVE_EncryptAes128Gcm
|
|
if (mode == MY_AES_GCM)
|
|
if (flags & ENCRYPTION_FLAG_NOPAD)
|
|
return MY_AES_OPENSSL_ERROR;
|
|
else
|
|
new (ctx) MyCTX_gcm();
|
|
else
|
|
#endif
|
|
if (mode == MY_AES_CTR)
|
|
new (ctx) MyCTX();
|
|
else
|
|
#endif
|
|
if (flags & ENCRYPTION_FLAG_NOPAD)
|
|
new (ctx) MyCTX_nopad();
|
|
else
|
|
new (ctx) MyCTX();
|
|
return ((MyCTX*)ctx)->init(ciphers[mode](klen), flags & 1,
|
|
key, klen, iv, ivlen);
|
|
}
|
|
|
|
int my_aes_crypt_update(void *ctx, const uchar *src, uint slen,
|
|
uchar *dst, uint *dlen)
|
|
{
|
|
return ((MyCTX*)ctx)->update(src, slen, dst, dlen);
|
|
}
|
|
|
|
int my_aes_crypt_finish(void *ctx, uchar *dst, uint *dlen)
|
|
{
|
|
int res= ((MyCTX*)ctx)->finish(dst, dlen);
|
|
((MyCTX*)ctx)->~MyCTX();
|
|
return res;
|
|
}
|
|
|
|
int my_aes_crypt(enum my_aes_mode mode, int flags,
|
|
const uchar *src, uint slen, uchar *dst, uint *dlen,
|
|
const uchar *key, uint klen, const uchar *iv, uint ivlen)
|
|
{
|
|
void *ctx= alloca(MY_AES_CTX_SIZE);
|
|
int res1, res2;
|
|
uint d1= 0, d2;
|
|
if ((res1= my_aes_crypt_init(ctx, mode, flags, key, klen, iv, ivlen)))
|
|
return res1;
|
|
res1= my_aes_crypt_update(ctx, src, slen, dst, &d1);
|
|
res2= my_aes_crypt_finish(ctx, dst + d1, &d2);
|
|
if (res1 || res2)
|
|
ERR_remove_state(0); /* in case of failure clear error queue */
|
|
else
|
|
*dlen= d1 + d2;
|
|
return res1 ? res1 : res2;
|
|
}
|
|
|
|
|
|
/*
|
|
calculate the length of the cyphertext from the length of the plaintext
|
|
for different AES encryption modes with padding enabled.
|
|
Without padding (ENCRYPTION_FLAG_NOPAD) cyphertext has the same length
|
|
as the plaintext
|
|
*/
|
|
unsigned int my_aes_get_size(enum my_aes_mode mode __attribute__((unused)), unsigned int source_length)
|
|
{
|
|
#ifdef HAVE_EncryptAes128Ctr
|
|
if (mode == MY_AES_CTR)
|
|
return source_length;
|
|
#ifdef HAVE_EncryptAes128Gcm
|
|
if (mode == MY_AES_GCM)
|
|
return source_length + MY_AES_BLOCK_SIZE;
|
|
#endif
|
|
#endif
|
|
return (source_length / MY_AES_BLOCK_SIZE + 1) * MY_AES_BLOCK_SIZE;
|
|
}
|
|
|
|
|
|
unsigned int my_aes_ctx_size(enum my_aes_mode)
|
|
{
|
|
return MY_AES_CTX_SIZE;
|
|
}
|
|
|
|
static std::mt19937 rnd;
|
|
|
|
int my_random_bytes(uchar *buf, int num)
|
|
{
|
|
if (RAND_bytes(buf, num) != 1)
|
|
{ /* shouldn't happen */
|
|
uchar *end= buf + num - 3;
|
|
uint r= rnd();
|
|
for (; buf < end; buf+= 4, r= rnd())
|
|
int4store(buf, r);
|
|
switch (num % 4)
|
|
{
|
|
case 0: break;
|
|
case 1: *buf= rnd(); break;
|
|
case 2: r=rnd(); int2store(buf, r); break;
|
|
case 3: r=rnd(); int3store(buf, r); break;
|
|
}
|
|
return MY_AES_OPENSSL_ERROR;
|
|
}
|
|
return MY_AES_OK;
|
|
}
|
|
|
|
}
|