From 4189e5d4286de20f61077bc202eaf3772bc66748 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Fri, 2 Mar 2012 18:10:13 +0000 Subject: [PATCH] Add option to reject unauthenticated ciphers --- xmltooling/XMLToolingConfig.cpp | 3 ++ xmltooling/XMLToolingConfig.h | 3 +- xmltooling/encryption/Decrypter.h | 11 +++++- xmltooling/encryption/impl/Decrypter.cpp | 58 ++++++++++++++++++++++---------- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/xmltooling/XMLToolingConfig.cpp b/xmltooling/XMLToolingConfig.cpp index bd7fbcb..fe398b7 100644 --- a/xmltooling/XMLToolingConfig.cpp +++ b/xmltooling/XMLToolingConfig.cpp @@ -714,6 +714,9 @@ void XMLToolingInternalConfig::registerXMLAlgorithm( ) { m_algorithmMap[type][xmlAlgorithm] = pair((keyAlgorithm ? keyAlgorithm : ""), size); + // Authenticated encryption algorithms are also generic encryption algorithms. + if (type == ALGTYPE_AUTHNENCRYPT) + m_algorithmMap[ALGTYPE_ENCRYPT][xmlAlgorithm] = pair((keyAlgorithm ? keyAlgorithm : ""), size); } bool XMLToolingInternalConfig::isXMLAlgorithmSupported(const XMLCh* xmlAlgorithm, XMLSecurityAlgorithmType type) diff --git a/xmltooling/XMLToolingConfig.h b/xmltooling/XMLToolingConfig.h index 76d81fc..93e9e51 100644 --- a/xmltooling/XMLToolingConfig.h +++ b/xmltooling/XMLToolingConfig.h @@ -317,7 +317,8 @@ namespace xmltooling { ALGTYPE_SIGN, ALGTYPE_ENCRYPT, ALGTYPE_KEYENCRYPT, - ALGTYPE_KEYAGREE + ALGTYPE_KEYAGREE, + ALGTYPE_AUTHNENCRYPT }; /** diff --git a/xmltooling/encryption/Decrypter.h b/xmltooling/encryption/Decrypter.h index 3fbdfb4..5f26258 100644 --- a/xmltooling/encryption/Decrypter.h +++ b/xmltooling/encryption/Decrypter.h @@ -52,14 +52,22 @@ namespace xmlencryption { /** * Constructor. * + *

The final boolean parameter is used to enforce a requirement for an authenticated cipher + * suite such as AES-GCM or similar. These ciphers include an HMAC or equivalent step that + * prevents tampering. Newer applications should set this parameter to true unless the ciphertext + * has been independently authenticated, and even in such a case, it is rarely possible to prevent + * chosen ciphertext attacks by trusted signers. + * * @param credResolver locked credential resolver to supply decryption keys * @param criteria optional external criteria to use with resolver * @param EKResolver locates an EncryptedKey pertaining to the EncryptedData + * @param requireAuthenticatedCipher true iff the bulk data encryption algorithm must be an authenticated cipher */ Decrypter( const xmltooling::CredentialResolver* credResolver=nullptr, xmltooling::CredentialCriteria* criteria=nullptr, - const EncryptedKeyResolver* EKResolver=nullptr + const EncryptedKeyResolver* EKResolver=nullptr, + bool requireAuthenticatedCipher=false ); virtual ~Decrypter(); @@ -148,6 +156,7 @@ namespace xmlencryption { const xmltooling::CredentialResolver* m_credResolver; xmltooling::CredentialCriteria* m_criteria; const EncryptedKeyResolver* m_EKResolver; + bool m_requireAuthenticatedCipher; }; DECL_XMLTOOLING_EXCEPTION(DecryptionException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlencryption,xmltooling::XMLToolingException,Exceptions in decryption processing); diff --git a/xmltooling/encryption/impl/Decrypter.cpp b/xmltooling/encryption/impl/Decrypter.cpp index 489834c..ce9445f 100644 --- a/xmltooling/encryption/impl/Decrypter.cpp +++ b/xmltooling/encryption/impl/Decrypter.cpp @@ -48,8 +48,14 @@ using namespace xmltooling; using namespace xercesc; using namespace std; -Decrypter::Decrypter(const CredentialResolver* credResolver, CredentialCriteria* criteria, const EncryptedKeyResolver* EKResolver) - : m_cipher(nullptr), m_credResolver(credResolver), m_criteria(criteria), m_EKResolver(EKResolver) + +Decrypter::Decrypter( + const CredentialResolver* credResolver, + CredentialCriteria* criteria, + const EncryptedKeyResolver* EKResolver, + bool requireAuthenticatedCipher + ) : m_cipher(nullptr), m_credResolver(credResolver), m_criteria(criteria), m_EKResolver(EKResolver), + m_requireAuthenticatedCipher(requireAuthenticatedCipher) { } @@ -72,22 +78,30 @@ void Decrypter::setKEKResolver(const CredentialResolver* resolver, CredentialCri DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, XSECCryptoKey* key) { - if (encryptedData.getDOM()==nullptr) + if (encryptedData.getDOM() == nullptr) throw DecryptionException("The object must be marshalled before decryption."); + XMLToolingInternalConfig& xmlconf = XMLToolingInternalConfig::getInternalConfig(); + if (m_requireAuthenticatedCipher) { + const XMLCh* alg = encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr; + if (!alg || !xmlconf.isXMLAlgorithmSupported(alg, XMLToolingConfig::ALGTYPE_AUTHNENCRYPT)) { + throw DecryptionException("Unauthenticated data encryption algorithm unsupported."); + } + } + // We can reuse the cipher object if the document hasn't changed. if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) { - XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher); + xmlconf.m_xsecProvider->releaseCipher(m_cipher); m_cipher=nullptr; } if (!m_cipher) - m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument()); + m_cipher = xmlconf.m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument()); try { m_cipher->setKey(key->clone()); - DOMNode* ret=m_cipher->decryptElementDetached(encryptedData.getDOM()); + DOMNode* ret = m_cipher->decryptElementDetached(encryptedData.getDOM()); if (ret->getNodeType()!=DOMNode::DOCUMENT_FRAGMENT_NODE) { ret->release(); throw DecryptionException("Decryption operation did not result in DocumentFragment."); @@ -117,7 +131,7 @@ DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, const EncryptionMethod* meth = encryptedData.getEncryptionMethod(); if (meth) m_criteria->setXMLAlgorithm(meth->getAlgorithm()); - m_credResolver->resolve(creds,m_criteria); + m_credResolver->resolve(creds, m_criteria); } else { CredentialCriteria criteria; @@ -126,12 +140,12 @@ DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, const EncryptionMethod* meth = encryptedData.getEncryptionMethod(); if (meth) criteria.setXMLAlgorithm(meth->getAlgorithm()); - m_credResolver->resolve(creds,&criteria); + m_credResolver->resolve(creds, &criteria); } // Loop over them and try each one. XSECCryptoKey* key; - for (vector::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) { + for (vector::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) { try { key = (*cred)->getPrivateKey(); if (!key) @@ -169,18 +183,26 @@ DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, XSECCryptoKey* key) { - if (encryptedData.getDOM()==nullptr) + if (encryptedData.getDOM() == nullptr) throw DecryptionException("The object must be marshalled before decryption."); + XMLToolingInternalConfig& xmlconf = XMLToolingInternalConfig::getInternalConfig(); + if (m_requireAuthenticatedCipher) { + const XMLCh* alg = encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr; + if (!alg || !xmlconf.isXMLAlgorithmSupported(alg, XMLToolingConfig::ALGTYPE_AUTHNENCRYPT)) { + throw DecryptionException("Unauthenticated data encryption algorithm unsupported."); + } + } + // We can reuse the cipher object if the document hasn't changed. - if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) { - XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher); - m_cipher=nullptr; + if (m_cipher && m_cipher->getDocument() != encryptedData.getDOM()->getOwnerDocument()) { + xmlconf.m_xsecProvider->releaseCipher(m_cipher); + m_cipher = nullptr; } if (!m_cipher) - m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument()); + m_cipher = xmlconf.m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument()); try { m_cipher->setKey(key->clone()); @@ -228,7 +250,7 @@ void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, co // Loop over them and try each one. XSECCryptoKey* key; - for (vector::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) { + for (vector::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) { try { key = (*cred)->getPrivateKey(); if (!key) @@ -290,11 +312,11 @@ XSECCryptoKey* Decrypter::decryptKey(const EncryptedKey& encryptedKey, const XML if (m_cipher && m_cipher->getDocument()!=encryptedKey.getDOM()->getOwnerDocument()) { XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher); - m_cipher=nullptr; + m_cipher = nullptr; } if (!m_cipher) - m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument()); + m_cipher = XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument()); // Resolve key decryption keys. int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES; @@ -320,7 +342,7 @@ XSECCryptoKey* Decrypter::decryptKey(const EncryptedKey& encryptedKey, const XML throw DecryptionException("Unable to resolve any key decryption keys."); XMLByte buffer[1024]; - for (vector::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) { + for (vector::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) { try { if (!(*cred)->getPrivateKey()) throw DecryptionException("Credential did not contain a private key."); -- 2.1.4