| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /* |
| * XSEC |
| * |
| * XSECCryptoSymmetricKey := Bulk encryption algorithms should all be |
| * implemented via this interface |
| * |
| * Author(s): Berin Lautenbach |
| * |
| * $Id$ |
| * |
| */ |
| |
| #include <xsec/framework/XSECDefs.hpp> |
| #include <iostream> |
| #include <xsec/enc/OpenSSL/OpenSSLCryptoSymmetricKey.hpp> |
| #include <xsec/framework/XSECError.hpp> |
| #include <xsec/enc/XSECCryptoException.hpp> |
| |
| #include <xercesc/util/Janitor.hpp> |
| XERCES_CPP_NAMESPACE_USE; |
| |
| #if defined (XSEC_HAVE_OPENSSL) |
| |
| #include <string.h> |
| |
| #include <openssl/rand.h> |
| |
| // -------------------------------------------------------------------------------- |
| // Constructors and Destructors |
| // -------------------------------------------------------------------------------- |
| |
| OpenSSLCryptoSymmetricKey::OpenSSLCryptoSymmetricKey(XSECCryptoSymmetricKey::SymmetricKeyType type) : |
| m_keyType(type), |
| m_keyMode(MODE_NONE), |
| m_keyBuf(""), |
| m_tagBuf(""), |
| m_keyLen(0), |
| #if (OPENSSL_VERSION_NUMBER < 0x10100000L) |
| mp_ctx(&m_ctx_space), |
| #else |
| mp_ctx(EVP_CIPHER_CTX_new()), |
| #endif |
| m_initialised(false), |
| m_blockSize(0), |
| m_ivSize(0), |
| m_bytesInLastBlock(0), |
| m_ivSent(false), |
| m_doPad(false) { |
| if (!mp_ctx) |
| throw XSECCryptoException(XSECCryptoException::ECError, "OpenSSL::CryptoSymmetricKey - cannot allocate contexts"); |
| |
| EVP_CIPHER_CTX_init(mp_ctx); |
| m_keyBuf.isSensitive(); |
| |
| } |
| |
| OpenSSLCryptoSymmetricKey::~OpenSSLCryptoSymmetricKey() { |
| |
| // Clean up the context |
| |
| EVP_CIPHER_CTX_cleanup(mp_ctx); |
| #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) |
| EVP_CIPHER_CTX_free(mp_ctx); |
| #endif |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Basic Key interface methods |
| // -------------------------------------------------------------------------------- |
| |
| XSECCryptoSymmetricKey::SymmetricKeyType OpenSSLCryptoSymmetricKey::getSymmetricKeyType() const { |
| |
| return m_keyType; |
| |
| } |
| |
| const XMLCh * OpenSSLCryptoSymmetricKey::getProviderName() const { |
| |
| return DSIGConstants::s_unicodeStrPROVOpenSSL; |
| |
| } |
| |
| XSECCryptoKey * OpenSSLCryptoSymmetricKey::clone() const { |
| |
| OpenSSLCryptoSymmetricKey * ret; |
| |
| XSECnew(ret, OpenSSLCryptoSymmetricKey(m_keyType)); |
| ret->m_keyMode = m_keyMode; |
| ret->m_keyLen = m_keyLen; |
| ret->m_keyBuf = m_keyBuf; |
| |
| return ret; |
| |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Store the key value |
| // -------------------------------------------------------------------------------- |
| |
| void OpenSSLCryptoSymmetricKey::setKey(const unsigned char * key, unsigned int keyLen) { |
| |
| m_keyBuf.sbMemcpyIn(key, keyLen); |
| m_keyLen = keyLen; |
| |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Decrypt |
| // -------------------------------------------------------------------------------- |
| |
| int OpenSSLCryptoSymmetricKey::decryptCtxInit(const unsigned char* iv, const unsigned char* tag, unsigned taglen) { |
| |
| // Returns amount of IV data used (in bytes) |
| // Sets m_initialised iff the key is OK and the IV is OK. |
| |
| // GCM modes will leave this unset until the second call with the iv |
| |
| if (m_initialised) |
| return 0; |
| |
| if (m_keyLen == 0) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Cannot initialise without key"); |
| } |
| else if (m_keyMode == MODE_NONE) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Cannot initialise without mode"); |
| } |
| |
| // Set up the context according to the required cipher type |
| |
| switch (m_keyType) { |
| |
| case (KEY_3DES_192) : |
| |
| // A 3DES key |
| |
| if (m_keyMode == MODE_CBC) { |
| |
| if (iv == NULL) { |
| return 0; // Cannot initialise without an IV |
| } |
| |
| /* Do not use "_ex" calls yet - as want backwards compatibility |
| with 0.9.6 */ |
| |
| #if defined(XSEC_OPENSSL_CONST_BUFFERS) |
| EVP_DecryptInit(mp_ctx, EVP_des_ede3_cbc(),m_keyBuf.rawBuffer(), iv); |
| #else |
| EVP_DecryptInit(mp_ctx, EVP_des_ede3_cbc(),(unsigned char *) m_keyBuf.rawBuffer(), (unsigned char *) iv); |
| #endif |
| m_ivSize = 8; |
| } |
| else if (m_keyMode == MODE_ECB) { |
| #if defined(XSEC_OPENSSL_CONST_BUFFERS) |
| EVP_DecryptInit(mp_ctx, EVP_des_ecb(), m_keyBuf.rawBuffer(), NULL); |
| #else |
| EVP_DecryptInit(mp_ctx, EVP_des_ecb(), (unsigned char *) m_keyBuf.rawBuffer(), NULL); |
| #endif |
| m_ivSize = 0; |
| } |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unsupported DES3 cipher mode"); |
| } |
| |
| |
| m_blockSize = 8; |
| break; |
| |
| #if defined (XSEC_OPENSSL_HAVE_AES) |
| |
| case (KEY_AES_128) : |
| |
| // An AES key |
| |
| if (m_keyMode == MODE_CBC) { |
| |
| if (iv == NULL) { |
| return 0; // Cannot initialise without an IV |
| } |
| |
| EVP_DecryptInit_ex(mp_ctx, EVP_aes_128_cbc(), NULL, m_keyBuf.rawBuffer(), iv); |
| |
| } |
| #if defined (XSEC_OPENSSL_HAVE_GCM) |
| else if (m_keyMode == MODE_GCM) { |
| |
| if (tag != NULL && taglen != 16) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Invalid authentication tag"); |
| } |
| |
| if (iv == NULL) { |
| // Just save off tag for later. |
| m_tagBuf.sbMemcpyIn(tag, taglen); |
| return 0; |
| } |
| |
| if (m_tagBuf.sbRawBufferSize() == 0) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Authentication tag not set"); |
| } |
| |
| // We have everything, so we can fully init. |
| EVP_CipherInit_ex(mp_ctx, EVP_aes_128_gcm(), NULL, NULL, NULL, 0); |
| EVP_CIPHER_CTX_ctrl(mp_ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL); |
| EVP_CIPHER_CTX_ctrl(mp_ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)m_tagBuf.rawBuffer()); |
| EVP_CipherInit_ex(mp_ctx, NULL, NULL, m_keyBuf.rawBuffer(), iv, 0); |
| } |
| #endif |
| else if (m_keyMode == MODE_ECB) { |
| |
| EVP_DecryptInit_ex(mp_ctx, EVP_aes_128_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); |
| |
| } |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unsupported AES cipher mode"); |
| } |
| |
| m_blockSize = 16; |
| break; |
| |
| case (KEY_AES_192) : |
| |
| // An AES key |
| |
| if (m_keyMode == MODE_CBC) { |
| |
| if (iv == NULL) { |
| return 0; // Cannot initialise without an IV |
| } |
| |
| EVP_DecryptInit_ex(mp_ctx, EVP_aes_192_cbc(), NULL, m_keyBuf.rawBuffer(), iv); |
| |
| } |
| #if defined (XSEC_OPENSSL_HAVE_GCM) |
| else if (m_keyMode == MODE_GCM) { |
| |
| if (tag != NULL && taglen != 16) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Invalid authentication tag"); |
| } |
| |
| if (iv == NULL) { |
| // Just save off tag for later. |
| m_tagBuf.sbMemcpyIn(tag, taglen); |
| return 0; |
| } |
| |
| if (m_tagBuf.sbRawBufferSize() == 0) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Authentication tag not set"); |
| } |
| |
| // We have everything, so we can fully init. |
| EVP_CipherInit_ex(mp_ctx, EVP_aes_192_gcm(), NULL, NULL, NULL, 0); |
| EVP_CIPHER_CTX_ctrl(mp_ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL); |
| EVP_CIPHER_CTX_ctrl(mp_ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)m_tagBuf.rawBuffer()); |
| EVP_CipherInit_ex(mp_ctx, NULL, NULL, m_keyBuf.rawBuffer(), iv, 0); |
| |
| } |
| #endif |
| else if (m_keyMode == MODE_ECB) { |
| |
| EVP_DecryptInit_ex(mp_ctx, EVP_aes_192_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); |
| |
| } |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unsupported AES cipher mode"); |
| } |
| |
| m_blockSize = 16; |
| break; |
| |
| case (KEY_AES_256) : |
| |
| // An AES key |
| |
| if (m_keyMode == MODE_CBC) { |
| |
| if (iv == NULL) { |
| return 0; // Cannot initialise without an IV |
| } |
| |
| EVP_DecryptInit_ex(mp_ctx, EVP_aes_256_cbc(), NULL, m_keyBuf.rawBuffer(), iv); |
| |
| } |
| #if defined (XSEC_OPENSSL_HAVE_GCM) |
| else if (m_keyMode == MODE_GCM) { |
| |
| if (tag != NULL && taglen != 16) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Invalid authentication tag"); |
| } |
| |
| if (iv == NULL) { |
| // Just save off tag for later. |
| m_tagBuf.sbMemcpyIn(tag, taglen); |
| return 0; |
| } |
| |
| if (m_tagBuf.sbRawBufferSize() == 0) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Authentication tag not set"); |
| } |
| |
| // We have everything, so we can fully init. |
| EVP_CipherInit_ex(mp_ctx, EVP_aes_256_gcm(), NULL, NULL, NULL, 0); |
| EVP_CIPHER_CTX_ctrl(mp_ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL); |
| EVP_CIPHER_CTX_ctrl(mp_ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)m_tagBuf.rawBuffer()); |
| EVP_CipherInit_ex(mp_ctx, NULL, NULL, m_keyBuf.rawBuffer(), iv, 0); |
| |
| } |
| #endif |
| else if (m_keyMode == MODE_ECB) { |
| |
| EVP_DecryptInit_ex(mp_ctx, EVP_aes_256_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); |
| |
| } |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unsupported AES cipher mode"); |
| } |
| |
| m_blockSize = 16; |
| break; |
| #else |
| |
| case (KEY_AES_128) : |
| case (KEY_AES_192) : |
| case (KEY_AES_256) : |
| |
| throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm, |
| "OpenSSL:SymmetricKey - AES not supported in this version of OpenSSL"); |
| |
| #endif /* XSEC_OPENSSL_HAVE_AES */ |
| |
| default : |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unknown key type"); |
| |
| } |
| |
| |
| // Setup ivSize |
| switch (m_keyMode) { |
| case MODE_CBC: |
| m_ivSize = m_blockSize; |
| break; |
| |
| case MODE_GCM: |
| m_ivSize = 12; |
| break; |
| |
| default: |
| m_ivSize = 0; |
| } |
| |
| // Reset some parameters |
| m_initialised = true; |
| m_bytesInLastBlock = 0; |
| |
| // Disable OpenSSL padding - The interop samples have broken PKCS padding - AARGHH |
| |
| #if defined (XSEC_OPENSSL_CANSET_PADDING) |
| EVP_CIPHER_CTX_set_padding(mp_ctx, 0); |
| #endif |
| |
| // Return number of bytes chewed up by IV |
| return m_ivSize; |
| } |
| |
| |
| bool OpenSSLCryptoSymmetricKey::decryptInit(bool doPad, |
| SymmetricKeyMode mode, |
| const unsigned char* iv, |
| const unsigned char* tag, |
| unsigned int taglen) { |
| |
| m_doPad = doPad; |
| m_keyMode = mode; |
| m_initialised = false; |
| decryptCtxInit(iv, tag, taglen); |
| return true; |
| |
| } |
| |
| unsigned int OpenSSLCryptoSymmetricKey::decrypt(const unsigned char * inBuf, |
| unsigned char * plainBuf, |
| unsigned int inLength, |
| unsigned int maxOutLength) { |
| |
| |
| |
| // NOTE: This won't actually stop OpenSSL blowing the buffer, so the onus is |
| // on the caller. |
| |
| #if 0 |
| |
| /* normally commented out code to determine endian problems */ |
| unsigned int i; |
| unsigned char e[2048]; |
| unsigned char * inBuf1 = (unsigned char *) inBuf; |
| if (inLength < 2048) { |
| memcpy(e, inBuf, inLength); |
| for (i = 0; i < inLength;++i) { |
| inBuf1[i] = e[inLength - 1 - i]; |
| } |
| } |
| |
| #endif |
| |
| // If cipher isn't initialized yet, we call back in with the ciphertext to supply the IV |
| // For GCM, the tag has to have been supplied already or this will fail. |
| |
| unsigned int offset = 0; |
| if (!m_initialised) { |
| offset = decryptCtxInit(inBuf, NULL, 0); |
| |
| if (offset > inLength) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Not enough data passed in to get IV"); |
| } |
| } |
| |
| int outl = maxOutLength; |
| |
| if (inLength - offset + m_bytesInLastBlock > maxOutLength) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::decrypt - Not enough space in output buffer"); |
| } |
| |
| #if defined (XSEC_OPENSSL_CONST_BUFFERS) |
| if (EVP_DecryptUpdate(mp_ctx, &plainBuf[m_bytesInLastBlock], &outl, &inBuf[offset], inLength - offset) == 0) { |
| #else |
| if (EVP_DecryptUpdate(mp_ctx, &plainBuf[m_bytesInLastBlock], &outl, (unsigned char *) &inBuf[offset], inLength - offset) == 0) { |
| #endif |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error during OpenSSL decrypt"); |
| |
| } |
| |
| // Store the last block if we are padding |
| if (m_doPad && m_blockSize > 0 && outl >= m_blockSize) { |
| |
| // Output will always be *at least* the blocksize |
| |
| // Copy the previous last block into the start of the output buffer |
| memcpy(plainBuf, m_lastBlock, m_bytesInLastBlock); |
| |
| // Copy the tail into the buffer |
| memcpy(m_lastBlock, &plainBuf[outl + m_bytesInLastBlock - m_blockSize], m_blockSize); |
| |
| outl = outl + m_bytesInLastBlock - m_blockSize; |
| m_bytesInLastBlock = m_blockSize; |
| |
| } |
| |
| return outl; |
| |
| } |
| |
| unsigned int OpenSSLCryptoSymmetricKey::decryptFinish(unsigned char * plainBuf, |
| unsigned int maxOutLength) { |
| |
| int outl = maxOutLength; |
| m_initialised = false; |
| |
| #if defined (XSEC_OPENSSL_CANSET_PADDING) |
| |
| if (EVP_DecryptFinal(mp_ctx, plainBuf, &outl) == 0) { |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error during OpenSSL decrypt finalisation"); |
| |
| } |
| |
| if (outl > 0) { |
| |
| // Should never see any bytes output, as we are not padding |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unexpectedly received bytes from EVP_DecryptFinal_ex"); |
| |
| } |
| |
| // Calculate any padding issues |
| if (m_doPad && m_bytesInLastBlock == m_blockSize) { |
| |
| outl = m_blockSize - m_lastBlock[m_blockSize - 1]; |
| |
| if (outl > m_blockSize || outl < 0) { |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey::decryptFinish - Out of range padding value in final block"); |
| |
| } |
| |
| memcpy(plainBuf, m_lastBlock, outl); |
| |
| } |
| |
| if ((unsigned int) outl > maxOutLength) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::decryptFinish - **WARNING** - Plaintext output > maxOutLength!"); |
| } |
| |
| return outl; |
| |
| #else |
| |
| /* Working with a version of OpenSSL that *always* performs padding |
| so we need to work around it */ |
| unsigned char *scrPlainBuf; |
| unsigned char *cipherBuf; |
| scrPlainBuf = new unsigned char[3 * m_blockSize]; |
| ArrayJanitor<unsigned char> j_scrPlainBuf(scrPlainBuf); |
| cipherBuf = new unsigned char[m_blockSize]; |
| ArrayJanitor<unsigned char> j_cipherBuf(cipherBuf); |
| |
| /* Zeroise the cipher buffer */ |
| memset(cipherBuf, 0, m_blockSize); |
| |
| unsigned int offset = 0; |
| |
| /* Get any previous bytes from the m_lastBlock */ |
| if (m_bytesInLastBlock > 0 && m_bytesInLastBlock <= m_blockSize) { |
| memcpy(scrPlainBuf, m_lastBlock, m_bytesInLastBlock); |
| offset = m_bytesInLastBlock; |
| } |
| |
| outl = m_blockSize; |
| |
| /* This is really ugly - but we have to trick OpenSSL into thinking there |
| is more, so that it sends us the lasts block with the padding in it. |
| We can then clean that up ourselves |
| */ |
| |
| if (EVP_DecryptUpdate(mp_ctx, &scrPlainBuf[offset], &outl, cipherBuf, m_blockSize) == 0) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error cecrypting final block during OpenSSL"); |
| } |
| |
| outl += offset; |
| |
| if (m_doPad && outl > 0) { |
| |
| /* Strip any padding */ |
| outl -= scrPlainBuf[outl - 1]; |
| |
| if (outl > (2 * m_blockSize) || outl < 0) { |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey::decryptFinish - Out of range padding value in final block"); |
| |
| } |
| |
| } |
| |
| if (outl > (int) maxOutLength) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::decryptFinish - **WARNING** - Plaintext output > maxOutLength!"); |
| } |
| |
| if (outl > 0) { |
| |
| memcpy(plainBuf, scrPlainBuf, outl); |
| |
| } |
| |
| return outl; |
| #endif |
| |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Encrypt |
| // -------------------------------------------------------------------------------- |
| |
| bool OpenSSLCryptoSymmetricKey::encryptInit(bool doPad, |
| SymmetricKeyMode mode, |
| const unsigned char * iv) { |
| |
| if (m_initialised == true) |
| return true; |
| |
| m_doPad = doPad; |
| m_keyMode = mode; |
| |
| if (m_keyLen == 0) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Cannot initialise without key"); |
| } |
| else if (m_keyMode == MODE_NONE) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Cannot initialise without mode"); |
| } |
| |
| // Do some parameter initialisation |
| m_initialised = true; |
| m_bytesInLastBlock = 0; |
| |
| // Set up the context according to the required cipher type |
| |
| const unsigned char * usedIV = NULL; |
| unsigned char genIV[256]; |
| |
| // Tell the library that the IV still has to be sent |
| |
| m_ivSent = false; |
| |
| switch (m_keyType) { |
| |
| case (XSECCryptoSymmetricKey::KEY_3DES_192) : |
| |
| // A 3DES key |
| |
| if (m_keyMode == MODE_CBC) { |
| |
| if (iv == NULL) { |
| |
| bool res = ((RAND_status() == 1) && (RAND_bytes(genIV, 8) == 1)); |
| if (res == false) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error generating random IV"); |
| } |
| |
| usedIV = genIV; |
| |
| } |
| else { |
| usedIV = iv; |
| } |
| |
| #if defined (XSEC_OPENSSL_CONST_BUFFERS) |
| EVP_EncryptInit(mp_ctx, EVP_des_ede3_cbc(), m_keyBuf.rawBuffer(), usedIV); |
| #else |
| EVP_EncryptInit(mp_ctx, EVP_des_ede3_cbc(), (unsigned char *) m_keyBuf.rawBuffer(), (unsigned char *) usedIV); |
| #endif |
| } |
| else if (m_keyMode == MODE_ECB) { |
| #if defined (XSEC_OPENSSL_CONST_BUFFERS) |
| EVP_EncryptInit(mp_ctx, EVP_des_ede3_ecb(), m_keyBuf.rawBuffer(), NULL); |
| #else |
| EVP_EncryptInit(mp_ctx, EVP_des_ede3(), (unsigned char *) m_keyBuf.rawBuffer(), NULL); |
| #endif |
| } |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unsupported DES3 cipher mode"); |
| } |
| |
| |
| m_blockSize = 8; |
| break; |
| |
| #if defined (XSEC_OPENSSL_HAVE_AES) |
| |
| case (XSECCryptoSymmetricKey::KEY_AES_128) : |
| |
| // An AES key |
| |
| if (m_keyMode == MODE_CBC) { |
| |
| if (iv == NULL) { |
| |
| bool res = ((RAND_status() == 1) && (RAND_bytes(genIV, 16) == 1)); |
| if (res == false) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error generating random IV"); |
| } |
| |
| usedIV = genIV; |
| |
| } |
| else |
| usedIV = iv; |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_128_cbc(), NULL, m_keyBuf.rawBuffer(), usedIV); |
| } |
| else if (m_keyMode == MODE_ECB) { |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_128_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); |
| |
| } |
| #ifdef XSEC_OPENSSL_HAVE_GCM |
| else if (m_keyMode == MODE_GCM) { |
| |
| if (iv == NULL) { |
| |
| bool res = ((RAND_status() == 1) && (RAND_bytes(genIV, 12) == 1)); |
| if (res == false) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error generating random IV"); |
| } |
| |
| usedIV = genIV; |
| |
| } |
| else |
| usedIV = iv; |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_128_gcm(), NULL, m_keyBuf.rawBuffer(), usedIV); |
| } |
| #endif |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unsupported AES cipher mode"); |
| } |
| |
| m_blockSize = 16; |
| break; |
| |
| case (XSECCryptoSymmetricKey::KEY_AES_192) : |
| |
| // An AES key |
| |
| if (m_keyMode == MODE_CBC) { |
| |
| if (iv == NULL) { |
| |
| bool res = ((RAND_status() == 1) && (RAND_bytes(genIV, 16) == 1)); |
| if (res == false) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error generating random IV"); |
| } |
| |
| usedIV = genIV; |
| |
| } |
| else |
| usedIV = iv; |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_192_cbc(), NULL, m_keyBuf.rawBuffer(), usedIV); |
| |
| } |
| #ifdef XSEC_OPENSSL_HAVE_GCM |
| else if (m_keyMode == MODE_GCM) { |
| |
| if (iv == NULL) { |
| |
| bool res = ((RAND_status() == 1) && (RAND_bytes(genIV, 12) == 1)); |
| if (res == false) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error generating random IV"); |
| } |
| |
| usedIV = genIV; |
| |
| } |
| else |
| usedIV = iv; |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_192_gcm(), NULL, m_keyBuf.rawBuffer(), usedIV); |
| } |
| #endif |
| else if (m_keyMode == MODE_ECB) { |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_192_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); |
| } |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unsupported AES cipher mode"); |
| } |
| |
| m_blockSize = 16; |
| break; |
| |
| case (XSECCryptoSymmetricKey::KEY_AES_256) : |
| |
| // An AES key |
| |
| if (m_keyMode == MODE_CBC) { |
| if (iv == NULL) { |
| |
| bool res = ((RAND_status() == 1) && (RAND_bytes(genIV, 16) == 1)); |
| if (res == false) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error generating random IV"); |
| } |
| |
| usedIV = genIV; |
| |
| } |
| else |
| usedIV = iv; |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_256_cbc(), NULL, m_keyBuf.rawBuffer(), usedIV); |
| |
| } |
| #ifdef XSEC_OPENSSL_HAVE_GCM |
| else if (m_keyMode == MODE_GCM) { |
| |
| if (iv == NULL) { |
| |
| bool res = ((RAND_status() == 1) && (RAND_bytes(genIV, 12) == 1)); |
| if (res == false) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error generating random IV"); |
| } |
| |
| usedIV = genIV; |
| |
| } |
| else |
| usedIV = iv; |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_256_gcm(), NULL, m_keyBuf.rawBuffer(), usedIV); |
| } |
| #endif |
| else if (m_keyMode == MODE_ECB) { |
| |
| EVP_EncryptInit_ex(mp_ctx, EVP_aes_256_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); |
| |
| } |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unsupported AES cipher mode"); |
| } |
| |
| m_blockSize = 16; |
| break; |
| |
| #else |
| |
| case (XSECCryptoSymmetricKey::KEY_AES_128) : |
| case (XSECCryptoSymmetricKey::KEY_AES_192) : |
| case (XSECCryptoSymmetricKey::KEY_AES_256) : |
| |
| throw XSECCryptoException(XSECCryptoException::UnsupportedAlgorithm, |
| "OpenSSL:SymmetricKey - AES not supported in this version of OpenSSL"); |
| |
| #endif /* XSEC_OPENSSL_HAVE_AES */ |
| |
| default : |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Unknown key type"); |
| |
| } |
| |
| // Clear up any read padding |
| |
| if (m_keyMode == MODE_CBC) { |
| m_ivSize = m_blockSize; |
| memcpy(m_lastBlock, usedIV, m_ivSize); |
| } |
| else if (m_keyMode == MODE_GCM) { |
| m_ivSize = 12; |
| memcpy(m_lastBlock, usedIV, m_ivSize); |
| } |
| else { |
| m_ivSize = 0; |
| } |
| |
| #if defined (XSEC_OPENSSL_CANSET_PADDING) |
| // Setup padding |
| if (m_doPad) { |
| EVP_CIPHER_CTX_set_padding(mp_ctx, 1); |
| } |
| else { |
| EVP_CIPHER_CTX_set_padding(mp_ctx, 0); |
| } |
| #endif |
| |
| return true; |
| |
| } |
| |
| unsigned int OpenSSLCryptoSymmetricKey::encrypt(const unsigned char * inBuf, |
| unsigned char * cipherBuf, |
| unsigned int inLength, |
| unsigned int maxOutLength) { |
| |
| if (m_initialised == false) { |
| |
| encryptInit(); |
| |
| } |
| |
| // NOTE: This won't actually stop OpenSSL blowing the buffer, so the onus is |
| // on the caller. |
| |
| unsigned int offset = 0; |
| if (m_ivSent == false && m_ivSize > 0) { |
| |
| memcpy(cipherBuf, m_lastBlock, m_ivSize); |
| m_ivSent = true; |
| |
| offset = m_ivSize; |
| |
| } |
| |
| int outl = maxOutLength - offset; |
| |
| if (inLength + offset > maxOutLength) { |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Not enough space in output buffer for encrypt"); |
| |
| } |
| #if defined (XSEC_OPENSSL_CONST_BUFFERS) |
| if (EVP_EncryptUpdate(mp_ctx, &cipherBuf[offset], &outl, inBuf, inLength) == 0) { |
| #else |
| if (EVP_EncryptUpdate(mp_ctx, &cipherBuf[offset], &outl, (unsigned char *) inBuf, inLength) == 0) { |
| #endif |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSL:SymmetricKey - Error during OpenSSL encrypt"); |
| |
| } |
| |
| return outl + offset; |
| |
| } |
| |
| unsigned int OpenSSLCryptoSymmetricKey::encryptFinish(unsigned char * cipherBuf, |
| unsigned int maxOutLength, |
| unsigned int taglen) { |
| |
| int outl = maxOutLength; |
| m_initialised = false; |
| |
| if (EVP_EncryptFinal(mp_ctx, cipherBuf, &outl) == 0) { |
| |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::encryptFinish - Error during OpenSSL decrypt finalisation"); |
| |
| } |
| |
| if ((unsigned int) outl > maxOutLength) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::encryptFinish - **WARNING** - Cipheroutput > maxOutLength!"); |
| } |
| |
| #if !defined (XSEC_OPENSSL_CANSET_PADDING) |
| if (!m_doPad) { |
| |
| if (outl < m_blockSize) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::encryptFinish - cannot remove padding!"); |
| } |
| |
| outl -= m_blockSize; |
| |
| } |
| #endif |
| |
| if (taglen > 0) { |
| // Extract authentication tag and add to ciphertext. |
| if (maxOutLength - outl < taglen) { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::encryptFinish - **WARNING** - no room in ciphertext buffer for authentication tag"); |
| } |
| if (m_keyMode == MODE_GCM) { |
| #ifdef XSEC_OPENSSL_HAVE_GCM |
| EVP_CIPHER_CTX_ctrl(mp_ctx, EVP_CTRL_GCM_GET_TAG, taglen, cipherBuf + outl); |
| outl += taglen; |
| #else |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::encryptFinish - AES-GCM not supported in this version of OpenSSL"); |
| #endif |
| } |
| else { |
| throw XSECCryptoException(XSECCryptoException::SymmetricError, |
| "OpenSSLSymmetricKey::encryptFinish - cipher mode does not support authentication tag"); |
| } |
| } |
| |
| return outl; |
| |
| } |
| |
| #endif /* XSEC_HAVE_OPENSSL */ |