4a163d56d7e6803019e09e10acd4648cd9c45285
[shibboleth/cpp-opensaml.git] / saml / saml2 / core / impl / Assertions.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  * Assertions.cpp
23  * 
24  * Built-in behavior for SAML 2.0 Assertion interfaces.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "saml/encryption/EncryptedKeyResolver.h"
30 #include "saml2/core/Assertions.h"
31 #include "saml2/metadata/Metadata.h"
32 #include "saml2/metadata/MetadataProvider.h"
33 #include "saml2/metadata/MetadataCredentialContext.h"
34 #include "saml2/metadata/MetadataCredentialCriteria.h"
35
36 #include <xmltooling/logging.h>
37 #include <xmltooling/XMLToolingConfig.h>
38 #include <xmltooling/encryption/Encrypter.h>
39 #include <xmltooling/encryption/Decrypter.h>
40 #include <xmltooling/security/Credential.h>
41 #include <xmltooling/signature/KeyInfo.h>
42 #include <xmltooling/util/ParserPool.h>
43
44 #include <xsec/utils/XSECPlatformUtils.hpp>
45
46 using namespace opensaml::saml2md;
47 using namespace opensaml::saml2;
48 using namespace xmlencryption;
49 using namespace xmlsignature;
50 using namespace xmltooling;
51 using namespace std;
52
53 void EncryptedElementType::encrypt(
54     const EncryptableObject& xmlObject,
55     const MetadataProvider& metadataProvider,
56     MetadataCredentialCriteria& criteria,
57     bool compact,
58     const XMLCh* algorithm
59     )
60 {
61     XMLToolingConfig& conf = XMLToolingConfig::getConfig();
62
63     // With one recipient, we let the library generate the encryption key for us.
64     // Get the key encryption key to use. To make use of EncryptionMethod, we have
65     // to examine each possible credential in conjunction with the algorithms we
66     // support.
67     criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
68     vector<const Credential*> creds;
69     if (metadataProvider.resolve(creds, &criteria) == 0)
70         throw EncryptionException("No peer encryption credential found.");
71
72     const XMLCh* dataalg;
73     const XMLCh* keyalg;
74     const Credential* KEK = nullptr;
75
76     for (vector<const Credential*>::const_iterator c = creds.begin(); !KEK && c != creds.end(); ++c) {
77         // Try and find EncryptionMethod information surrounding the credential.
78         // All we're doing if they're present is setting algorithms where possible to
79         // the algorithms preferred by the credential, if we support them.
80         // The problem is that if we don't support them, the only case we can detect
81         // is if neither algorithm type is set *and* there's an EncryptionMethod present.
82         dataalg = keyalg = nullptr;
83         const MetadataCredentialContext* metaCtx = dynamic_cast<const MetadataCredentialContext*>((*c)->getCredentalContext());
84         if (metaCtx) {
85             const vector<EncryptionMethod*>& encMethods = metaCtx->getKeyDescriptor().getEncryptionMethods();
86             for (vector<EncryptionMethod*>::const_iterator meth = encMethods.begin(); meth != encMethods.end(); ++meth) {
87                 if ((*meth)->getAlgorithm()) {
88                     if (!dataalg && conf.isXMLAlgorithmSupported((*meth)->getAlgorithm(), XMLToolingConfig::ALGTYPE_ENCRYPT))
89                         dataalg = (*meth)->getAlgorithm();
90                     else if (!keyalg && conf.isXMLAlgorithmSupported((*meth)->getAlgorithm(), XMLToolingConfig::ALGTYPE_KEYENCRYPT))
91                         keyalg = (*meth)->getAlgorithm();
92                 }
93             }
94
95             if (!dataalg && !keyalg && !encMethods.empty()) {
96                 // We know nothing, and something was specified that we don't support, so keep looking.
97                 continue;
98             }
99         }
100
101         if (!keyalg && !(keyalg = Encrypter::getKeyTransportAlgorithm(*(*c), algorithm ? algorithm : dataalg))) {
102             // We can't derive a supported algorithm from the credential, so it will fail later anyway.
103             continue;
104         }
105
106         // Use this key.
107         KEK = *c;
108     }
109
110     if (!KEK)
111         throw EncryptionException("No supported peer encryption credential found.");
112
113     // Passed in algorithm takes precedence.
114     if (algorithm && *algorithm)
115         dataalg = algorithm;
116     if (!dataalg) {
117 #ifdef XSEC_OPENSSL_HAVE_AES
118         dataalg = DSIGConstants::s_unicodeStrURIAES256_CBC;
119 #else
120         dataalg = DSIGConstants::s_unicodeStrURI3DES_CBC;
121 #endif
122     }
123
124     Encrypter encrypter;
125     Encrypter::EncryptionParams ep(dataalg, nullptr, 0, nullptr, compact);
126     Encrypter::KeyEncryptionParams kep(*KEK, keyalg);
127     setEncryptedData(encrypter.encryptElement(xmlObject.marshall(), ep, &kep));
128 }
129
130 void EncryptedElementType::encrypt(
131     const EncryptableObject& xmlObject,
132     const vector< pair<const MetadataProvider*, MetadataCredentialCriteria*> >& recipients,
133     bool compact,
134     const XMLCh* algorithm
135     )
136 {
137     // With multiple recipients, we have to generate an encryption key and then multicast it,
138     // so we need to split the encryption and key wrapping steps.
139     if (!algorithm || !*algorithm) {
140 #ifdef XSEC_OPENSSL_HAVE_AES
141         algorithm = DSIGConstants::s_unicodeStrURIAES256_CBC;
142 #else
143         algorithm = DSIGConstants::s_unicodeStrURI3DES_CBC;
144 #endif
145     }
146
147     // Generate a random key.
148     unsigned char keyBuffer[32];
149     if (XSECPlatformUtils::g_cryptoProvider->getRandom(keyBuffer,32)<32)
150         throw EncryptionException("Unable to generate encryption key; was PRNG seeded?");
151     Encrypter encrypter;
152     Encrypter::EncryptionParams ep(algorithm, keyBuffer, 32, nullptr, compact);
153     setEncryptedData(encrypter.encryptElement(xmlObject.marshall(), ep));
154     getEncryptedData()->setId(SAMLConfig::getConfig().generateIdentifier());
155
156     // Generate a uniquely named KeyInfo.
157     KeyInfo* keyInfo = KeyInfoBuilder::buildKeyInfo();
158     getEncryptedData()->setKeyInfo(keyInfo);
159     KeyName* carriedName = KeyNameBuilder::buildKeyName();
160     keyInfo->getKeyNames().push_back(carriedName);
161     carriedName->setName(SAMLConfig::getConfig().generateIdentifier());
162
163     VectorOf(EncryptedKey) keys = getEncryptedKeys();
164
165     // Now we encrypt the key for each recipient.
166     for (vector< pair<const MetadataProvider*, MetadataCredentialCriteria*> >::const_iterator r = recipients.begin(); r!=recipients.end(); ++r) {
167         // Get key encryption keys to use.
168         r->second->setUsage(Credential::ENCRYPTION_CREDENTIAL);
169         vector<const Credential*> creds;
170         if (r->first->resolve(creds, r->second) == 0) {
171             auto_ptr_char name(dynamic_cast<const EntityDescriptor*>(r->second->getRole().getParent())->getEntityID());
172             logging::Category::getInstance(SAML_LOGCAT".Encryption").warn("No key encryption credentials found for (%s).", name.get());
173             continue;
174         }
175
176         const XMLCh* keyalg;
177         const Credential* KEK = nullptr;
178
179         for (vector<const Credential*>::const_iterator c = creds.begin(); !KEK && c != creds.end(); ++c) {
180             // Try and find EncryptionMethod information surrounding the credential.
181             // All we're doing if they're present is setting algorithms where possible to
182             // the algorithms preferred by the credential, if we support them.
183             // The problem is that if we don't support them, the only case we can detect
184             // is if neither algorithm type is set *and* there's an EncryptionMethod present.
185             keyalg = nullptr;
186             const MetadataCredentialContext* metaCtx = dynamic_cast<const MetadataCredentialContext*>((*c)->getCredentalContext());
187             if (metaCtx) {
188                 const vector<EncryptionMethod*>& encMethods = metaCtx->getKeyDescriptor().getEncryptionMethods();
189                 for (vector<EncryptionMethod*>::const_iterator meth = encMethods.begin(); meth != encMethods.end(); ++meth) {
190                     if ((*meth)->getAlgorithm()) {
191                         if (!keyalg && XMLToolingConfig::getConfig().isXMLAlgorithmSupported((*meth)->getAlgorithm(), XMLToolingConfig::ALGTYPE_KEYENCRYPT))
192                             keyalg = (*meth)->getAlgorithm();
193                     }
194                 }
195             }
196
197             if (!keyalg && !(keyalg = Encrypter::getKeyTransportAlgorithm(*(*c), algorithm))) {
198                 // We can't derive a supported algorithm from the credential, so it will fail later anyway.
199                 continue;
200             }
201
202             // Use this key.
203             KEK = *c;
204         }
205
206         if (!KEK) {
207             auto_ptr_char name(dynamic_cast<const EntityDescriptor*>(r->second->getRole().getParent())->getEntityID());
208             logging::Category::getInstance(SAML_LOGCAT".Encryption").warn("no supported key encryption credential found for (%s).", name.get());
209             continue;
210         }
211
212         // Encrypt the key and add it to the message.
213         Encrypter::KeyEncryptionParams kep(
214             *KEK, keyalg, dynamic_cast<const EntityDescriptor*>(r->second->getRole().getParent())->getEntityID()
215             );
216         EncryptedKey* encryptedKey = encrypter.encryptKey(keyBuffer, ep.m_keyBufferSize, kep, compact);
217         keys.push_back(encryptedKey);
218         if (keys.size() > 1) {
219             // Copy details from the other key.
220             encryptedKey->setCarriedKeyName(keys.front()->getCarriedKeyName()->cloneCarriedKeyName());
221             encryptedKey->setReferenceList(keys.front()->getReferenceList()->cloneReferenceList());
222         }
223         else {
224             // Attach the carried key name.
225             CarriedKeyName* carried = CarriedKeyNameBuilder::buildCarriedKeyName();
226             carried->setName(carriedName->getName());
227             encryptedKey->setCarriedKeyName(carried);
228
229             // Attach a back-reference to the data.
230             ReferenceList* reflist = ReferenceListBuilder::buildReferenceList();
231             encryptedKey->setReferenceList(reflist);
232             DataReference* dataref = DataReferenceBuilder::buildDataReference();
233             reflist->getDataReferences().push_back(dataref);
234             XMLCh* uri = new XMLCh[XMLString::stringLen(getEncryptedData()->getId()) + 2];
235             *uri = chPound;
236             *(uri+1) = chNull;
237             XMLString::catString(uri, getEncryptedData()->getId());
238             dataref->setURI(uri);
239             delete[] uri;
240         }
241     }
242 }
243
244 XMLObject* EncryptedElementType::decrypt(
245     const CredentialResolver& credResolver, const XMLCh* recipient, CredentialCriteria* criteria, bool requireAuthenticatedCipher
246     ) const
247 {
248     if (!getEncryptedData())
249         throw DecryptionException("No encrypted data present.");
250     opensaml::EncryptedKeyResolver ekr(*this);
251     Decrypter decrypter(&credResolver, criteria, &ekr, requireAuthenticatedCipher);
252     DOMDocumentFragment* frag = decrypter.decryptData(*getEncryptedData(), recipient);
253     if (frag->hasChildNodes() && frag->getFirstChild()==frag->getLastChild()) {
254         DOMNode* plaintext=frag->getFirstChild();
255         if (plaintext->getNodeType()==DOMNode::ELEMENT_NODE) {
256             // Import the tree into a new Document that we can bind to the unmarshalled object.
257             XercesJanitor<DOMDocument> newdoc(XMLToolingConfig::getConfig().getParser().newDocument());
258             DOMElement* treecopy;
259             try {
260                 treecopy = static_cast<DOMElement*>(newdoc->importNode(plaintext, true));
261             }
262             catch (XMLException& ex) {
263                 frag->release();
264                 auto_ptr_char temp(ex.getMessage());
265                 throw DecryptionException(
266                     string("Error importing decypted DOM into new document: ") + (temp.get() ? temp.get() : "no message")
267                     );
268             }
269             frag->release();
270             newdoc->appendChild(treecopy);
271             auto_ptr<XMLObject> ret(XMLObjectBuilder::buildOneFromElement(treecopy, true));
272             newdoc.release();
273             return ret.release();
274         }
275     }
276     frag->release();
277     throw DecryptionException("Decryption did not result in a single element.");
278 }