| /** |
| * 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 |
| * |
| * XSECAlgorithmHandlerDefault := Interface class to define handling of |
| * default encryption algorithms |
| * |
| * $Id$ |
| * |
| */ |
| |
| // XSEC Includes |
| |
| |
| #include <xsec/dsig/DSIGAlgorithmHandlerDefault.hpp> |
| #include <xsec/enc/XSECCryptoKey.hpp> |
| #include <xsec/framework/XSECDefs.hpp> |
| #include <xsec/framework/XSECError.hpp> |
| #include <xsec/transformers/TXFMChain.hpp> |
| #include <xsec/transformers/TXFMBase64.hpp> |
| #include <xsec/transformers/TXFMHash.hpp> |
| |
| #include "../utils/XSECAlgorithmSupport.hpp" |
| #include "../utils/XSECDOMUtils.hpp" |
| |
| #include <xercesc/dom/DOM.hpp> |
| #include <xercesc/util/Janitor.hpp> |
| |
| XERCES_CPP_NAMESPACE_USE |
| |
| #define MAXB64BUFSIZE 2048 |
| |
| // -------------------------------------------------------------------------------- |
| // Some useful utility functions |
| // -------------------------------------------------------------------------------- |
| |
| |
| bool compareBase64StringToRaw(const char* b64Str, |
| unsigned char* raw, |
| unsigned int rawLen, |
| unsigned int maxCompare = 0) { |
| // Decode a base64 buffer and then compare the result to a raw buffer |
| // Compare at most maxCompare bits (if maxCompare > 0) |
| // Note - whilst the other parameters are bytes, maxCompare is bits |
| |
| // The div function below takes signed int, so make sure the value |
| // is safe to cast. |
| if ((int) maxCompare < 0) { |
| |
| throw XSECException(XSECException::CryptoProviderError, |
| "Comparison length was unsafe"); |
| |
| } |
| |
| unsigned char outputStr[MAXB64BUFSIZE]; |
| unsigned int outputLen = 0; |
| |
| XSECCryptoBase64 * b64 = XSECPlatformUtils::g_cryptoProvider->base64(); |
| |
| if (!b64) { |
| |
| throw XSECException(XSECException::CryptoProviderError, |
| "Error requesting Base64 object from Crypto Provider"); |
| |
| } |
| |
| Janitor<XSECCryptoBase64> j_b64(b64); |
| |
| b64->decodeInit(); |
| outputLen = b64->decode((unsigned char *) b64Str, (unsigned int) strlen((char *) b64Str), outputStr, MAXB64BUFSIZE); |
| outputLen += b64->decodeFinish(&outputStr[outputLen], MAXB64BUFSIZE - outputLen); |
| |
| // Compare |
| |
| div_t d; |
| d.rem = 0; |
| d.quot = 0; |
| |
| unsigned int maxCompareBytes; |
| |
| unsigned int size; |
| |
| if (maxCompare > 0) { |
| d = div(maxCompare, 8); |
| maxCompareBytes = d.quot; |
| if (d.rem != 0) |
| maxCompareBytes++; |
| |
| if (rawLen < maxCompareBytes && outputLen < maxCompareBytes) { |
| if (rawLen != outputLen) |
| return false; |
| size = rawLen; |
| } |
| else if (rawLen < maxCompareBytes || outputLen < maxCompareBytes) { |
| return false; |
| } |
| else |
| size = maxCompareBytes; |
| } |
| else { |
| |
| if (rawLen != outputLen) |
| return false; |
| |
| size = rawLen; |
| |
| } |
| |
| // Compare bytes |
| unsigned int i, j; |
| for (i = 0; i < size; ++ i) { |
| if (raw[i] != outputStr[i]) |
| return false; |
| } |
| |
| // Compare bits |
| |
| char mask = 0x01; |
| if (maxCompare != 0) { |
| for (j = 0 ; j < (unsigned int) d.rem; ++j) { |
| |
| if ((raw[i] & mask) != (outputStr[i] & mask)) |
| return false; |
| |
| mask = mask << 1; |
| } |
| } |
| |
| return true; |
| } |
| |
| |
| void convertRawToBase64String(safeBuffer& b64SB, |
| unsigned char* raw, |
| unsigned int rawLen, |
| unsigned int maxBits = 0) { |
| |
| // Translate the rawbuffer (at most maxBits or rawLen - whichever is smaller) |
| // to a base64 string |
| |
| unsigned char b64Str[MAXB64BUFSIZE]; |
| unsigned int outputLen = 0; |
| |
| XSECCryptoBase64 * b64 = XSECPlatformUtils::g_cryptoProvider->base64(); |
| |
| if (!b64) { |
| throw XSECException(XSECException::CryptoProviderError, |
| "Error requesting Base64 object from Crypto Provider"); |
| } |
| |
| Janitor<XSECCryptoBase64> j_b64(b64); |
| |
| // Determine length to translate |
| unsigned int size; |
| |
| if (maxBits > 0) { |
| div_t d = div(maxBits, 8); |
| size = d.quot; |
| if (d.rem != 0) |
| ++size; |
| |
| if (size > rawLen) |
| size = rawLen; |
| } |
| |
| else |
| size = rawLen; |
| |
| b64->encodeInit(); |
| outputLen = b64->encode((unsigned char *) raw, rawLen, b64Str, MAXB64BUFSIZE - 1); |
| outputLen += b64->encodeFinish(&b64Str[outputLen], MAXB64BUFSIZE - outputLen - 1); |
| b64Str[outputLen] = '\0'; |
| |
| // Copy out |
| |
| b64SB.sbStrcpyIn((char *) b64Str); |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Clone |
| // -------------------------------------------------------------------------------- |
| |
| XSECAlgorithmHandler* DSIGAlgorithmHandlerDefault::clone() const { |
| |
| DSIGAlgorithmHandlerDefault* ret; |
| XSECnew(ret, DSIGAlgorithmHandlerDefault); |
| |
| return ret; |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Add a hash txfm |
| // -------------------------------------------------------------------------------- |
| |
| TXFMBase* addHashTxfm(XSECCryptoHash::HashType hashType, const XSECCryptoKey* key, DOMDocument* doc) { |
| |
| // Given a hash method and signature method, create an appropriate TXFM |
| |
| TXFMBase* txfm; |
| |
| XSECnew(txfm, TXFMHash(doc, hashType, key)); |
| |
| return txfm; |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Map a Signature hash |
| // -------------------------------------------------------------------------------- |
| |
| bool DSIGAlgorithmHandlerDefault::appendSignatureHashTxfm(TXFMChain* inputBytes, |
| const XMLCh* URI, |
| const XSECCryptoKey* key) const { |
| |
| XSECCryptoHash::HashType hashType; |
| |
| // Map to internal constants |
| |
| if (!XSECAlgorithmSupport::evalSignatureMethod(URI, key, hashType)) { |
| safeBuffer sb; |
| sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Unknown or key-incompatible URI : "); |
| sb.sbXMLChCat(URI); |
| |
| throw XSECException(XSECException::AlgorithmMapperError, |
| sb.rawXMLChBuffer()); |
| } |
| |
| // Now append the appropriate hash transform onto the end of the chain |
| // If this is an HMAC of some kind - this function will add the appropriate key |
| |
| TXFMBase* htxfm = addHashTxfm( |
| hashType, |
| (key->getKeyType() == XSECCryptoKey::KEY_HMAC ? key : NULL), |
| inputBytes->getLastTxfm()->getDocument()); |
| inputBytes->appendTxfm(htxfm); |
| |
| return true; |
| } |
| |
| |
| // -------------------------------------------------------------------------------- |
| // Sign |
| // -------------------------------------------------------------------------------- |
| |
| unsigned int DSIGAlgorithmHandlerDefault::signToSafeBuffer( |
| TXFMChain* inputBytes, |
| const XMLCh* URI, |
| const XSECCryptoKey* key, |
| unsigned int outputLength, |
| safeBuffer& result) const { |
| |
| XSECCryptoHash::HashType hashType; |
| |
| // Map to internal constants |
| |
| if (!XSECAlgorithmSupport::evalSignatureMethod(URI, key, hashType)) { |
| safeBuffer sb; |
| sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Unknown or key-incompatible URI : "); |
| sb.sbXMLChCat(URI); |
| |
| throw XSECException(XSECException::AlgorithmMapperError, |
| sb.rawXMLChBuffer()); |
| } |
| |
| // Now append the appropriate hash transform onto the end of the chain |
| // If this is an HMAC of some kind - this function will add the appropriate key |
| |
| TXFMBase * htxfm = addHashTxfm( |
| hashType, |
| (key->getKeyType() == XSECCryptoKey::KEY_HMAC ? key : NULL), |
| inputBytes->getLastTxfm()->getDocument()); |
| inputBytes->appendTxfm(htxfm); |
| |
| unsigned char hash[4096]; |
| |
| int hashLen = inputBytes->getLastTxfm()->readBytes((XMLByte *) hash, 4096); |
| |
| // Now check the calculated hash |
| |
| // For now, use a fixed length buffer, but expand it, |
| // and detect if the signature size exceeds what we can |
| // handle. |
| char b64Buf[MAXB64BUFSIZE]; |
| unsigned int b64Len; |
| safeBuffer b64SB; |
| |
| switch (key->getKeyType()) { |
| |
| case (XSECCryptoKey::KEY_DSA_PRIVATE) : |
| case (XSECCryptoKey::KEY_DSA_PAIR) : |
| |
| b64Len = ((XSECCryptoKeyDSA *) key)->signBase64Signature( |
| hash, |
| hashLen, |
| (char *) b64Buf, |
| MAXB64BUFSIZE); |
| |
| if (b64Len <= 0) { |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "Unknown error occurred during a DSA Signing operation"); |
| } |
| else if (b64Len >= MAXB64BUFSIZE) { |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "DSA Signing operation exceeded size of buffer"); |
| } |
| |
| if (b64Buf[b64Len-1] == '\n') |
| b64Buf[b64Len-1] = '\0'; |
| else |
| b64Buf[b64Len] = '\0'; |
| |
| break; |
| |
| case (XSECCryptoKey::KEY_RSA_PRIVATE) : |
| case (XSECCryptoKey::KEY_RSA_PAIR) : |
| |
| b64Len = ((XSECCryptoKeyRSA *) key)->signSHA1PKCS1Base64Signature( |
| hash, |
| hashLen, |
| (char *) b64Buf, |
| MAXB64BUFSIZE, |
| hashType); |
| |
| if (b64Len <= 0) { |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "Unknown error occurred during a RSA Signing operation"); |
| } |
| else if (b64Len >= MAXB64BUFSIZE) { |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "RSA Signing operation exceeded size of buffer"); |
| } |
| |
| // Clean up some "funnies" and make sure the string is NULL terminated |
| |
| if (b64Buf[b64Len-1] == '\n') |
| b64Buf[b64Len-1] = '\0'; |
| else |
| b64Buf[b64Len] = '\0'; |
| |
| break; |
| |
| case (XSECCryptoKey::KEY_EC_PRIVATE) : |
| case (XSECCryptoKey::KEY_EC_PAIR) : |
| |
| b64Len = ((XSECCryptoKeyEC *) key)->signBase64SignatureDSA( |
| hash, |
| hashLen, |
| (char *) b64Buf, |
| MAXB64BUFSIZE); |
| |
| if (b64Len <= 0) { |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "Unknown error occurred during an ECDSA Signing operation"); |
| } |
| else if (b64Len >= MAXB64BUFSIZE) { |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "ECDSA Signing operation exceeded size of buffer"); |
| } |
| |
| if (b64Buf[b64Len-1] == '\n') |
| b64Buf[b64Len-1] = '\0'; |
| else |
| b64Buf[b64Len] = '\0'; |
| |
| break; |
| |
| case (XSECCryptoKey::KEY_HMAC) : |
| |
| // Signature already created, so just translate to base 64 and enter string |
| |
| // FIX: CVE-2009-0217 |
| if (outputLength > 0 && (outputLength > (unsigned int)hashLen || outputLength < 80 || outputLength < (unsigned int)hashLen / 2)) { |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "HMACOutputLength set to unsafe value."); |
| } |
| |
| convertRawToBase64String(b64SB, |
| hash, |
| hashLen, |
| outputLength); |
| |
| strncpy(b64Buf, (char *) b64SB.rawBuffer(), MAXB64BUFSIZE); |
| break; |
| |
| default : |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "Key found, but don't know how to sign the document using it"); |
| |
| } |
| |
| result = b64Buf; |
| |
| return (unsigned int) strlen(b64Buf); |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Verify |
| // -------------------------------------------------------------------------------- |
| bool DSIGAlgorithmHandlerDefault::verifyBase64Signature( |
| TXFMChain* inputBytes, |
| const XMLCh* URI, |
| const char* sig, |
| unsigned int outputLength, |
| const XSECCryptoKey* key) const { |
| |
| XSECCryptoHash::HashType hashType; |
| |
| // Map to internal constants |
| |
| if (!XSECAlgorithmSupport::evalSignatureMethod(URI, key, hashType)) { |
| safeBuffer sb; |
| sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Unknown or key-incompatible URI : "); |
| sb.sbXMLChCat(URI); |
| |
| throw XSECException(XSECException::AlgorithmMapperError, |
| sb.rawXMLChBuffer()); |
| } |
| |
| // Now append the appropriate hash transform onto the end of the chain |
| // If this is an HMAC of some kind - this function will add the appropriate key |
| |
| TXFMBase * htxfm = addHashTxfm( |
| hashType, |
| (key->getKeyType() == XSECCryptoKey::KEY_HMAC ? key : NULL), |
| inputBytes->getLastTxfm()->getDocument()); |
| inputBytes->appendTxfm(htxfm); |
| |
| unsigned char hash[4096]; |
| |
| int hashLen = inputBytes->getLastTxfm()->readBytes((XMLByte *) hash, 4096); |
| |
| // Now check the calculated hash |
| bool sigVfyRet = false; |
| |
| switch (key->getKeyType()) { |
| |
| case (XSECCryptoKey::KEY_DSA_PUBLIC) : |
| case (XSECCryptoKey::KEY_DSA_PAIR) : |
| |
| sigVfyRet = ((XSECCryptoKeyDSA *) key)->verifyBase64Signature( |
| hash, |
| hashLen, |
| (char *) sig, |
| (unsigned int) strlen(sig)); |
| |
| break; |
| |
| case (XSECCryptoKey::KEY_RSA_PUBLIC) : |
| case (XSECCryptoKey::KEY_RSA_PAIR) : |
| |
| sigVfyRet = ((XSECCryptoKeyRSA *) key)->verifySHA1PKCS1Base64Signature( |
| hash, |
| hashLen, |
| sig, |
| (unsigned int) strlen(sig), |
| hashType); |
| |
| break; |
| |
| case (XSECCryptoKey::KEY_EC_PUBLIC) : |
| case (XSECCryptoKey::KEY_EC_PAIR) : |
| |
| sigVfyRet = ((XSECCryptoKeyEC *) key)->verifyBase64SignatureDSA( |
| hash, |
| hashLen, |
| (char *) sig, |
| (unsigned int) strlen(sig)); |
| |
| break; |
| |
| case (XSECCryptoKey::KEY_HMAC) : |
| |
| // Already done - just compare calculated value with read value |
| |
| // FIX: CVE-2009-0217 |
| if (outputLength > 0 && (outputLength > (unsigned int)hashLen || outputLength < 80 || outputLength < (unsigned int)hashLen / 2)) { |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "HMACOutputLength set to unsafe value."); |
| } |
| |
| sigVfyRet = compareBase64StringToRaw(sig, |
| hash, |
| hashLen, |
| outputLength); |
| |
| break; |
| |
| default : |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "Key found, but don't know how to verify the signature using it"); |
| |
| } |
| |
| return sigVfyRet; |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Hash TXFM appenders |
| // -------------------------------------------------------------------------------- |
| |
| bool DSIGAlgorithmHandlerDefault::appendHashTxfm( |
| TXFMChain* inputBytes, |
| const XMLCh* URI) const { |
| |
| // Is this a URI we recognize? |
| |
| XSECCryptoHash::HashType hashType = XSECAlgorithmSupport::getHashType(URI); |
| |
| if (hashType == XSECCryptoHash::HASH_NONE) { |
| safeBuffer sb; |
| sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Unknown Hash URI : "); |
| sb.sbXMLChCat(URI); |
| |
| throw XSECException(XSECException::AlgorithmMapperError, sb.rawXMLChBuffer()); |
| } |
| |
| TXFMBase* txfm; |
| DOMDocument* d = inputBytes->getLastTxfm()->getDocument(); |
| XSECnew(txfm, TXFMHash(d, hashType)); |
| |
| inputBytes->appendTxfm(txfm); |
| |
| return true; |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // SafeBuffer decryption |
| // -------------------------------------------------------------------------------- |
| |
| unsigned int DSIGAlgorithmHandlerDefault::decryptToSafeBuffer( |
| TXFMChain* cipherText, |
| XENCEncryptionMethod* encryptionMethod, |
| const XSECCryptoKey* key, |
| DOMDocument* doc, |
| safeBuffer& result |
| ) const { |
| |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "DSIGAlgorithmHandlerDefault - Encryption operations not supported"); |
| } |
| |
| bool DSIGAlgorithmHandlerDefault::appendDecryptCipherTXFM( |
| TXFMChain* cipherText, |
| XENCEncryptionMethod* encryptionMethod, |
| const XSECCryptoKey* key, |
| XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc |
| ) const { |
| |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "DSIGAlgorithmHandlerDefault - Encryption operations not supported"); |
| } |
| |
| |
| // -------------------------------------------------------------------------------- |
| // SafeBuffer encryption |
| // -------------------------------------------------------------------------------- |
| |
| bool DSIGAlgorithmHandlerDefault::encryptToSafeBuffer( |
| TXFMChain* plainText, |
| XENCEncryptionMethod* encryptionMethod, |
| const XSECCryptoKey* key, |
| XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc, |
| safeBuffer& result |
| ) const { |
| |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "DSIGAlgorithmHandlerDefault - Encryption operations not supported"); |
| } |
| |
| // -------------------------------------------------------------------------------- |
| // Key Creation |
| // -------------------------------------------------------------------------------- |
| |
| XSECCryptoKey* DSIGAlgorithmHandlerDefault::createKeyForURI( |
| const XMLCh* uri, |
| const unsigned char* keyBuffer, |
| unsigned int keyLen |
| ) const { |
| |
| throw XSECException(XSECException::AlgorithmMapperError, |
| "DSIGAlgorithmHandlerDefault - Key creation operations not supported"); |
| } |