xsec/dsig/DSIGAlgorithmHandlerDefault.cpp (351 lines of code) (raw):

/** * 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"); }