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