diff options
author | 2018-02-08 08:34:03 -0800 | |
---|---|---|
committer | 2018-02-08 09:14:52 -0800 | |
commit | 0c89b3ccba7c9b7332ab67ae1936aff51ca62367 (patch) | |
tree | 70c1b1d160d4c6d0a83395ca9a87c1264d0d3439 /SoftHSMv2/src/lib/crypto/OSSLEVPSymmetricAlgorithm.cpp | |
parent | 945613b4db4e07f75d2bc7463db580ddfaa700fd (diff) |
Initial sshsm project structure
Issue-ID: AAF-94
Change-Id: I5e82fff418e7567b161acf9b98013a9b85ffc5b4
Signed-off-by: NingSun <ning.sun@intel.com>
Diffstat (limited to 'SoftHSMv2/src/lib/crypto/OSSLEVPSymmetricAlgorithm.cpp')
-rw-r--r-- | SoftHSMv2/src/lib/crypto/OSSLEVPSymmetricAlgorithm.cpp | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/SoftHSMv2/src/lib/crypto/OSSLEVPSymmetricAlgorithm.cpp b/SoftHSMv2/src/lib/crypto/OSSLEVPSymmetricAlgorithm.cpp new file mode 100644 index 0000000..d43e741 --- /dev/null +++ b/SoftHSMv2/src/lib/crypto/OSSLEVPSymmetricAlgorithm.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2010 SURFnet bv + * All rights reserved. + * + * 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 THE AUTHOR ``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 THE AUTHOR 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. + */ + +// TODO: Store EVP context in securely allocated memory + +/***************************************************************************** + OSSLEVPSymmetricAlgorithm.cpp + + OpenSSL symmetric algorithm implementation + *****************************************************************************/ + +#include "config.h" +#include "OSSLEVPSymmetricAlgorithm.h" +#include "OSSLUtil.h" +#include "salloc.h" +#include <openssl/err.h> + +// Constructor +OSSLEVPSymmetricAlgorithm::OSSLEVPSymmetricAlgorithm() +{ + pCurCTX = NULL; + maximumBytes = BN_new(); + BN_one(maximumBytes); + BN_set_negative(maximumBytes, 1); + counterBytes = BN_new(); + BN_zero(counterBytes); +} + +// Destructor +OSSLEVPSymmetricAlgorithm::~OSSLEVPSymmetricAlgorithm() +{ + EVP_CIPHER_CTX_free(pCurCTX); + BN_free(maximumBytes); + BN_free(counterBytes); +} + +// Encryption functions +bool OSSLEVPSymmetricAlgorithm::encryptInit(const SymmetricKey* key, const SymMode::Type mode /* = SymMode::CBC */, const ByteString& IV /* = ByteString()*/, bool padding /* = true */, size_t counterBits /* = 0 */, const ByteString& aad /* = ByteString() */, size_t tagBytes /* = 0 */) +{ + // Call the superclass initialiser + if (!SymmetricAlgorithm::encryptInit(key, mode, IV, padding, counterBits, aad, tagBytes)) + { + return false; + } + + // Check the IV + if (mode != SymMode::GCM && (IV.size() > 0) && (IV.size() != getBlockSize())) + { + ERROR_MSG("Invalid IV size (%d bytes, expected %d bytes)", IV.size(), getBlockSize()); + + ByteString dummy; + SymmetricAlgorithm::encryptFinal(dummy); + + return false; + } + + ByteString iv; + + if (IV.size() > 0) + { + iv = IV; + } + else + { + iv.wipe(getBlockSize()); + } + + // Check the counter bits + if (counterBits > 0) + { + BIGNUM* counter = OSSL::byteString2bn(iv); + BN_mask_bits(counter, counterBits); + + // Reverse the bits + while (counterBits > 0) + { + counterBits--; + if (BN_is_bit_set(counter, counterBits)) + { + BN_clear_bit(counter, counterBits); + } + else + { + BN_set_bit(counter, counterBits); + } + } + + // Set the maximum bytes + BN_add_word(counter, 1); + BN_mul_word(counter, getBlockSize()); + BN_copy(maximumBytes, counter); + BN_free(counter); + BN_zero(counterBytes); + } + else + { + BN_one(maximumBytes); + BN_set_negative(maximumBytes, 1); + } + + // Determine the cipher class + const EVP_CIPHER* cipher = getCipher(); + + if (cipher == NULL) + { + ERROR_MSG("Failed to initialise EVP encrypt operation"); + + ByteString dummy; + SymmetricAlgorithm::encryptFinal(dummy); + + return false; + } + + // Allocate the EVP context + pCurCTX = EVP_CIPHER_CTX_new(); + + if (pCurCTX == NULL) + { + ERROR_MSG("Failed to allocate space for EVP_CIPHER_CTX"); + + ByteString dummy; + SymmetricAlgorithm::encryptFinal(dummy); + + return false; + } + + int rv; + if (mode == SymMode::GCM) + { + rv = EVP_EncryptInit_ex(pCurCTX, cipher, NULL, NULL, NULL); + + if (rv) + { + EVP_CIPHER_CTX_ctrl(pCurCTX, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL); + rv = EVP_EncryptInit_ex(pCurCTX, NULL, NULL, (unsigned char*) currentKey->getKeyBits().const_byte_str(), iv.byte_str()); + } + } + else + { + rv = EVP_EncryptInit(pCurCTX, cipher, (unsigned char*) currentKey->getKeyBits().const_byte_str(), iv.byte_str()); + } + + if (!rv) + { + ERROR_MSG("Failed to initialise EVP encrypt operation: %s", ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + ByteString dummy; + SymmetricAlgorithm::encryptFinal(dummy); + + return false; + } + + EVP_CIPHER_CTX_set_padding(pCurCTX, padding ? 1 : 0); + + if (mode == SymMode::GCM) + { + int outLen = 0; + if (aad.size() && !EVP_EncryptUpdate(pCurCTX, NULL, &outLen, (unsigned char*) aad.const_byte_str(), aad.size())) + { + ERROR_MSG("Failed to update with AAD: %s", ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + ByteString dummy; + SymmetricAlgorithm::encryptFinal(dummy); + + return false; + } + } + + return true; +} + +bool OSSLEVPSymmetricAlgorithm::encryptUpdate(const ByteString& data, ByteString& encryptedData) +{ + if (!SymmetricAlgorithm::encryptUpdate(data, encryptedData)) + { + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return false; + } + + if (data.size() == 0) + { + encryptedData.resize(0); + + return true; + } + + // Count number of bytes written + if (!BN_is_negative(maximumBytes)) + { + BN_add_word(counterBytes, data.size()); + } + + // Prepare the output block + encryptedData.resize(data.size() + getBlockSize() - 1); + + int outLen = encryptedData.size(); + if (!EVP_EncryptUpdate(pCurCTX, &encryptedData[0], &outLen, (unsigned char*) data.const_byte_str(), data.size())) + { + ERROR_MSG("EVP_EncryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + ByteString dummy; + SymmetricAlgorithm::encryptFinal(dummy); + + return false; + } + + // Resize the output block + encryptedData.resize(outLen); + currentBufferSize -= outLen; + + return true; +} + +bool OSSLEVPSymmetricAlgorithm::encryptFinal(ByteString& encryptedData) +{ + SymMode::Type mode = currentCipherMode; + size_t tagBytes = currentTagBytes; + + if (!SymmetricAlgorithm::encryptFinal(encryptedData)) + { + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return false; + } + + // Prepare the output block + encryptedData.resize(getBlockSize()); + + int outLen = encryptedData.size(); + + if (!EVP_EncryptFinal(pCurCTX, &encryptedData[0], &outLen)) + { + ERROR_MSG("EVP_EncryptFinal failed: %s", ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return false; + } + + // Resize the output block + encryptedData.resize(outLen); + + if (mode == SymMode::GCM) + { + ByteString tag; + tag.resize(tagBytes); + EVP_CIPHER_CTX_ctrl(pCurCTX, EVP_CTRL_GCM_GET_TAG, tagBytes, &tag[0]); + encryptedData += tag; + } + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return true; +} + +// Decryption functions +bool OSSLEVPSymmetricAlgorithm::decryptInit(const SymmetricKey* key, const SymMode::Type mode /* = SymMode::CBC */, const ByteString& IV /* = ByteString() */, bool padding /* = true */, size_t counterBits /* = 0 */, const ByteString& aad /* = ByteString() */, size_t tagBytes /* = 0 */) +{ + // Call the superclass initialiser + if (!SymmetricAlgorithm::decryptInit(key, mode, IV, padding, counterBits, aad, tagBytes)) + { + return false; + } + + // Check the IV + if (mode != SymMode::GCM && (IV.size() > 0) && (IV.size() != getBlockSize())) + { + ERROR_MSG("Invalid IV size (%d bytes, expected %d bytes)", IV.size(), getBlockSize()); + + ByteString dummy; + SymmetricAlgorithm::decryptFinal(dummy); + + return false; + } + + ByteString iv; + + if (IV.size() > 0) + { + iv = IV; + } + else + { + iv.wipe(getBlockSize()); + } + + // Check the counter bits + if (counterBits > 0) + { + BIGNUM* counter = OSSL::byteString2bn(iv); + BN_mask_bits(counter, counterBits); + + // Reverse the bits + while (counterBits > 0) + { + counterBits--; + if (BN_is_bit_set(counter, counterBits)) + { + BN_clear_bit(counter, counterBits); + } + else + { + BN_set_bit(counter, counterBits); + } + } + + // Set the maximum bytes + BN_add_word(counter, 1); + BN_mul_word(counter, getBlockSize()); + BN_copy(maximumBytes, counter); + BN_free(counter); + BN_zero(counterBytes); + } + else + { + BN_one(maximumBytes); + BN_set_negative(maximumBytes, 1); + } + + // Determine the cipher class + const EVP_CIPHER* cipher = getCipher(); + + if (cipher == NULL) + { + ERROR_MSG("Failed to initialise EVP decrypt operation"); + + ByteString dummy; + SymmetricAlgorithm::decryptFinal(dummy); + + return false; + } + + // Allocate the EVP context + pCurCTX = EVP_CIPHER_CTX_new(); + + if (pCurCTX == NULL) + { + ERROR_MSG("Failed to allocate space for EVP_CIPHER_CTX"); + + ByteString dummy; + SymmetricAlgorithm::decryptFinal(dummy); + + return false; + } + + int rv; + if (mode == SymMode::GCM) + { + rv = EVP_DecryptInit_ex(pCurCTX, cipher, NULL, NULL, NULL); + + if (rv) + { + EVP_CIPHER_CTX_ctrl(pCurCTX, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL); + rv = EVP_DecryptInit_ex(pCurCTX, NULL, NULL, (unsigned char*) currentKey->getKeyBits().const_byte_str(), iv.byte_str()); + } + } + else + { + rv = EVP_DecryptInit(pCurCTX, cipher, (unsigned char*) currentKey->getKeyBits().const_byte_str(), iv.byte_str()); + } + + if (!rv) + { + ERROR_MSG("Failed to initialise EVP decrypt operation: %s", ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + ByteString dummy; + SymmetricAlgorithm::decryptFinal(dummy); + + return false; + } + + EVP_CIPHER_CTX_set_padding(pCurCTX, padding ? 1 : 0); + + if (mode == SymMode::GCM) + { + int outLen = 0; + if (aad.size() && !EVP_DecryptUpdate(pCurCTX, NULL, &outLen, (unsigned char*) aad.const_byte_str(), aad.size())) + { + ERROR_MSG("Failed to update with AAD: %s", ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + ByteString dummy; + SymmetricAlgorithm::decryptFinal(dummy); + + return false; + } + } + + return true; +} + +bool OSSLEVPSymmetricAlgorithm::decryptUpdate(const ByteString& encryptedData, ByteString& data) +{ + if (!SymmetricAlgorithm::decryptUpdate(encryptedData, data)) + { + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return false; + } + + // AEAD ciphers should not return decrypted data until final is called + if (currentCipherMode == SymMode::GCM) + { + data.resize(0); + return true; + } + + // Count number of bytes written + if (!BN_is_negative(maximumBytes)) + { + BN_add_word(counterBytes, encryptedData.size()); + } + + // Prepare the output block + data.resize(encryptedData.size() + getBlockSize()); + + int outLen = data.size(); + + DEBUG_MSG("Decrypting %d bytes into buffer of %d bytes", encryptedData.size(), data.size()); + + if (!EVP_DecryptUpdate(pCurCTX, &data[0], &outLen, (unsigned char*) encryptedData.const_byte_str(), encryptedData.size())) + { + ERROR_MSG("EVP_DecryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + ByteString dummy; + SymmetricAlgorithm::decryptFinal(dummy); + + return false; + } + + DEBUG_MSG("Decrypt returned %d bytes of data", outLen); + + // Resize the output block + data.resize(outLen); + currentBufferSize -= outLen; + + return true; +} + +bool OSSLEVPSymmetricAlgorithm::decryptFinal(ByteString& data) +{ + SymMode::Type mode = currentCipherMode; + size_t tagBytes = currentTagBytes; + ByteString aeadBuffer = currentAEADBuffer; + + if (!SymmetricAlgorithm::decryptFinal(data)) + { + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return false; + } + + data.resize(0); + if (mode == SymMode::GCM) + { + // Check buffer size + if (aeadBuffer.size() < tagBytes) + { + ERROR_MSG("Tag bytes (%d) does not fit in AEAD buffer (%d)", tagBytes, aeadBuffer.size()); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return false; + } + + // Set the tag + EVP_CIPHER_CTX_ctrl(pCurCTX, EVP_CTRL_GCM_SET_TAG, tagBytes, &aeadBuffer[aeadBuffer.size()-tagBytes]); + + // Prepare the output block + data.resize(aeadBuffer.size() - tagBytes + getBlockSize()); + int outLen = data.size(); + + if (!EVP_DecryptUpdate(pCurCTX, &data[0], &outLen, (unsigned char*) aeadBuffer.const_byte_str(), aeadBuffer.size() - tagBytes)) + { + ERROR_MSG("EVP_DecryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return false; + } + + data.resize(outLen); + } + + // Prepare the output block + int initialSize = data.size(); + data.resize(initialSize + getBlockSize()); + + int outLen = data.size() - initialSize; + int rv; + + if (!(rv = EVP_DecryptFinal(pCurCTX, &data[initialSize], &outLen))) + { + ERROR_MSG("EVP_DecryptFinal failed (0x%08X): %s", rv, ERR_error_string(ERR_get_error(), NULL)); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return false; + } + + // Resize the output block + data.resize(initialSize + outLen); + + EVP_CIPHER_CTX_free(pCurCTX); + pCurCTX = NULL; + + return true; +} + +// Check if more bytes of data can be encrypted +bool OSSLEVPSymmetricAlgorithm::checkMaximumBytes(unsigned long bytes) +{ + if (BN_is_negative(maximumBytes)) return true; + + BIGNUM* bigNum = BN_new(); + BN_copy(bigNum, counterBytes); + BN_add_word(bigNum, bytes); + + bool rv = false; + if (BN_cmp(maximumBytes, bigNum) >= 0) rv = true; + + BN_free(bigNum); + + return rv; +} |