/* * Copyright 2002-2005 The Apache Software Foundation. * * Licensed 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: OpenSSLCryptoSymmetricKey.cpp 395833 2006-04-21 10:21:57Z blautenb $ * */ #include #include #include #include #include #include XERCES_CPP_NAMESPACE_USE; #if defined (HAVE_OPENSSL) #include #include // -------------------------------------------------------------------------------- // Constructors and Destructors // -------------------------------------------------------------------------------- OpenSSLCryptoSymmetricKey::OpenSSLCryptoSymmetricKey(XSECCryptoSymmetricKey::SymmetricKeyType type) : m_keyType(type), m_keyMode(MODE_CBC), m_keyBuf(""), m_keyLen(0), m_initialised(false) { EVP_CIPHER_CTX_init(&m_ctx); m_keyBuf.isSensitive(); } OpenSSLCryptoSymmetricKey::~OpenSSLCryptoSymmetricKey() { // Clean up the context EVP_CIPHER_CTX_cleanup(&m_ctx); } // -------------------------------------------------------------------------------- // Basic Key interface methods // -------------------------------------------------------------------------------- XSECCryptoSymmetricKey::SymmetricKeyType OpenSSLCryptoSymmetricKey::getSymmetricKeyType() { return m_keyType; } const XMLCh * OpenSSLCryptoSymmetricKey::getProviderName() { return DSIGConstants::s_unicodeStrPROVOpenSSL; } XSECCryptoKey * OpenSSLCryptoSymmetricKey::clone() { 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) { // Returns amount of IV data used (in bytes) // Sets m_initialised iff the key is OK and the IV is OK. if (m_initialised) return 0; if (m_keyLen == 0) { throw XSECCryptoException(XSECCryptoException::SymmetricError, "OpenSSL:SymmetricKey - Cannot initialise without key"); } // Set up the context according to the required cipher type switch (m_keyType) { case (XSECCryptoSymmetricKey::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(&m_ctx, EVP_des_ede3_cbc(),m_keyBuf.rawBuffer(), iv); #else EVP_DecryptInit(&m_ctx, EVP_des_ede3_cbc(),(unsigned char *) m_keyBuf.rawBuffer(), (unsigned char *) iv); #endif m_ivSize = 8; } else { #if defined(XSEC_OPENSSL_CONST_BUFFERS) EVP_DecryptInit(&m_ctx, EVP_des_ede3(), m_keyBuf.rawBuffer(), NULL); #else EVP_DecryptInit(&m_ctx, EVP_des_ede3(), (unsigned char *) m_keyBuf.rawBuffer(), NULL); #endif m_ivSize = 0; } 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) { return 0; // Cannot initialise without an IV } EVP_DecryptInit_ex(&m_ctx, EVP_aes_128_cbc(), NULL, m_keyBuf.rawBuffer(), iv); } else { EVP_DecryptInit_ex(&m_ctx, EVP_aes_128_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); } m_blockSize = 16; break; case (XSECCryptoSymmetricKey::KEY_AES_192) : // An AES key if (m_keyMode == MODE_CBC) { if (iv == NULL) { return 0; // Cannot initialise without an IV } EVP_DecryptInit_ex(&m_ctx, EVP_aes_192_cbc(), NULL, m_keyBuf.rawBuffer(), iv); } else { EVP_DecryptInit_ex(&m_ctx, EVP_aes_192_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); } m_blockSize = 16; break; case (XSECCryptoSymmetricKey::KEY_AES_256) : // An AES key if (m_keyMode == MODE_CBC) { if (iv == NULL) { return 0; // Cannot initialise without an IV } EVP_DecryptInit_ex(&m_ctx, EVP_aes_256_cbc(), NULL, m_keyBuf.rawBuffer(), iv); } else { EVP_DecryptInit_ex(&m_ctx, EVP_aes_256_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); } 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 : // Cannot do this without an IV throw XSECCryptoException(XSECCryptoException::SymmetricError, "OpenSSL:SymmetricKey - Unknown key type"); } // Setup ivSize m_ivSize = (m_keyMode == MODE_CBC ? m_blockSize : 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(&m_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) { m_doPad = doPad; m_keyMode = mode; m_initialised = false; decryptCtxInit(iv); 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 unsigned int offset = 0; if (!m_initialised) { offset = decryptCtxInit(inBuf); 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(&m_ctx, &plainBuf[m_bytesInLastBlock], &outl, &inBuf[offset], inLength - offset) == 0) { #else if (EVP_DecryptUpdate(&m_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(&m_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 recieved 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 j_scrPlainBuf(scrPlainBuf); cipherBuf = new unsigned char[m_blockSize]; ArrayJanitor 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(&m_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"); } // 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(&m_ctx, EVP_des_ede3_cbc(), m_keyBuf.rawBuffer(), usedIV); #else EVP_EncryptInit(&m_ctx, EVP_des_ede3_cbc(), (unsigned char *) m_keyBuf.rawBuffer(), (unsigned char *) usedIV); #endif } else { #if defined (XSEC_OPENSSL_CONST_BUFFERS) EVP_EncryptInit(&m_ctx, EVP_des_ede3_ecb(), m_keyBuf.rawBuffer(), NULL); #else EVP_EncryptInit(&m_ctx, EVP_des_ede3(), (unsigned char *) m_keyBuf.rawBuffer(), NULL); #endif } 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(&m_ctx, EVP_aes_128_cbc(), NULL, m_keyBuf.rawBuffer(), usedIV); } else { EVP_EncryptInit_ex(&m_ctx, EVP_aes_128_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); } 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(&m_ctx, EVP_aes_192_cbc(), NULL, m_keyBuf.rawBuffer(), usedIV); } else { EVP_EncryptInit_ex(&m_ctx, EVP_aes_192_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); } 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(&m_ctx, EVP_aes_256_cbc(), NULL, m_keyBuf.rawBuffer(), usedIV); } else { EVP_EncryptInit_ex(&m_ctx, EVP_aes_256_ecb(), NULL, m_keyBuf.rawBuffer(), NULL); } 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 m_ivSize = 0; #if defined (XSEC_OPENSSL_CANSET_PADDING) // Setup padding if (m_doPad) { EVP_CIPHER_CTX_set_padding(&m_ctx, 1); } else { EVP_CIPHER_CTX_set_padding(&m_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(&m_ctx, &cipherBuf[offset], &outl, inBuf, inLength) == 0) { #else if (EVP_EncryptUpdate(&m_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) { int outl = maxOutLength; m_initialised = false; if (EVP_EncryptFinal(&m_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 return outl; } #endif /* HAVE_OPENSSL */