Add option to reject unauthenticated ciphers
authorScott Cantor <cantor.2@osu.edu>
Fri, 2 Mar 2012 18:10:13 +0000 (18:10 +0000)
committerScott Cantor <cantor.2@osu.edu>
Fri, 2 Mar 2012 18:10:13 +0000 (18:10 +0000)
xmltooling/XMLToolingConfig.cpp
xmltooling/XMLToolingConfig.h
xmltooling/encryption/Decrypter.h
xmltooling/encryption/impl/Decrypter.cpp

index bd7fbcb..fe398b7 100644 (file)
@@ -714,6 +714,9 @@ void XMLToolingInternalConfig::registerXMLAlgorithm(
     )
 {
     m_algorithmMap[type][xmlAlgorithm] = pair<string,unsigned int>((keyAlgorithm ? keyAlgorithm : ""), size);
+    // Authenticated encryption algorithms are also generic encryption algorithms.
+    if (type == ALGTYPE_AUTHNENCRYPT)
+        m_algorithmMap[ALGTYPE_ENCRYPT][xmlAlgorithm] = pair<string,unsigned int>((keyAlgorithm ? keyAlgorithm : ""), size);
 }
 
 bool XMLToolingInternalConfig::isXMLAlgorithmSupported(const XMLCh* xmlAlgorithm, XMLSecurityAlgorithmType type)
index 76d81fc..93e9e51 100644 (file)
@@ -317,7 +317,8 @@ namespace xmltooling {
             ALGTYPE_SIGN,
             ALGTYPE_ENCRYPT,
             ALGTYPE_KEYENCRYPT,
-            ALGTYPE_KEYAGREE
+            ALGTYPE_KEYAGREE,
+            ALGTYPE_AUTHNENCRYPT
         };
 
         /**
index 3fbdfb4..5f26258 100644 (file)
@@ -52,14 +52,22 @@ namespace xmlencryption {
         /**
          * Constructor.
          * 
+         * <p>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);
index 489834c..ce9445f 100644 (file)
@@ -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 Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
+    for (vector<const Credential*>::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 Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
+    for (vector<const Credential*>::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 Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
+    for (vector<const Credential*>::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) {
         try {
             if (!(*cred)->getPrivateKey())
                 throw DecryptionException("Credential did not contain a private key.");