ce9445f51d73e5ba2872a610e935ff62455f1480
[shibboleth/cpp-xmltooling.git] / xmltooling / encryption / impl / Decrypter.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * Decrypter.cpp
23  * 
24  * Methods for decrypting XMLObjects and other data.
25  */
26
27 #include "internal.h"
28 #include "logging.h"
29 #include "encryption/Decrypter.h"
30 #include "encryption/EncryptedKeyResolver.h"
31 #include "encryption/Encryption.h"
32 #include "security/Credential.h"
33 #include "security/CredentialCriteria.h"
34 #include "security/CredentialResolver.h"
35
36 #include <xsec/enc/XSECCryptoException.hpp>
37 #include <xsec/framework/XSECException.hpp>
38 #include <xsec/framework/XSECAlgorithmMapper.hpp>
39 #include <xsec/framework/XSECAlgorithmHandler.hpp>
40 #include <xsec/utils/XSECBinTXFMInputStream.hpp>
41 #include <xsec/xenc/XENCCipher.hpp>
42 #include <xsec/xenc/XENCEncryptedData.hpp>
43 #include <xsec/xenc/XENCEncryptedKey.hpp>
44
45 using namespace xmlencryption;
46 using namespace xmlsignature;
47 using namespace xmltooling;
48 using namespace xercesc;
49 using namespace std;
50
51
52 Decrypter::Decrypter(
53     const CredentialResolver* credResolver,
54     CredentialCriteria* criteria,
55     const EncryptedKeyResolver* EKResolver,
56     bool requireAuthenticatedCipher
57     ) : m_cipher(nullptr), m_credResolver(credResolver), m_criteria(criteria), m_EKResolver(EKResolver),
58         m_requireAuthenticatedCipher(requireAuthenticatedCipher)
59 {
60 }
61
62 Decrypter::~Decrypter()
63 {
64     if (m_cipher)
65         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
66 }
67
68 void Decrypter::setEncryptedKeyResolver(const EncryptedKeyResolver* EKResolver)
69 {
70     m_EKResolver=EKResolver;
71 }
72
73 void Decrypter::setKEKResolver(const CredentialResolver* resolver, CredentialCriteria* criteria)
74 {
75     m_credResolver=resolver;
76     m_criteria=criteria;
77 }
78
79 DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, XSECCryptoKey* key)
80 {
81     if (encryptedData.getDOM() == nullptr)
82         throw DecryptionException("The object must be marshalled before decryption.");
83
84     XMLToolingInternalConfig& xmlconf = XMLToolingInternalConfig::getInternalConfig();
85     if (m_requireAuthenticatedCipher) {
86         const XMLCh* alg = encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
87         if (!alg || !xmlconf.isXMLAlgorithmSupported(alg, XMLToolingConfig::ALGTYPE_AUTHNENCRYPT)) {
88             throw DecryptionException("Unauthenticated data encryption algorithm unsupported.");
89         }
90     }
91
92     // We can reuse the cipher object if the document hasn't changed.
93
94     if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) {
95         xmlconf.m_xsecProvider->releaseCipher(m_cipher);
96         m_cipher=nullptr;
97     }
98     
99     if (!m_cipher)
100         m_cipher = xmlconf.m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument());
101
102     try {
103         m_cipher->setKey(key->clone());
104         DOMNode* ret = m_cipher->decryptElementDetached(encryptedData.getDOM());
105         if (ret->getNodeType()!=DOMNode::DOCUMENT_FRAGMENT_NODE) {
106             ret->release();
107             throw DecryptionException("Decryption operation did not result in DocumentFragment.");
108         }
109         return static_cast<DOMDocumentFragment*>(ret);
110     }
111     catch(XSECException& e) {
112         auto_ptr_char temp(e.getMsg());
113         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
114     }
115     catch(XSECCryptoException& e) {
116         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
117     }
118 }
119
120 DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, const XMLCh* recipient)
121 {
122     if (!m_credResolver)
123         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
124
125     // Resolve a decryption key directly.
126     vector<const Credential*> creds;
127     int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
128     if (m_criteria) {
129         m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
130         m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types);
131         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
132         if (meth)
133             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
134         m_credResolver->resolve(creds, m_criteria);
135     }
136     else {
137         CredentialCriteria criteria;
138         criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
139         criteria.setKeyInfo(encryptedData.getKeyInfo(), types);
140         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
141         if (meth)
142             criteria.setXMLAlgorithm(meth->getAlgorithm());
143         m_credResolver->resolve(creds, &criteria);
144     }
145
146     // Loop over them and try each one.
147     XSECCryptoKey* key;
148     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) {
149         try {
150             key = (*cred)->getPrivateKey();
151             if (!key)
152                 continue;
153             return decryptData(encryptedData, key);
154         }
155         catch(DecryptionException& ex) {
156             logging::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
157         }
158     }
159
160     // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm...
161     const XMLCh* algorithm=
162         encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
163     if (!algorithm)
164         throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
165     
166     // Check for external resolver.
167     const EncryptedKey* encKey=nullptr;
168     if (m_EKResolver)
169         encKey = m_EKResolver->resolveKey(encryptedData, recipient);
170     else {
171         EncryptedKeyResolver ekr;
172         encKey = ekr.resolveKey(encryptedData, recipient);
173     }
174
175     if (!encKey)
176         throw DecryptionException("Unable to locate an encrypted key.");
177
178     auto_ptr<XSECCryptoKey> keywrapper(decryptKey(*encKey, algorithm));
179     if (!keywrapper.get())
180         throw DecryptionException("Unable to decrypt the encrypted key.");
181     return decryptData(encryptedData, keywrapper.get());
182 }
183
184 void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, XSECCryptoKey* key)
185 {
186     if (encryptedData.getDOM() == nullptr)
187         throw DecryptionException("The object must be marshalled before decryption.");
188
189     XMLToolingInternalConfig& xmlconf = XMLToolingInternalConfig::getInternalConfig();
190     if (m_requireAuthenticatedCipher) {
191         const XMLCh* alg = encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
192         if (!alg || !xmlconf.isXMLAlgorithmSupported(alg, XMLToolingConfig::ALGTYPE_AUTHNENCRYPT)) {
193             throw DecryptionException("Unauthenticated data encryption algorithm unsupported.");
194         }
195     }
196
197     // We can reuse the cipher object if the document hasn't changed.
198
199     if (m_cipher && m_cipher->getDocument() != encryptedData.getDOM()->getOwnerDocument()) {
200         xmlconf.m_xsecProvider->releaseCipher(m_cipher);
201         m_cipher = nullptr;
202     }
203     
204     if (!m_cipher)
205         m_cipher = xmlconf.m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument());
206
207     try {
208         m_cipher->setKey(key->clone());
209         auto_ptr<XSECBinTXFMInputStream> in(m_cipher->decryptToBinInputStream(encryptedData.getDOM()));
210         
211         XMLByte buf[8192];
212         xsecsize_t count = in->readBytes(buf, sizeof(buf));
213         while (count > 0)
214             out.write(reinterpret_cast<char*>(buf),count);
215     }
216     catch(XSECException& e) {
217         auto_ptr_char temp(e.getMsg());
218         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
219     }
220     catch(XSECCryptoException& e) {
221         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
222     }
223 }
224
225 void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, const XMLCh* recipient)
226 {
227     if (!m_credResolver)
228         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
229
230     // Resolve a decryption key directly.
231     vector<const Credential*> creds;
232     int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
233     if (m_criteria) {
234         m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
235         m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types);
236         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
237         if (meth)
238             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
239         m_credResolver->resolve(creds,m_criteria);
240     }
241     else {
242         CredentialCriteria criteria;
243         criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
244         criteria.setKeyInfo(encryptedData.getKeyInfo(), types);
245         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
246         if (meth)
247             criteria.setXMLAlgorithm(meth->getAlgorithm());
248         m_credResolver->resolve(creds,&criteria);
249     }
250
251     // Loop over them and try each one.
252     XSECCryptoKey* key;
253     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) {
254         try {
255             key = (*cred)->getPrivateKey();
256             if (!key)
257                 continue;
258             return decryptData(out, encryptedData, key);
259         }
260         catch(DecryptionException& ex) {
261             logging::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
262         }
263     }
264
265     // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm...
266     const XMLCh* algorithm=
267         encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
268     if (!algorithm)
269         throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
270     
271     // Check for external resolver.
272     const EncryptedKey* encKey=nullptr;
273     if (m_EKResolver)
274         encKey = m_EKResolver->resolveKey(encryptedData, recipient);
275     else {
276         EncryptedKeyResolver ekr;
277         encKey = ekr.resolveKey(encryptedData, recipient);
278     }
279
280     if (!encKey)
281         throw DecryptionException("Unable to locate an encrypted key.");
282
283     auto_ptr<XSECCryptoKey> keywrapper(decryptKey(*encKey, algorithm));
284     if (!keywrapper.get())
285         throw DecryptionException("Unable to decrypt the encrypted key.");
286     decryptData(out, encryptedData, keywrapper.get());
287 }
288
289 XSECCryptoKey* Decrypter::decryptKey(const EncryptedKey& encryptedKey, const XMLCh* algorithm)
290 {
291     if (!m_credResolver)
292         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
293
294     if (encryptedKey.getDOM()==nullptr)
295         throw DecryptionException("The object must be marshalled before decryption.");
296
297     XSECAlgorithmHandler* handler;
298     try {
299         handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
300         if (!handler)
301             throw DecryptionException("Unrecognized algorithm, no way to build object around decrypted key.");
302     }
303     catch(XSECException& e) {
304         auto_ptr_char temp(e.getMsg());
305         throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
306     }
307     catch(XSECCryptoException& e) {
308         throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
309     }
310     
311     // We can reuse the cipher object if the document hasn't changed.
312
313     if (m_cipher && m_cipher->getDocument()!=encryptedKey.getDOM()->getOwnerDocument()) {
314         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
315         m_cipher = nullptr;
316     }
317     
318     if (!m_cipher)
319         m_cipher = XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument());
320     
321     // Resolve key decryption keys.
322     int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
323     vector<const Credential*> creds;
324     if (m_criteria) {
325         m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
326         m_criteria->setKeyInfo(encryptedKey.getKeyInfo(), types);
327         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
328         if (meth)
329             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
330         m_credResolver->resolve(creds, m_criteria);
331     }
332     else {
333         CredentialCriteria criteria;
334         criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
335         criteria.setKeyInfo(encryptedKey.getKeyInfo(), types);
336         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
337         if (meth)
338             criteria.setXMLAlgorithm(meth->getAlgorithm());
339         m_credResolver->resolve(creds, &criteria);
340     }
341     if (creds.empty())
342         throw DecryptionException("Unable to resolve any key decryption keys.");
343
344     XMLByte buffer[1024];
345     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) {
346         try {
347             if (!(*cred)->getPrivateKey())
348                 throw DecryptionException("Credential did not contain a private key.");
349             memset(buffer,0,sizeof(buffer));
350             m_cipher->setKEK((*cred)->getPrivateKey()->clone());
351
352             try {
353                 int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024);
354                 if (keySize<=0)
355                     throw DecryptionException("Unable to decrypt key.");
356         
357                 // Try to wrap the key.
358                 return handler->createKeyForURI(algorithm, buffer, keySize);
359             }
360             catch(XSECException& e) {
361                 auto_ptr_char temp(e.getMsg());
362                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
363             }
364             catch(XSECCryptoException& e) {
365                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
366             }
367         }
368         catch(DecryptionException& ex) {
369             logging::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
370         }
371     }
372     
373     // Some algorithms are vulnerable to chosen ciphertext attacks, so we generate a random key
374     // to prevent discovery of the validity of the original candidate.
375     logging::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(
376         "unable to decrypt key, generating random key for defensive purposes"
377         );
378     pair<const char*,unsigned int> mapped = XMLToolingConfig::getConfig().mapXMLAlgorithmToKeyAlgorithm(algorithm);
379     if (!mapped.second)
380         mapped.second = 256;
381     try {
382         if (XSECPlatformUtils::g_cryptoProvider->getRandom(reinterpret_cast<unsigned char*>(buffer),mapped.second) < mapped.second)
383             throw DecryptionException("Unable to generate random data; was PRNG seeded?");
384         return handler->createKeyForURI(algorithm, buffer, mapped.second);
385     }
386     catch(XSECException& e) {
387         auto_ptr_char temp(e.getMsg());
388         throw DecryptionException(string("XMLSecurity exception while generating key: ") + temp.get());
389     }
390     catch (XSECCryptoException& e) {
391         throw DecryptionException(string("XMLSecurity exception while generating key: ") + e.getMsg());
392     }
393 }