diff options
author | Monty <monty@mariadb.org> | 2014-12-22 16:53:17 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2015-02-10 10:21:17 +0100 |
commit | d7d589dc01f6d70d1518b74d46fd3b75e76267f5 (patch) | |
tree | f9df59951453660f0038ed78cf5ceadc852bf74c /mysys_ssl | |
parent | 3a3ec744b5a31318a00821dc0ed6da8af49fda25 (diff) | |
download | mariadb-git-d7d589dc01f6d70d1518b74d46fd3b75e76267f5.tar.gz |
Push for testing of encryption
Diffstat (limited to 'mysys_ssl')
-rw-r--r-- | mysys_ssl/CMakeLists.txt | 3 | ||||
-rw-r--r-- | mysys_ssl/my_aes.cc | 792 | ||||
-rw-r--r-- | mysys_ssl/my_crypt.cc | 370 | ||||
-rw-r--r-- | mysys_ssl/my_crypt_key_management.cc | 110 | ||||
-rw-r--r-- | mysys_ssl/my_crypt_key_management_impl.cc | 34 |
5 files changed, 1296 insertions, 13 deletions
diff --git a/mysys_ssl/CMakeLists.txt b/mysys_ssl/CMakeLists.txt index 45867095e4a..05b04b4d8cb 100644 --- a/mysys_ssl/CMakeLists.txt +++ b/mysys_ssl/CMakeLists.txt @@ -39,6 +39,9 @@ SET(MYSYS_SSL_SOURCES my_sha2.cc my_md5.cc my_rnd.cc + my_crypt.cc + my_crypt_key_management.cc + my_crypt_key_management_impl.cc ) ADD_CONVENIENCE_LIBRARY(mysys_ssl ${MYSYS_SSL_SOURCES}) diff --git a/mysys_ssl/my_aes.cc b/mysys_ssl/my_aes.cc index 9327bc32a3b..20bd03551c2 100644 --- a/mysys_ssl/my_aes.cc +++ b/mysys_ssl/my_aes.cc @@ -17,13 +17,17 @@ #include <my_global.h> #include <m_string.h> #include <my_aes.h> +#include <my_crypt.h> #if defined(HAVE_YASSL) #include "aes.hpp" #include "openssl/ssl.h" +#include "crypto_wrapper.hpp" #elif defined(HAVE_OPENSSL) #include <openssl/aes.h> #include <openssl/evp.h> +#include <openssl/buffer.h> +#include <openssl/conf.h> // Wrap C struct, to ensure resources are released. struct MyCipherCtx @@ -37,11 +41,6 @@ struct MyCipherCtx enum encrypt_dir { MY_AES_ENCRYPT, MY_AES_DECRYPT }; -#define MY_AES_BLOCK_SIZE 16 /* Block size in bytes */ - -/* If bad data discovered during decoding */ -#define AES_BAD_DATA -1 - /** This is internal function just keeps joint code of Key generation @@ -101,28 +100,796 @@ static int my_aes_create_key(const char *key, int key_length, uint8 *rkey) return 0; } +/** + Decode Hexencoded String to uint8[]. + + SYNOPSIS + my_aes_hex2uint() + @param iv [in] Pointer to hexadecimal encoded IV String + @param dest [out] Pointer to output uint8 array. Memory allocated by caller + @param iv_length [in] Size of destination array. + */ + +void my_aes_hex2uint(const char* in, unsigned char *out, int dest_length) +{ + const char *pos= in; + int count; + for (count = 0; count < dest_length; count++) + { + uchar res; + sscanf(pos, "%2hhx", &res); + out[count] = res; + pos += 2 * sizeof(char); + } +} + + +/** + Calculate key and iv from a given salt and secret as it is handled + in openssl encrypted files via console + + SYNOPSIS + my_bytes_to_key() + @param salt [in] the given salt as extracted from the encrypted file + @param secret [in] the given secret as String, provided by the user + @param key [out] 32 Bytes of key are written to this pointer + @param iv [out] 16 Bytes of iv are written to this pointer +*/ + +void my_bytes_to_key(const unsigned char *salt, const char *secret, unsigned char *key, + unsigned char *iv) +{ +#ifdef HAVE_YASSL + /* the yassl function has no support for SHA1. Reason unknown. */ + int keyLen = 32; + int ivLen = 16; + int EVP_SALT_SZ = 8; + const int SHA_LEN = 20; + yaSSL::SHA myMD; + uint digestSz = myMD.get_digestSize(); + unsigned char digest[SHA_LEN]; // max size + int sz = strlen(secret); + int count = 1; + int keyLeft = keyLen; + int ivLeft = ivLen; + int keyOutput = 0; + + while (keyOutput < (keyLen + ivLen)) + { + int digestLeft = digestSz; + if (keyOutput) // first time D_0 is empty + myMD.update(digest, digestSz); + myMD.update((yaSSL::byte* )secret, sz); + if (salt) + myMD.update(salt, EVP_SALT_SZ); + myMD.get_digest(digest); + for (int j = 1; j < count; j++) + { + myMD.update(digest, digestSz); + myMD.get_digest(digest); + } + + if (keyLeft) + { + int store = MY_MIN(keyLeft, static_cast<int>(digestSz)); + memcpy(&key[keyLen - keyLeft], digest, store); + + keyOutput += store; + keyLeft -= store; + digestLeft -= store; + } + + if (ivLeft && digestLeft) + { + int store = MY_MIN(ivLeft, digestLeft); + memcpy(&iv[ivLen - ivLeft], &digest[digestSz - digestLeft], store); + + keyOutput += store; + ivLeft -= store; + } + } +#elif defined(HAVE_OPENSSL) + const EVP_CIPHER *type = EVP_aes_256_cbc(); + const EVP_MD *digest = EVP_sha1(); + EVP_BytesToKey(type, digest, salt, (uchar*) secret, strlen(secret), 1, key, iv); +#endif +} /** - Crypt buffer with AES encryption algorithm. + Crypt buffer with AES CBC encryption algorithm. SYNOPSIS - my_aes_encrypt() + my_aes_encrypt_cbc() @param source [in] Pointer to data for encryption @param source_length [in] Size of encryption data @param dest [out] Buffer to place encrypted data (must be large enough) + @param dest_length [out] Pointer to size of encrypted data @param key [in] Key to be used for encryption - @param key_length [in] Length of the key. Will handle keys of any length + @param key_length [in] Length of the key. 16, 24 or 32 + @param iv [in] Iv to be used for encryption + @param iv_length [in] Length of the iv. should be 16. + @param noPadding [in] if set to true, no padding is used. if the input length is not a + multiple of the AES block size, trailing bytes are only copied to destination buffer. + This allows currently the same interface for CBC, ECB and CTR encryption. + @return + != 0 error + 0 no error +*/ + +static int my_aes_encrypt_cbc(const uchar* source, uint32 source_length, + uchar* dest, uint32* dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + uint8 remaining_bytes = (noPadding == 0) ? 0 : source_length % MY_AES_BLOCK_SIZE; + source_length = source_length - remaining_bytes; + +#ifdef HAVE_YASSL + TaoCrypt::AES_CBC_Encryption enc; + /* 128 bit block used for padding */ + uint8 block[MY_AES_BLOCK_SIZE]; + int num_blocks; /* number of complete blocks */ + int i; + switch(key_length) { + case 16: + break; + case 24: + break; + case 32: + break; + default: + return AES_BAD_KEYSIZE; + } + + enc.SetKey((const TaoCrypt::byte *) key, key_length, (const TaoCrypt::byte *) iv); + + num_blocks = source_length / MY_AES_BLOCK_SIZE; + + for (i = num_blocks; i > 0; i--) /* Encode complete blocks */ + { + enc.Process((TaoCrypt::byte *) dest, (const TaoCrypt::byte *) source, + MY_AES_BLOCK_SIZE); + source += MY_AES_BLOCK_SIZE; + dest += MY_AES_BLOCK_SIZE; + } + + if (noPadding) { + if (remaining_bytes!=0) { + memcpy(dest + source_length, source + source_length, remaining_bytes); + } + *dest_length = MY_AES_BLOCK_SIZE * (num_blocks) + remaining_bytes; + return AES_OK; + + } + + /* Encode the rest. We always have incomplete block */ + char pad_len = MY_AES_BLOCK_SIZE - (source_length - + MY_AES_BLOCK_SIZE * num_blocks); + memcpy(block, source, 16 - pad_len); + memset(block + MY_AES_BLOCK_SIZE - pad_len, pad_len, pad_len); + + enc.Process((TaoCrypt::byte *) dest, (const TaoCrypt::byte *) block, + MY_AES_BLOCK_SIZE); + + *dest_length = MY_AES_BLOCK_SIZE * (num_blocks + 1); + return AES_OK; +#elif defined(HAVE_OPENSSL) + MyCipherCtx ctx; + int u_len, f_len; + /* The real key to be used for encryption */ + const EVP_CIPHER* cipher; + switch(key_length) { + case 16: + cipher = EVP_aes_128_cbc(); + break; + case 24: + cipher = EVP_aes_192_cbc(); + break; + case 32: + cipher = EVP_aes_256_cbc(); + break; + default: + return AES_BAD_KEYSIZE; + } + //Initialize Encryption Engine here, default software Engine is default + ENGINE *engine = NULL; + + if (! EVP_EncryptInit_ex(&ctx.ctx, cipher, engine, key, iv)) + return AES_BAD_DATA; /* Error */ + if (noPadding) { + EVP_CIPHER_CTX_set_padding(&ctx.ctx, 0); + } + EVP_CIPHER_CTX_key_length(&ctx.ctx); + OPENSSL_assert(EVP_CIPHER_CTX_key_length(&ctx.ctx) == key_length); + OPENSSL_assert(EVP_CIPHER_CTX_iv_length(&ctx.ctx) == iv_length); + OPENSSL_assert(EVP_CIPHER_CTX_block_size(&ctx.ctx) == 16); + if (! EVP_EncryptUpdate(&ctx.ctx, (unsigned char *) dest, &u_len, + (unsigned const char *) source, source_length)) + return AES_BAD_DATA; /* Error */ + if (! EVP_EncryptFinal_ex(&ctx.ctx, (unsigned char *) dest + u_len, &f_len)) + return AES_BAD_DATA; /* Error */ + + if (remaining_bytes!=0) { + memcpy(dest + source_length, source + source_length, remaining_bytes); + } + *dest_length = (unsigned long int) (u_len + f_len + remaining_bytes); + + return AES_OK; +#else + /* currently Open SSL is required */ + return AES_BAD_DATA; +#endif +} + + +/** + Crypt buffer with AES ECB encryption algorithm. + SYNOPSIS + my_aes_encrypt_ecb() + @param source [in] Pointer to data for encryption + @param source_length [in] Size of encryption data + @param dest [out] Buffer to place encrypted data (must be large enough) + @param dest_length [out] Pointer to size of encrypted data + @param key [in] Key to be used for encryption + @param key_length [in] Length of the key. 16, 24 or 32 + @param iv [in] Iv to be used for encryption + @param iv_length [in] Length of the iv. should be 16. + @param noPadding [in] if set to true, no padding is used. if the input length is not a + multiple of the AES block size, trailing bytes are only copied to destination buffer. + This allows currently the same interface for CBC, ECB and CTR encryption. @return - >= 0 Size of encrypted data - < 0 Error + != 0 error + 0 no error +*/ + +static int my_aes_encrypt_ecb(const uchar* source, uint32 source_length, + uchar* dest, uint32* dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + uint8 remaining_bytes = (noPadding == 0) ? 0 : source_length % MY_AES_BLOCK_SIZE; + source_length = source_length - remaining_bytes; + +#ifdef HAVE_YASSL + TaoCrypt::AES_ECB_Encryption enc; + /* 128 bit block used for padding */ + uint8 block[MY_AES_BLOCK_SIZE]; + int num_blocks; /* number of complete blocks */ + int i; + switch(key_length) { + case 16: + break; + case 24: + break; + case 32: + break; + default: + return AES_BAD_KEYSIZE; + } + + enc.SetKey((const TaoCrypt::byte *) key, key_length, (const TaoCrypt::byte *) iv); + + num_blocks = source_length / MY_AES_BLOCK_SIZE; + + for (i = num_blocks; i > 0; i--) /* Encode complete blocks */ + { + enc.Process((TaoCrypt::byte *) dest, (const TaoCrypt::byte *) source, + MY_AES_BLOCK_SIZE); + source += MY_AES_BLOCK_SIZE; + dest += MY_AES_BLOCK_SIZE; + } + + if (noPadding) { + if (remaining_bytes!=0) { + memcpy(dest + source_length, source + source_length, remaining_bytes); + } + *dest_length = MY_AES_BLOCK_SIZE * (num_blocks) + remaining_bytes; + return AES_OK; + + } + + /* Encode the rest. We always have incomplete block */ + char pad_len = MY_AES_BLOCK_SIZE - (source_length - + MY_AES_BLOCK_SIZE * num_blocks); + memcpy(block, source, 16 - pad_len); + memset(block + MY_AES_BLOCK_SIZE - pad_len, pad_len, pad_len); + + enc.Process((TaoCrypt::byte *) dest, (const TaoCrypt::byte *) block, + MY_AES_BLOCK_SIZE); + + *dest_length = MY_AES_BLOCK_SIZE * (num_blocks + 1); + return AES_OK; +#elif defined(HAVE_OPENSSL) + MyCipherCtx ctx; + int u_len, f_len; + /* The real key to be used for encryption */ + const EVP_CIPHER* cipher; + switch(key_length) { + case 16: + cipher = EVP_aes_128_ecb(); + break; + case 24: + cipher = EVP_aes_192_ecb(); + break; + case 32: + cipher = EVP_aes_256_ecb(); + break; + default: + return AES_BAD_KEYSIZE; + } + //Initialize Encryption Engine here, default software Engine is default + ENGINE *engine = NULL; + + if (! EVP_EncryptInit_ex(&ctx.ctx, cipher, engine, key, iv)) + return AES_BAD_DATA; /* Error */ + if (noPadding) { + EVP_CIPHER_CTX_set_padding(&ctx.ctx, 0); + } + EVP_CIPHER_CTX_key_length(&ctx.ctx); + OPENSSL_assert(EVP_CIPHER_CTX_key_length(&ctx.ctx) == key_length); + OPENSSL_assert(EVP_CIPHER_CTX_iv_length(&ctx.ctx) == iv_length); + OPENSSL_assert(EVP_CIPHER_CTX_block_size(&ctx.ctx) == 16); + if (! EVP_EncryptUpdate(&ctx.ctx, (unsigned char *) dest, &u_len, + (unsigned const char *) source, source_length)) + return AES_BAD_DATA; /* Error */ + if (! EVP_EncryptFinal_ex(&ctx.ctx, (unsigned char *) dest + u_len, &f_len)) + return AES_BAD_DATA; /* Error */ + + if (remaining_bytes!=0) { + memcpy(dest + source_length, source + source_length, remaining_bytes); + } + *dest_length = (unsigned long int) (u_len + f_len + remaining_bytes); + + return AES_OK; +#else + /* currently Open SSL is required */ + return AES_BAD_DATA; +#endif +} + + + +/** + AES decryption - CBC mode + + SYNOPSIS + my_aes_decrypt_cbc() + @param source [in] Pointer to data to decrypt + @param source_length [in] Size of data + @param dest [out] Buffer to place decrypted data (must be large enough) + @param dest_length [out] Pointer to size of decrypted data + @param key [in] Key to be used for decryption + @param key_length [in] Length of the key. 16, 24 or 32 + @param iv [in] Iv to be used for encryption + @param iv_length [in] Length of the iv. should be 16. + @param noPadding [in] if set to true, no padding is used. if the input length is not a + multiple of the AES block size, trailing bytes are only copied to destination buffer. + This allows currently the same interface for CBC, ECB and CTR encryption. + + @return + != 0 error + 0 no error +*/ + +static int my_aes_decrypt_cbc(const uchar* source, uint32 source_length, + uchar* dest, uint32 *dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + uint8 remaining_bytes = (noPadding == 0) ? 0 : source_length % MY_AES_BLOCK_SIZE; + source_length = source_length - remaining_bytes; + + +#ifdef HAVE_YASSL + TaoCrypt::AES_CBC_Decryption dec; + /* 128 bit block used for padding */ + uint8 block[MY_AES_BLOCK_SIZE]; + uint num_blocks; /* Number of complete blocks */ + int i; + switch(key_length) { + case 16: + break; + case 24: + break; + case 32: + break; + default: + return AES_BAD_KEYSIZE; + } + + dec.SetKey((const TaoCrypt::byte *) key, key_length, iv); + + num_blocks = source_length / MY_AES_BLOCK_SIZE; + + if ((source_length != num_blocks * MY_AES_BLOCK_SIZE) || num_blocks == 0 ) + /* Input size has to be even and at least one block */ + return AES_BAD_DATA; + + /* Decode all but last blocks */ + for (i = num_blocks - 1; i > 0; i--) + { + dec.Process((TaoCrypt::byte *) dest, (const TaoCrypt::byte *) source, + MY_AES_BLOCK_SIZE); + source += MY_AES_BLOCK_SIZE; + dest += MY_AES_BLOCK_SIZE; + } + + dec.Process((TaoCrypt::byte *) block, (const TaoCrypt::byte *) source, + MY_AES_BLOCK_SIZE); + + if (noPadding) { + memcpy(dest, block, MY_AES_BLOCK_SIZE); + if (remaining_bytes!=0) { + memcpy(dest + source_length, source + source_length, remaining_bytes); + } + *dest_length = MY_AES_BLOCK_SIZE * num_blocks + remaining_bytes; + return AES_OK; + } + + /* Use last char in the block as size */ + uint pad_len = (uint) (uchar) block[MY_AES_BLOCK_SIZE - 1]; + + if (pad_len > MY_AES_BLOCK_SIZE) + return AES_BAD_DATA; + /* We could also check whole padding but we do not really need this */ + + memcpy(dest, block, MY_AES_BLOCK_SIZE - pad_len); + *dest_length = MY_AES_BLOCK_SIZE * num_blocks - pad_len; + return AES_OK; +#elif defined(HAVE_OPENSSL) + MyCipherCtx ctx; + int u_len, f_len; + + const EVP_CIPHER* cipher; + switch(key_length) { + case 16: + cipher = EVP_aes_128_cbc(); + break; + case 24: + cipher = EVP_aes_192_cbc(); + break; + case 32: + cipher = EVP_aes_256_cbc(); + break; + default: + return AES_BAD_KEYSIZE; + } + //Initialize Encryption Engine here, default software Engine is default + ENGINE *engine = NULL; + + if (! EVP_DecryptInit_ex(&ctx.ctx, cipher, engine, key, iv)) + return AES_BAD_DATA; /* Error */ + if (noPadding) { + EVP_CIPHER_CTX_set_padding(&ctx.ctx, 0); + } + OPENSSL_assert(EVP_CIPHER_CTX_key_length(&ctx.ctx) == key_length); + OPENSSL_assert(EVP_CIPHER_CTX_iv_length(&ctx.ctx) == iv_length); + OPENSSL_assert(EVP_CIPHER_CTX_block_size(&ctx.ctx) == 16); + if (! EVP_DecryptUpdate(&ctx.ctx, (unsigned char *) dest, &u_len, + (unsigned char *)source, source_length)) + return AES_BAD_DATA; /* Error */ + if (! EVP_DecryptFinal_ex(&ctx.ctx, (unsigned char *) dest + u_len, &f_len)) { + *dest_length = (unsigned long int) u_len; + return AES_BAD_DATA; + } + if (remaining_bytes!=0) { + memcpy(dest + source_length, source + source_length, remaining_bytes); + } + *dest_length = (unsigned long int) (u_len + f_len) + remaining_bytes; +#endif + return AES_OK; +} + +/** + AES decryption - ECB mode + + SYNOPSIS + my_aes_decrypt_ecb() + @param source [in] Pointer to data to decrypt + @param source_length [in] Size of data + @param dest [out] Buffer to place decrypted data (must be large enough) + @param dest_length [out] Pointer to size of decrypted data + @param key [in] Key to be used for decryption + @param key_length [in] Length of the key. 16, 24 or 32 + @param iv [in] Iv to be used for encryption + @param iv_length [in] Length of the iv. should be 16. + @param noPadding [in] if set to true, no padding is used. if the input length is not a + multiple of the AES block size, trailing bytes are only copied to destination buffer. + This allows currently the same interface for CBC, ECB and CTR encryption. + + @return + != 0 error + 0 no error */ -int my_aes_encrypt(const char* source, int source_length, char* dest, +static int my_aes_decrypt_ecb(const uchar* source, uint32 source_length, + uchar* dest, uint32 *dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + uint8 remaining_bytes = (noPadding == 0) ? 0 : source_length % MY_AES_BLOCK_SIZE; + source_length = source_length - remaining_bytes; + + +#ifdef HAVE_YASSL + TaoCrypt::AES_ECB_Decryption dec; + /* 128 bit block used for padding */ + uint8 block[MY_AES_BLOCK_SIZE]; + uint num_blocks; /* Number of complete blocks */ + int i; + switch(key_length) { + case 16: + break; + case 24: + break; + case 32: + break; + default: + return AES_BAD_KEYSIZE; + } + + dec.SetKey((const TaoCrypt::byte *) key, key_length, iv); + + num_blocks = source_length / MY_AES_BLOCK_SIZE; + + if ((source_length != num_blocks * MY_AES_BLOCK_SIZE) || num_blocks == 0 ) + /* Input size has to be even and at least one block */ + return AES_BAD_DATA; + + /* Decode all but last blocks */ + for (i = num_blocks - 1; i > 0; i--) + { + dec.Process((TaoCrypt::byte *) dest, (const TaoCrypt::byte *) source, + MY_AES_BLOCK_SIZE); + source += MY_AES_BLOCK_SIZE; + dest += MY_AES_BLOCK_SIZE; + } + + dec.Process((TaoCrypt::byte *) block, (const TaoCrypt::byte *) source, + MY_AES_BLOCK_SIZE); + + if (noPadding) { + memcpy(dest, block, MY_AES_BLOCK_SIZE); + if (remaining_bytes!=0) { + memcpy(dest + source_length, source + source_length, remaining_bytes); + } + *dest_length = MY_AES_BLOCK_SIZE * num_blocks + remaining_bytes; + return AES_OK; + } + + /* Use last char in the block as size */ + uint pad_len = (uint) (uchar) block[MY_AES_BLOCK_SIZE - 1]; + + if (pad_len > MY_AES_BLOCK_SIZE) + return AES_BAD_DATA; + /* We could also check whole padding but we do not really need this */ + + memcpy(dest, block, MY_AES_BLOCK_SIZE - pad_len); + *dest_length = MY_AES_BLOCK_SIZE * num_blocks - pad_len; + return AES_OK; +#elif defined(HAVE_OPENSSL) + MyCipherCtx ctx; + int u_len, f_len; + + const EVP_CIPHER* cipher; + switch(key_length) { + case 16: + cipher = EVP_aes_128_ecb(); + break; + case 24: + cipher = EVP_aes_192_ecb(); + break; + case 32: + cipher = EVP_aes_256_ecb(); + break; + default: + return AES_BAD_KEYSIZE; + } + //Initialize Encryption Engine here, default software Engine is default + ENGINE *engine = NULL; + + if (! EVP_DecryptInit_ex(&ctx.ctx, cipher, engine, key, iv)) + return AES_BAD_DATA; /* Error */ + if (noPadding) { + EVP_CIPHER_CTX_set_padding(&ctx.ctx, 0); + } + OPENSSL_assert(EVP_CIPHER_CTX_key_length(&ctx.ctx) == key_length); + OPENSSL_assert(EVP_CIPHER_CTX_iv_length(&ctx.ctx) == iv_length); + OPENSSL_assert(EVP_CIPHER_CTX_block_size(&ctx.ctx) == 16); + if (! EVP_DecryptUpdate(&ctx.ctx, (unsigned char *) dest, &u_len, + (unsigned char *)source, source_length)) + return AES_BAD_DATA; /* Error */ + if (! EVP_DecryptFinal_ex(&ctx.ctx, (unsigned char *) dest + u_len, &f_len)) { + *dest_length = (unsigned long int) u_len; + return AES_BAD_DATA; + } + if (remaining_bytes!=0) { + memcpy(dest + source_length, source + source_length, remaining_bytes); + } + *dest_length = (unsigned long int) (u_len + f_len) + remaining_bytes; + +#endif + return AES_OK; +} + + + + + +/** + Encryption interface that doesn't do anything (for testing) + + SYNOPSIS + my_aes_encrypt_none() + @param source [in] Pointer to data for encryption + @param source_length [in] Size of encryption data + @param dest [out] Buffer to place encrypted data (must be large enough) + @param dest_length [out] Pointer to size of encrypted data + @param key [in] Key to be used for encryption + @param key_length [in] Length of the key. 16, 24 or 32 + @param iv [in] Iv to be used for encryption + @param iv_length [in] Length of the iv. should be 16. + @param noPadding [in] unused + @return + != 0 error + 0 no error +*/ + +static int my_aes_encrypt_none(const uchar* source, uint32 source_length, + uchar* dest, uint32* dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + memcpy(dest, source, source_length); + *dest_length= source_length; + return 0; +} + + +/** + Decryption interface that doesn't do anything (for testing) + + SYNOPSIS + my_aes_decrypt_none() + @param source [in] Pointer to data to decrypt + @param source_length [in] Size of data + @param dest [out] Buffer to place decrypted data (must be large enough) + @param dest_length [out] Pointer to size of decrypted data + @param key [in] Key to be used for decryption + @param key_length [in] Length of the key. 16, 24 or 32 + @param iv [in] Iv to be used for encryption + @param iv_length [in] Length of the iv. should be 16. + @param noPadding [in] unused + + @return + != 0 error + 0 no error +*/ + +int my_aes_decrypt_none(const uchar* source, uint32 source_length, + uchar* dest, uint32 *dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + memcpy(dest, source, source_length); + *dest_length= source_length; + return 0; +} + +/** + Initialize encryption methods +*/ + +my_aes_decrypt_dynamic_type my_aes_decrypt_dynamic= my_aes_decrypt_none; +my_aes_encrypt_dynamic_type my_aes_encrypt_dynamic= my_aes_encrypt_none; +enum_my_aes_encryption_algorithm current_aes_dynamic_method= MY_AES_ALGORITHM_NONE; + +my_bool my_aes_init_dynamic_encrypt(enum_my_aes_encryption_algorithm method) +{ + switch (method) + { + /* used for encrypting tables */ + case MY_AES_ALGORITHM_ECB: + my_aes_encrypt_dynamic= my_aes_encrypt_ecb; + my_aes_decrypt_dynamic= my_aes_decrypt_ecb; + break; + case MY_AES_ALGORITHM_CBC: + my_aes_encrypt_dynamic= my_aes_encrypt_cbc; + my_aes_decrypt_dynamic= my_aes_decrypt_cbc; + break; +#ifdef HAVE_EncryptAes128Ctr + /* encrypt everything, with a set of keys */ + case MY_AES_ALGORITHM_CTR: + my_aes_encrypt_dynamic= my_aes_encrypt_ctr; + my_aes_decrypt_dynamic= my_aes_decrypt_ctr; + break; +#endif + /* Simulate encrypting interface */ + case MY_AES_ALGORITHM_NONE: + my_aes_encrypt_dynamic= my_aes_encrypt_none; + my_aes_decrypt_dynamic= my_aes_decrypt_none; + break; + default: + return 1; + } + current_aes_dynamic_method= method; + return 0; +} + +my_aes_decrypt_dynamic_type +get_aes_decrypt_func(enum_my_aes_encryption_algorithm method) +{ + switch (method) + { + /* used for encrypting tables */ + case MY_AES_ALGORITHM_ECB: + return my_aes_decrypt_ecb; + break; + case MY_AES_ALGORITHM_CBC: + return my_aes_decrypt_cbc; + break; +#ifdef HAVE_EncryptAes128Ctr + /* encrypt everything, with a set of keys */ + case MY_AES_ALGORITHM_CTR: + return my_aes_decrypt_ctr; + break; +#endif + /* Simulate encrypting interface */ + case MY_AES_ALGORITHM_NONE: + return my_aes_decrypt_none; + break; + default: + return NULL; + } + return NULL; +} + +my_aes_encrypt_dynamic_type +get_aes_encrypt_func(enum_my_aes_encryption_algorithm method) +{ + switch (method) + { + /* used for encrypting tables */ + case MY_AES_ALGORITHM_ECB: + return my_aes_encrypt_ecb; + break; + case MY_AES_ALGORITHM_CBC: + return my_aes_encrypt_cbc; + break; +#ifdef HAVE_EncryptAes128Ctr + /* encrypt everything, with a set of keys */ + case MY_AES_ALGORITHM_CTR: + return my_aes_encrypt_ctr; + break; +#endif + /* Simulate encrypting interface */ + case MY_AES_ALGORITHM_NONE: + return my_aes_encrypt_none; + break; + default: + return NULL; + } + return NULL; +} + + +/**************************************************************** + Encryption function visible to MariaDB users +****************************************************************/ + +int my_aes_encrypt(const uchar* source, int source_length, uchar* dest, const char* key, int key_length) { #if defined(HAVE_YASSL) TaoCrypt::AES_ECB_Encryption enc; + /* 128 bit block used for padding */ uint8 block[MY_AES_BLOCK_SIZE]; int num_blocks; /* number of complete blocks */ @@ -194,7 +961,7 @@ int my_aes_encrypt(const char* source, int source_length, char* dest, < 0 Error */ -int my_aes_decrypt(const char *source, int source_length, char *dest, +int my_aes_decrypt(const uchar *source, int source_length, uchar *dest, const char *key, int key_length) { #if defined(HAVE_YASSL) @@ -275,4 +1042,3 @@ int my_aes_get_size(int source_length) return MY_AES_BLOCK_SIZE * (source_length / MY_AES_BLOCK_SIZE) + MY_AES_BLOCK_SIZE; } - diff --git a/mysys_ssl/my_crypt.cc b/mysys_ssl/my_crypt.cc new file mode 100644 index 00000000000..0d49999fdfa --- /dev/null +++ b/mysys_ssl/my_crypt.cc @@ -0,0 +1,370 @@ +/* + TODO: add support for YASSL +*/ + +#include <my_global.h> +#include <my_crypt.h> + +/* YASSL doesn't support EVP_CIPHER_CTX */ +#ifdef HAVE_EncryptAes128Ctr + +#include "mysql.h" +#include <openssl/evp.h> +#include <openssl/aes.h> +#include <openssl/rand.h> + +static const int CRYPT_ENCRYPT = 1; +static const int CRYPT_DECRYPT = 0; + +class Encrypter { + public: + virtual ~Encrypter() {} + + virtual Crypt_result Encrypt(const uchar* plaintext, + int plaintext_size, + uchar* ciphertext, + int* ciphertext_used) = 0; + virtual Crypt_result GetTag(uchar* tag, int tag_size) = 0; +}; + +class Decrypter { + public: + virtual ~Decrypter() {} + + virtual Crypt_result SetTag(const uchar* tag, int tag_size) = 0; + virtual Crypt_result Decrypt(const uchar* ciphertext, + int ciphertext_size, + uchar* plaintext, + int* plaintext_used) = 0; + virtual Crypt_result CheckTag() = 0; +}; + +class Crypto { + public: + virtual ~Crypto(); + + Crypt_result Crypt(const uchar* input, int input_size, + uchar* output, int* output_used); + + protected: + Crypto(); + + EVP_CIPHER_CTX ctx; +}; + + +/* Various crypto implementations */ + +class Aes128CtrCrypto : public Crypto { + public: + virtual Crypt_result Init(const uchar* key, const uchar* iv, + int iv_size); + + protected: + Aes128CtrCrypto() {} + + virtual int mode() = 0; +}; + +class Aes128CtrEncrypter : public Aes128CtrCrypto, public Encrypter { + public: + Aes128CtrEncrypter() {} + virtual Crypt_result Encrypt(const uchar* plaintext, + int plaintext_size, + uchar* ciphertext, + int* ciphertext_used); + + virtual Crypt_result GetTag(uchar* tag, int tag_size) { + DBUG_ASSERT(false); + return AES_INVALID; + } + + protected: + virtual int mode() { + return CRYPT_ENCRYPT; + } + + private: + Aes128CtrEncrypter(const Aes128CtrEncrypter& o); + Aes128CtrEncrypter& operator=(const Aes128CtrEncrypter& o); +}; + +class Aes128CtrDecrypter : public Aes128CtrCrypto, public Decrypter { + public: + Aes128CtrDecrypter() {} + virtual Crypt_result Decrypt(const uchar* ciphertext, + int ciphertext_size, + uchar* plaintext, + int* plaintext_used); + + virtual Crypt_result SetTag(const uchar* tag, int tag_size) { + DBUG_ASSERT(false); + return AES_INVALID; + } + + virtual Crypt_result CheckTag() { + DBUG_ASSERT(false); + return AES_INVALID; + } + + protected: + virtual int mode() { + return CRYPT_DECRYPT; + } + + private: + Aes128CtrDecrypter(const Aes128CtrDecrypter& o); + Aes128CtrDecrypter& operator=(const Aes128CtrDecrypter& o); +}; + +class Aes128EcbCrypto : public Crypto { + public: + virtual Crypt_result Init(const unsigned char* key); + + protected: + Aes128EcbCrypto() {} + + virtual int mode() = 0; +}; + +class Aes128EcbEncrypter : public Aes128EcbCrypto, public Encrypter { + public: + Aes128EcbEncrypter() {} + virtual Crypt_result Encrypt(const unsigned char* plaintext, + int plaintext_size, + unsigned char* ciphertext, + int* ciphertext_used); + + virtual Crypt_result GetTag(unsigned char* tag, int tag_size) { + DBUG_ASSERT(false); + return AES_INVALID; + } + + protected: + virtual int mode() { + return CRYPT_ENCRYPT; + } + + private: + Aes128EcbEncrypter(const Aes128EcbEncrypter& o); + Aes128EcbEncrypter& operator=(const Aes128EcbEncrypter& o); +}; + +class Aes128EcbDecrypter : public Aes128EcbCrypto, public Decrypter { + public: + Aes128EcbDecrypter() {} + virtual Crypt_result Decrypt(const unsigned char* ciphertext, + int ciphertext_size, + unsigned char* plaintext, + int* plaintext_used); + + virtual Crypt_result SetTag(const unsigned char* tag, int tag_size) { + DBUG_ASSERT(false); + return AES_INVALID; + } + + virtual Crypt_result CheckTag() { + DBUG_ASSERT(false); + return AES_INVALID; + } + + protected: + virtual int mode() { + return CRYPT_DECRYPT; + } + + private: + Aes128EcbDecrypter(const Aes128EcbDecrypter& o); + Aes128EcbDecrypter& operator=(const Aes128EcbDecrypter& o); +}; + + +Crypto::~Crypto() { + EVP_CIPHER_CTX_cleanup(&ctx); +} + +Crypto::Crypto() { + EVP_CIPHER_CTX_init(&ctx); +} + +/* + WARNING: It is allowed to have output == NULL, for special cases like AAD + support in AES GCM. output_used however must never be NULL. +*/ + +Crypt_result Crypto::Crypt(const uchar* input, int input_size, + uchar* output, int* output_used) { + DBUG_ASSERT(input != NULL); + DBUG_ASSERT(output_used != NULL); + if (!EVP_CipherUpdate(&ctx, output, output_used, input, input_size)) { + return AES_OPENSSL_ERROR; + } + + return AES_OK; +} + +Crypt_result Aes128CtrCrypto::Init(const uchar* key, + const uchar* iv, + int iv_size) { + if (iv_size != 16) { + DBUG_ASSERT(false); + return AES_BAD_IV; + } + + if (!EVP_CipherInit_ex(&ctx, EVP_aes_128_ctr(), NULL, key, iv, mode())) { + return AES_OPENSSL_ERROR; + } + + return AES_OK; +} + +Crypt_result Aes128CtrEncrypter::Encrypt(const uchar* plaintext, + int plaintext_size, + uchar* ciphertext, + int* ciphertext_used) { + Crypt_result res = Crypt(plaintext, plaintext_size, ciphertext, + ciphertext_used); + DBUG_ASSERT(*ciphertext_used == plaintext_size); + return res; +} + +Crypt_result Aes128CtrDecrypter::Decrypt(const uchar* ciphertext, + int ciphertext_size, + uchar* plaintext, + int* plaintext_used) { + Crypt_result res = Crypt(ciphertext, ciphertext_size, plaintext, + plaintext_used); + DBUG_ASSERT(*plaintext_used == ciphertext_size); + return res; +} + + +Crypt_result Aes128EcbCrypto::Init(const unsigned char* key) { + if (!EVP_CipherInit_ex(&ctx, EVP_aes_128_ecb(), NULL, key, NULL, mode())) { + return AES_OPENSSL_ERROR; + } + + return AES_OK; +} + +Crypt_result Aes128EcbEncrypter::Encrypt(const unsigned char* plaintext, + int plaintext_size, + unsigned char* ciphertext, + int* ciphertext_used) { + Crypt_result res = Crypt(plaintext, plaintext_size, + ciphertext, ciphertext_used); + DBUG_ASSERT(*ciphertext_used == plaintext_size); + return res; +} + +Crypt_result Aes128EcbDecrypter::Decrypt(const unsigned char* ciphertext, + int ciphertext_size, + unsigned char* plaintext, + int* plaintext_used) { + Crypt_result res = Crypt(ciphertext, ciphertext_size, + plaintext, plaintext_used); + DBUG_ASSERT(*plaintext_used == ciphertext_size); + return res; +} + +C_MODE_START + + + /* Encrypt and decrypt according to Aes128Ctr */ + +Crypt_result my_aes_encrypt_ctr(const uchar* source, uint32 source_length, + uchar* dest, uint32* dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + Aes128CtrEncrypter encrypter; + Crypt_result res = encrypter.Init(key, iv, iv_length); + if (res != AES_OK) + return res; + return encrypter.Encrypt(source, source_length, dest, (int*)dest_length); +} + + +Crypt_result my_aes_decrypt_ctr(const uchar* source, uint32 source_length, + uchar* dest, uint32* dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + Aes128CtrDecrypter decrypter; + + Crypt_result res = decrypter.Init(key, iv, iv_length); + if (res != AES_OK) + return res; + return decrypter.Decrypt(source, source_length, dest, (int*)dest_length); +} + + +Crypt_result my_aes_encrypt_ecb(const uchar* source, uint32 source_length, + uchar* dest, uint32* dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + Aes128EcbEncrypter encrypter; + Crypt_result res = encrypter.Init(key); + if (res != AES_OK) + return res; + return encrypter.Encrypt(source, source_length, dest, (int*)dest_length); +} + +Crypt_result my_aes_decrypt_ecb(const uchar* source, uint32 source_length, + uchar* dest, uint32* dest_length, + const unsigned char* key, uint8 key_length, + const unsigned char* iv, uint8 iv_length, + uint noPadding) +{ + Aes128EcbDecrypter decrypter; + + Crypt_result res = decrypter.Init(key); + + if (res != AES_OK) + return res; + return decrypter.Decrypt(source, source_length, dest, (int*)dest_length); +} + +C_MODE_END + +#endif /* HAVE_EncryptAes128Ctr */ + +#if defined(HAVE_YASSL) + +#include <random.hpp> + +C_MODE_START + +Crypt_result my_random_bytes(uchar* buf, int num) +{ + TaoCrypt::RandomNumberGenerator rand; + rand.GenerateBlock((TaoCrypt::byte*) buf, num); + return AES_OK; +} + +C_MODE_END + +#else /* OpenSSL */ + +C_MODE_START + +Crypt_result my_random_bytes(uchar* buf, int num) +{ + /* + Unfortunately RAND_bytes manual page does not provide any guarantees + in relation to blocking behavior. Here we explicitly use SSLeay random + instead of whatever random engine is currently set in OpenSSL. That way + we are guaranteed to have a non-blocking random. + */ + RAND_METHOD* rand = RAND_SSLeay(); + if (rand == NULL || rand->bytes(buf, num) != 1) + return AES_OPENSSL_ERROR; + return AES_OK; +} + +C_MODE_END +#endif /* HAVE_YASSL */ diff --git a/mysys_ssl/my_crypt_key_management.cc b/mysys_ssl/my_crypt_key_management.cc new file mode 100644 index 00000000000..69efed32567 --- /dev/null +++ b/mysys_ssl/my_crypt_key_management.cc @@ -0,0 +1,110 @@ +#include <my_global.h> +#include <my_crypt_key_management.h> +#include <cstring> + +#ifndef DBUG_OFF +#include <myisampack.h> +my_bool debug_use_static_encryption_keys = 0; + +#ifdef HAVE_PSI_INTERFACE +PSI_rwlock_key key_LOCK_dbug_encryption_key_version; +#endif +mysql_rwlock_t LOCK_dbug_encryption_key_version; +unsigned int opt_debug_encryption_key_version = 0; +#endif + +/** + * Default functions + */ +int GetLatestCryptoKeyVersionImpl(); +unsigned int HasCryptoKeyImpl(unsigned int version); +int GetCryptoKeySizeImpl(unsigned int version); +int GetCryptoKeyImpl(unsigned int version, unsigned char* key_buffer, + unsigned int size); +int GetCryptoIVImpl(unsigned int version, unsigned char* key_buffer, + unsigned int size); + +/** + * Function pointers for + * - GetLatestCryptoKeyVersion + * - GetCryptoKey + */ +static +struct CryptoKeyFuncs_t cryptoKeyFuncs = { + GetLatestCryptoKeyVersionImpl, + HasCryptoKeyImpl, + GetCryptoKeySizeImpl, + GetCryptoKeyImpl, + GetCryptoIVImpl +}; + +extern "C" +int GetLatestCryptoKeyVersion() { +#ifndef DBUG_OFF + if (debug_use_static_encryption_keys) { + mysql_rwlock_rdlock(&LOCK_dbug_encryption_key_version); + unsigned int res = opt_debug_encryption_key_version; + mysql_rwlock_unlock(&LOCK_dbug_encryption_key_version); + return res; + } +#endif + + return (* cryptoKeyFuncs.getLatestCryptoKeyVersionFunc)(); +} + +extern "C" +unsigned int HasCryptoKey(unsigned int version) { + return (* cryptoKeyFuncs.hasCryptoKeyFunc)(version); +} + +extern "C" +int GetCryptoKeySize(unsigned int version) { + return (* cryptoKeyFuncs.getCryptoKeySize)(version); +} + +extern "C" +int GetCryptoKey(unsigned int version, unsigned char* key, unsigned int size) { +#ifndef DBUG_OFF + if (debug_use_static_encryption_keys) { + memset(key, 0, size); + // Just don't support tiny keys, no point anyway. + if (size < 4) { + return 1; + } + + mi_int4store(key, version); + return 0; + } +#endif + + return (* cryptoKeyFuncs.getCryptoKeyFunc)(version, key, size); +} + +extern "C" +int GetCryptoIV(unsigned int version, unsigned char* key, unsigned int size) { + return (* cryptoKeyFuncs.getCryptoIVFunc)(version, key, size); +} + +extern "C" +void +InstallCryptoKeyFunctions(const struct CryptoKeyFuncs_t* _cryptoKeyFuncs) +{ + if (_cryptoKeyFuncs == NULL) + { + /* restore defaults wHashhen called with NULL argument */ + cryptoKeyFuncs.getLatestCryptoKeyVersionFunc = + GetLatestCryptoKeyVersionImpl; + cryptoKeyFuncs.hasCryptoKeyFunc = + HasCryptoKeyImpl; + cryptoKeyFuncs.getCryptoKeySize = + GetCryptoKeySizeImpl; + cryptoKeyFuncs.getCryptoKeyFunc = + GetCryptoKeyImpl; + cryptoKeyFuncs.getCryptoIVFunc = + GetCryptoIVImpl; + } + else + { + cryptoKeyFuncs = *_cryptoKeyFuncs; + } +} diff --git a/mysys_ssl/my_crypt_key_management_impl.cc b/mysys_ssl/my_crypt_key_management_impl.cc new file mode 100644 index 00000000000..af2077d8d15 --- /dev/null +++ b/mysys_ssl/my_crypt_key_management_impl.cc @@ -0,0 +1,34 @@ +#include <my_global.h> + +// TODO Not yet implemented. +int GetLatestCryptoKeyVersionImpl() +{ + abort(); + return 0; /* Keep compiler happy */ +} + +unsigned int HasCryptoKeyImpl(unsigned int version) +{ + abort(); + return 0; /* Keep compiler happy */ +} + +int GetCryptoKeySizeImpl(unsigned int version) +{ + abort(); + return 0; /* Keep compiler happy */ +} + +int GetCryptoKeyImpl(unsigned int version, unsigned char* key, + unsigned int key_size) +{ + abort(); + return 0; /* Keep compiler happy */ +} + +int GetCryptoIVImpl(unsigned int version, unsigned char* key, + unsigned int key_size) +{ + abort(); + return 0; /* Keep compiler happy */ +} |