diff options
Diffstat (limited to 'src/crypto/aes_method.c')
-rw-r--r-- | src/crypto/aes_method.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/src/crypto/aes_method.c b/src/crypto/aes_method.c new file mode 100644 index 00000000..47193539 --- /dev/null +++ b/src/crypto/aes_method.c @@ -0,0 +1,357 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001, 2012 Oracle and/or its affiliates. All rights reserved. + * + * Some parts of this code originally written by Adam Stubblefield, + * -- astubble@rice.edu. + * + * $Id$ + */ + +#include "db_config.h" + +#include "db_int.h" +#include "dbinc/crypto.h" +#include "dbinc/hmac.h" + +#ifdef HAVE_CRYPTO_IPP +#include <ippcp.h> +#endif + +static void __aes_err __P((ENV *, int)); +static int __aes_derivekeys __P((ENV *, DB_CIPHER *, u_int8_t *, size_t)); + +/* + * __aes_setup -- + * Setup AES functions. + * + * PUBLIC: int __aes_setup __P((ENV *, DB_CIPHER *)); + */ +int +__aes_setup(env, db_cipher) + ENV *env; + DB_CIPHER *db_cipher; +{ + AES_CIPHER *aes_cipher; + int ret; +#ifdef HAVE_CRYPTO_IPP + int ctx_size = 0; + IppStatus ipp_ret; +#endif + + db_cipher->adj_size = __aes_adj_size; + db_cipher->close = __aes_close; + db_cipher->decrypt = __aes_decrypt; + db_cipher->encrypt = __aes_encrypt; + db_cipher->init = __aes_init; + if ((ret = __os_calloc(env, 1, sizeof(AES_CIPHER), &aes_cipher)) != 0) + return (ret); +#ifdef HAVE_CRYPTO_IPP + /* + * IPP AES encryption context size can only be obtained through this + * function call, cannot directly declare IppsRijndael128Spec within + * AES_CIPHER struct. + */ + if ((ipp_ret = ippsRijndael128GetSize(&ctx_size)) != ippStsNoErr) { + __aes_err(env, (int)ipp_ret); + return (EAGAIN); + } + if ((ret = __os_malloc(env, ctx_size, &aes_cipher->ipp_ctx)) != 0) { + __os_free(env, aes_cipher); + return (ret); + } +#endif + db_cipher->data = aes_cipher; + return (0); +} + +/* + * __aes_adj_size -- + * Given a size, return an addition amount needed to meet the + * "chunk" needs of the algorithm. + * + * PUBLIC: u_int __aes_adj_size __P((size_t)); + */ +u_int +__aes_adj_size(len) + size_t len; +{ + if (len % DB_AES_CHUNK == 0) + return (0); + return (DB_AES_CHUNK - (u_int)(len % DB_AES_CHUNK)); +} + +/* + * __aes_close -- + * Destroy the AES encryption instantiation. + * + * PUBLIC: int __aes_close __P((ENV *, void *)); + */ +int +__aes_close(env, data) + ENV *env; + void *data; +{ +#ifdef HAVE_CRYPTO_IPP + AES_CIPHER *aes_cipher = (AES_CIPHER *)data; + __os_free(env, aes_cipher->ipp_ctx); +#endif + __os_free(env, data); + return (0); +} + +/* + * __aes_decrypt -- + * Decrypt data with AES. + * + * PUBLIC: int __aes_decrypt __P((ENV *, void *, void *, + * PUBLIC: u_int8_t *, size_t)); + */ +int +__aes_decrypt(env, aes_data, iv, cipher, cipher_len) + ENV *env; + void *aes_data; + void *iv; + u_int8_t *cipher; + size_t cipher_len; +{ + AES_CIPHER *aes; +#ifdef HAVE_CRYPTO_IPP + IppStatus ipp_ret; +#else + cipherInstance c; +#endif + int ret; + + aes = (AES_CIPHER *)aes_data; + if (iv == NULL || cipher == NULL) + return (EINVAL); + if ((cipher_len % DB_AES_CHUNK) != 0) + return (EINVAL); + +#ifdef HAVE_CRYPTO_IPP + if ((ipp_ret = ippsRijndael128DecryptCBC((const Ipp8u *)cipher, + (Ipp8u *)cipher, cipher_len, (IppsRijndael128Spec *)aes->ipp_ctx, + (const Ipp8u *)iv, 0)) != ippStsNoErr) { + __aes_err(env, (int)ipp_ret); + return (EAGAIN); + } +#else + /* + * Initialize the cipher + */ + if ((ret = __db_cipherInit(&c, MODE_CBC, iv)) < 0) { + __aes_err(env, ret); + return (EAGAIN); + } + + /* Do the decryption */ + if ((ret = __db_blockDecrypt(&c, &aes->decrypt_ki, cipher, + cipher_len * 8, cipher)) < 0) { + __aes_err(env, ret); + return (EAGAIN); + } +#endif + return (0); +} + +/* + * __aes_encrypt -- + * Encrypt data with AES. + * + * PUBLIC: int __aes_encrypt __P((ENV *, void *, void *, + * PUBLIC: u_int8_t *, size_t)); + */ +int +__aes_encrypt(env, aes_data, iv, data, data_len) + ENV *env; + void *aes_data; + void *iv; + u_int8_t *data; + size_t data_len; +{ + AES_CIPHER *aes; +#ifdef HAVE_CRYPTO_IPP + IppStatus ipp_ret; +#else + cipherInstance c; +#endif + u_int32_t tmp_iv[DB_IV_BYTES/4]; + int ret; + + aes = (AES_CIPHER *)aes_data; + if (aes == NULL || data == NULL) + return (EINVAL); + if ((data_len % DB_AES_CHUNK) != 0) + return (EINVAL); + /* + * Generate the IV here. We store it in a tmp IV because + * the IV might be stored within the data we are encrypting + * and so we will copy it over to the given location after + * encryption is done. + * We don't do this outside of there because some encryption + * algorithms someone might add may not use IV's and we always + * want on here. + */ + if ((ret = __db_generate_iv(env, tmp_iv)) != 0) + return (ret); + +#ifdef HAVE_CRYPTO_IPP + if ((ipp_ret = ippsRijndael128EncryptCBC((const Ipp8u *)data, + (Ipp8u *)data, data_len, (IppsRijndael128Spec *)aes->ipp_ctx, + (const Ipp8u *)tmp_iv, 0)) != ippStsNoErr) { + __aes_err(env, (int)ipp_ret); + return (EAGAIN); + } +#else + /* + * Initialize the cipher + */ + if ((ret = __db_cipherInit(&c, MODE_CBC, (char *)tmp_iv)) < 0) { + __aes_err(env, ret); + return (EAGAIN); + } + + /* Do the encryption */ + if ((ret = __db_blockEncrypt(&c, &aes->encrypt_ki, data, data_len * 8, + data)) < 0) { + __aes_err(env, ret); + return (EAGAIN); + } +#endif + memcpy(iv, tmp_iv, DB_IV_BYTES); + return (0); +} + +/* + * __aes_init -- + * Initialize the AES encryption instantiation. + * + * PUBLIC: int __aes_init __P((ENV *, DB_CIPHER *)); + */ +int +__aes_init(env, db_cipher) + ENV *env; + DB_CIPHER *db_cipher; +{ + DB_ENV *dbenv; + + dbenv = env->dbenv; + + return (__aes_derivekeys( + env, db_cipher, (u_int8_t *)dbenv->passwd, dbenv->passwd_len)); +} + +static int +__aes_derivekeys(env, db_cipher, passwd, plen) + ENV *env; + DB_CIPHER *db_cipher; + u_int8_t *passwd; + size_t plen; +{ + AES_CIPHER *aes; + SHA1_CTX ctx; +#ifdef HAVE_CRYPTO_IPP + IppStatus ipp_ret; +#else + int ret; +#endif + u_int32_t temp[DB_MAC_KEY/4]; + + if (passwd == NULL) + return (EINVAL); + + aes = (AES_CIPHER *)db_cipher->data; + + /* Derive the crypto keys */ + __db_SHA1Init(&ctx); + __db_SHA1Update(&ctx, passwd, plen); + __db_SHA1Update(&ctx, (u_int8_t *)DB_ENC_MAGIC, strlen(DB_ENC_MAGIC)); + __db_SHA1Update(&ctx, passwd, plen); + __db_SHA1Final((u_int8_t *)temp, &ctx); + +#ifdef HAVE_CRYPTO_IPP + if ((ipp_ret = ippsRijndael128Init((const Ipp8u *)temp, + IppsRijndaelKey128, (IppsRijndael128Spec *)aes->ipp_ctx)) + != ippStsNoErr) { + __aes_err(env, (int)ipp_ret); + return (EAGAIN); + } +#else + if ((ret = __db_makeKey(&aes->encrypt_ki, DIR_ENCRYPT, + DB_AES_KEYLEN, (char *)temp)) != TRUE) { + __aes_err(env, ret); + return (EAGAIN); + } + if ((ret = __db_makeKey(&aes->decrypt_ki, DIR_DECRYPT, + DB_AES_KEYLEN, (char *)temp)) != TRUE) { + __aes_err(env, ret); + return (EAGAIN); + } +#endif + return (0); +} + +/* + * __aes_err -- + * Handle AES-specific errors. Codes and messages derived from + * rijndael/rijndael-api-fst.h. + */ +static void +__aes_err(env, err) + ENV *env; + int err; +{ + char *errstr; + + switch (err) { +#ifdef HAVE_CRYPTO_IPP + case ippStsNullPtrErr: + errstr = DB_STR("0182", "IPP AES NULL pointer error"); + break; + case ippStsLengthErr: + errstr = DB_STR("0183", "IPP AES length error"); + break; + case ippStsContextMatchErr: + errstr = DB_STR("0184", + "IPP AES context does not match operation"); + break; + case ippStsUnderRunErr: + errstr = DB_STR("0185", "IPP AES srclen size error"); + break; +#else + case BAD_KEY_DIR: + errstr = DB_STR("0186", "AES key direction is invalid"); + break; + case BAD_KEY_MAT: + errstr = DB_STR("0187", + "AES key material not of correct length"); + break; + case BAD_KEY_INSTANCE: + errstr = DB_STR("0188", "AES key passwd not valid"); + break; + case BAD_CIPHER_MODE: + errstr = DB_STR("0189", + "AES cipher in wrong state (not initialized)"); + break; + case BAD_BLOCK_LENGTH: + errstr = DB_STR("0190", "AES bad block length"); + break; + case BAD_CIPHER_INSTANCE: + errstr = DB_STR("0191", "AES cipher instance is invalid"); + break; + case BAD_DATA: + errstr = DB_STR("0192", "AES data contents are invalid"); + break; + case BAD_OTHER: + errstr = DB_STR("0193", "AES unknown error"); + break; +#endif + default: + errstr = DB_STR("0194", "AES error unrecognized"); + break; + } + __db_errx(env, "%s", errstr); + return; +} |