blob: 14694ced52089e869ef8d07d34100da69651dc32 [file] [log] [blame]
/**
* 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/framework/XSECDefs.hpp>
#include <xsec/transformers/TXFMChain.hpp>
#include <xsec/transformers/TXFMBase64.hpp>
#include <xsec/transformers/TXFMSHA1.hpp>
#include <xsec/transformers/TXFMMD5.hpp>
#include <xsec/enc/XSECCryptoKey.hpp>
#include <xsec/framework/XSECError.hpp>
#include <xsec/utils/XSECDOMUtils.hpp>
#include <xsec/dsig/DSIGAlgorithmHandlerDefault.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
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, maxCompareBits;
maxCompareBits = 0;
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; ++i) {
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(void) const {
DSIGAlgorithmHandlerDefault * ret;
XSECnew(ret, DSIGAlgorithmHandlerDefault);
return ret;
}
// --------------------------------------------------------------------------------
// Add a hash txfm
// --------------------------------------------------------------------------------
TXFMBase * addHashTxfm(signatureMethod sm, hashMethod hm, XSECCryptoKey * key,
DOMDocument * doc) {
// Given a hash method and signature method, create an appropriate TXFM
TXFMBase * txfm;
switch (hm) {
case HASH_MD5 :
if (sm == SIGNATURE_HMAC){
if (key->getKeyType() != XSECCryptoKey::KEY_HMAC) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault::addHashTxfm - non HMAC key passed in to HMAC signature");
}
XSECnew(txfm, TXFMMD5(doc, key));
}
else {
XSECnew(txfm, TXFMMD5(doc));
}
break;
case HASH_SHA1 :
if (sm == SIGNATURE_HMAC){
if (key->getKeyType() != XSECCryptoKey::KEY_HMAC) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault::addHashTxfm - non HMAC key passed in to HMAC signature");
}
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA1, key));
}
else {
XSECnew(txfm, TXFMSHA1(doc));
}
break;
case HASH_SHA224 :
if (sm == SIGNATURE_HMAC){
if (key->getKeyType() != XSECCryptoKey::KEY_HMAC) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault::addHashTxfm - non HMAC key passed in to HMAC signature");
}
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA224, key));
}
else {
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA224));
}
break;
case HASH_SHA256 :
if (sm == SIGNATURE_HMAC){
if (key->getKeyType() != XSECCryptoKey::KEY_HMAC) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault::addHashTxfm - non HMAC key passed in to HMAC signature");
}
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA256, key));
}
else {
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA256));
}
break;
case HASH_SHA384 :
if (sm == SIGNATURE_HMAC){
if (key->getKeyType() != XSECCryptoKey::KEY_HMAC) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault::addHashTxfm - non HMAC key passed in to HMAC signature");
}
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA384, key));
}
else {
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA384));
}
break;
case HASH_SHA512 :
if (sm == SIGNATURE_HMAC){
if (key->getKeyType() != XSECCryptoKey::KEY_HMAC) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault::addHashTxfm - non HMAC key passed in to HMAC signature");
}
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA512, key));
}
else {
XSECnew(txfm, TXFMSHA1(doc, HASH_SHA512));
}
break;
default :
throw XSECException(XSECException::AlgorithmMapperError,
"Hash method unknown in DSIGAlgorithmHandlerDefault::addHashTxfm");
}
return txfm;
}
// --------------------------------------------------------------------------------
// Map a Signature hash
// --------------------------------------------------------------------------------
bool DSIGAlgorithmHandlerDefault::appendSignatureHashTxfm(TXFMChain * inputBytes,
const XMLCh * URI,
XSECCryptoKey * key) {
signatureMethod sm;
hashMethod hm;
// Map to internal constants
if (!XSECmapURIToSignatureMethods(URI, sm, hm)) {
safeBuffer sb;
sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Unknown 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(sm, hm, key, inputBytes->getLastTxfm()->getDocument());
inputBytes->appendTxfm(htxfm);
return true;
}
// --------------------------------------------------------------------------------
// Sign
// --------------------------------------------------------------------------------
unsigned int DSIGAlgorithmHandlerDefault::signToSafeBuffer(
TXFMChain * inputBytes,
const XMLCh * URI,
XSECCryptoKey * key,
unsigned int outputLength,
safeBuffer & result) {
signatureMethod sm;
hashMethod hm;
// Map to internal constants
if (!XSECmapURIToSignatureMethods(URI, sm, hm)) {
safeBuffer sb;
sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Unknown 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(sm, hm, key, 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) :
if (sm != SIGNATURE_DSA) {
throw XSECException(XSECException::AlgorithmMapperError,
"Key type does not match <SignedInfo> signature type");
}
b64Len = ((XSECCryptoKeyDSA *) key)->signBase64Signature(
hash,
hashLen,
(char *) b64Buf,
MAXB64BUFSIZE);
if (b64Len <= 0) {
throw XSECException(XSECException::AlgorithmMapperError,
"Unknown error occured 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) :
if (sm != SIGNATURE_RSA) {
throw XSECException(XSECException::AlgorithmMapperError,
"Key type does not match <SignedInfo> signature type");
}
b64Len = ((XSECCryptoKeyRSA *) key)->signSHA1PKCS1Base64Signature(
hash,
hashLen,
(char *) b64Buf,
MAXB64BUFSIZE,
hm);
if (b64Len <= 0) {
throw XSECException(XSECException::AlgorithmMapperError,
"Unknown error occured 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) :
if (sm != SIGNATURE_ECDSA) {
throw XSECException(XSECException::AlgorithmMapperError,
"Key type does not match <SignedInfo> signature type");
}
b64Len = ((XSECCryptoKeyEC *) key)->signBase64SignatureDSA(
hash,
hashLen,
(char *) b64Buf,
MAXB64BUFSIZE);
if (b64Len <= 0) {
throw XSECException(XSECException::AlgorithmMapperError,
"Unknown error occured 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) :
if (sm != SIGNATURE_HMAC) {
throw XSECException(XSECException::AlgorithmMapperError,
"Key type does not match <SignedInfo> signature type");
}
// Signature already created, so just translate to base 64 and enter string
// FIX: CVE-2009-0217
if (outputLength > 0 && (outputLength < 80 || outputLength < 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,
XSECCryptoKey * key) {
signatureMethod sm;
hashMethod hm;
// Map to internal constants
if (!XSECmapURIToSignatureMethods(URI, sm, hm)) {
safeBuffer sb;
sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Unknown 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(sm, hm, key, 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) :
if (sm != SIGNATURE_DSA) {
throw XSECException(XSECException::AlgorithmMapperError,
"Key type does not match <SignedInfo> signature type");
}
sigVfyRet = ((XSECCryptoKeyDSA *) key)->verifyBase64Signature(
hash,
hashLen,
(char *) sig,
(unsigned int) strlen(sig));
break;
case (XSECCryptoKey::KEY_RSA_PUBLIC) :
case (XSECCryptoKey::KEY_RSA_PAIR) :
if (sm != SIGNATURE_RSA) {
throw XSECException(XSECException::AlgorithmMapperError,
"Key type does not match <SignedInfo> signature type");
}
sigVfyRet = ((XSECCryptoKeyRSA *) key)->verifySHA1PKCS1Base64Signature(
hash,
hashLen,
sig,
(unsigned int) strlen(sig),
hm);
break;
case (XSECCryptoKey::KEY_EC_PUBLIC) :
case (XSECCryptoKey::KEY_EC_PAIR) :
if (sm != SIGNATURE_ECDSA) {
throw XSECException(XSECException::AlgorithmMapperError,
"Key type does not match <SignedInfo> signature type");
}
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 < 80 || outputLength < 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 check the signature using it");
}
return sigVfyRet;
}
// --------------------------------------------------------------------------------
// Hash TXFM appenders
// --------------------------------------------------------------------------------
bool DSIGAlgorithmHandlerDefault::appendHashTxfm(
TXFMChain * inputBytes,
const XMLCh * URI) {
hashMethod hm;
// Is this a URI we recognise?
if (!XSECmapURIToHashMethod(URI, hm)) {
safeBuffer sb;
sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Unknown Hash URI : ");
sb.sbXMLChCat(URI);
throw XSECException(XSECException::AlgorithmMapperError,
sb.rawXMLChBuffer());
}
TXFMBase * txfm;
DOMDocument *d = inputBytes->getLastTxfm()->getDocument();
switch (hm) {
case HASH_SHA1 :
case HASH_SHA224 :
case HASH_SHA256 :
case HASH_SHA384 :
case HASH_SHA512 :
XSECnew(txfm, TXFMSHA1(d, hm));
break;
case HASH_MD5 :
XSECnew(txfm, TXFMMD5(d));
break;
default :
safeBuffer sb;
sb.sbTranscodeIn("DSIGAlgorithmHandlerDefault - Internal error unknown Hash, but URI known. URI : ");
sb.sbXMLChCat(URI);
throw XSECException(XSECException::AlgorithmMapperError,
sb.rawXMLChBuffer());
}
inputBytes->appendTxfm(txfm);
return true;
}
// --------------------------------------------------------------------------------
// SafeBuffer decryption
// --------------------------------------------------------------------------------
unsigned int DSIGAlgorithmHandlerDefault::decryptToSafeBuffer(
TXFMChain * cipherText,
XENCEncryptionMethod * encryptionMethod,
XSECCryptoKey * key,
DOMDocument * doc,
safeBuffer & result
) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault - Encryption operations not supported");
}
bool DSIGAlgorithmHandlerDefault::appendDecryptCipherTXFM(
TXFMChain * cipherText,
XENCEncryptionMethod * encryptionMethod,
XSECCryptoKey * key,
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * doc
) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault - Encryption operations not supported");
}
// --------------------------------------------------------------------------------
// SafeBuffer encryption
// --------------------------------------------------------------------------------
bool DSIGAlgorithmHandlerDefault::encryptToSafeBuffer(
TXFMChain * plainText,
XENCEncryptionMethod * encryptionMethod,
XSECCryptoKey * key,
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * doc,
safeBuffer & result
) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault - Encryption operations not supported");
}
// --------------------------------------------------------------------------------
// Key Creation
// --------------------------------------------------------------------------------
XSECCryptoKey * DSIGAlgorithmHandlerDefault::createKeyForURI(
const XMLCh * uri,
const unsigned char * keyBuffer,
unsigned int keyLen
) {
throw XSECException(XSECException::AlgorithmMapperError,
"DSIGAlgorithmHandlerDefault - Key creation operations not supported");
}