Reducing header overuse, non-inlining selected methods (CPPOST-35).
[shibboleth/cpp-xmltooling.git] / xmltooling / signature / impl / XMLSecSignatureImpl.cpp
1 /*
2 *  Copyright 2001-2009 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  * XMLSecSignatureImpl.cpp
19  * 
20  * Signature class for XMLSec-based signature-handling
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "logging.h"
26 #include "impl/UnknownElement.h"
27 #include "security/Credential.h"
28 #include "signature/ContentReference.h"
29 #include "signature/KeyInfo.h"
30 #include "signature/Signature.h"
31 #include "util/NDC.h"
32 #include "util/XMLConstants.h"
33 #include "util/XMLHelper.h"
34
35 #include <xercesc/framework/MemBufInputSource.hpp>
36 #include <xercesc/framework/Wrapper4InputSource.hpp>
37 #include <xercesc/util/XMLUniDefs.hpp>
38 #include <xsec/dsig/DSIGKeyInfoX509.hpp>
39 #include <xsec/dsig/DSIGReference.hpp>
40 #include <xsec/enc/XSECCryptoException.hpp>
41 #include <xsec/framework/XSECAlgorithmHandler.hpp>
42 #include <xsec/framework/XSECAlgorithmMapper.hpp>
43 #include <xsec/framework/XSECException.hpp>
44 #include <xsec/transformers/TXFMSB.hpp>
45 #include <xsec/transformers/TXFMChain.hpp>
46 #include <xsec/transformers/TXFMOutputFile.hpp>
47
48 using namespace xmlsignature;
49 using namespace xmltooling::logging;
50 using namespace xmltooling;
51 using namespace xercesc;
52 using namespace std;
53 using xmlconstants::XMLSIG_NS;
54 using xmlconstants::XMLSIG_PREFIX;
55
56 namespace xmlsignature {
57
58 #if defined (_MSC_VER)
59     #pragma warning( push )
60     #pragma warning( disable : 4250 4251 )
61 #endif
62     
63     class XMLTOOL_DLLLOCAL XMLSecSignatureImpl : public UnknownElementImpl, public virtual Signature
64     {
65     public:
66         XMLSecSignatureImpl() : AbstractXMLObject(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
67             UnknownElementImpl(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
68             m_signature(NULL), m_c14n(NULL), m_sm(NULL), m_key(NULL), m_keyInfo(NULL), m_reference(NULL) {}
69         virtual ~XMLSecSignatureImpl();
70         
71         void releaseDOM() const;
72         void releaseChildrenDOM(bool propagateRelease=true) const {
73             if (m_keyInfo) {
74                 m_keyInfo->releaseDOM();
75                 if (propagateRelease)
76                     m_keyInfo->releaseChildrenDOM();
77             }
78         }
79         XMLObject* clone() const;
80         Signature* cloneSignature() const;
81
82         DOMElement* marshall(DOMDocument* document=NULL, const vector<Signature*>* sigs=NULL, const Credential* credential=NULL) const;
83         DOMElement* marshall(DOMElement* parentElement, const vector<Signature*>* sigs=NULL, const Credential* credential=NULL) const;
84         XMLObject* unmarshall(DOMElement* element, bool bindDocument=false);
85         
86         // Getters
87         const XMLCh* getCanonicalizationMethod() const {
88             if (m_signature)
89                 return canonicalizationMethod2UNICODEURI(m_signature->getCanonicalizationMethod());
90             return m_c14n ? m_c14n : DSIGConstants::s_unicodeStrURIEXC_C14N_NOC;
91         }
92         const XMLCh* getSignatureAlgorithm() const {
93             if (!m_sm && m_signature) {
94                 safeBuffer sURI;
95                 if (signatureHashMethod2URI(sURI, m_signature->getSignatureMethod(), m_signature->getHashMethod()) == false)
96                     return NULL;
97                 m_sm = XMLString::replicate(sURI.sbStrToXMLCh());
98             }
99             return m_sm ? m_sm : DSIGConstants::s_unicodeStrURIRSA_SHA1;
100         }
101
102         KeyInfo* getKeyInfo() const { return m_keyInfo; }
103         ContentReference* getContentReference() const { return m_reference; }
104         DSIGSignature* getXMLSignature() const { return m_signature; }
105         
106         // Setters
107         void setCanonicalizationMethod(const XMLCh* c14n) { m_c14n = prepareForAssignment(m_c14n,c14n); }
108         void setSignatureAlgorithm(const XMLCh* sm) { m_sm = prepareForAssignment(m_sm,sm); }
109         void setSigningKey(XSECCryptoKey* signingKey) {
110             delete m_key;
111             m_key=signingKey;
112         }
113         void setKeyInfo(KeyInfo* keyInfo) {
114             prepareForAssignment(m_keyInfo, keyInfo);
115             m_keyInfo=keyInfo;
116         }
117         void setContentReference(ContentReference* reference) {
118             delete m_reference;
119             m_reference=reference;
120         }
121         
122         void sign(const Credential* credential=NULL);
123
124     private:
125         mutable DSIGSignature* m_signature;
126         XMLCh* m_c14n;
127         mutable XMLCh* m_sm;
128         XSECCryptoKey* m_key;
129         mutable KeyInfo* m_keyInfo;
130         ContentReference* m_reference;
131     };
132
133 #if defined (_MSC_VER)
134     #pragma warning( pop )
135 #endif
136 };
137
138 ContentReference::ContentReference()
139 {
140 }
141
142 ContentReference::~ContentReference()
143 {
144 }
145
146 Signature::Signature()
147 {
148 }
149
150 Signature::~Signature()
151 {
152 }
153
154 XMLSecSignatureImpl::~XMLSecSignatureImpl()
155 {
156     // Release the associated signature.
157     if (m_signature)
158         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
159
160     XMLString::release(&m_c14n);
161     XMLString::release(&m_sm);
162     delete m_key;
163     delete m_keyInfo;
164     delete m_reference;
165 }
166
167 void XMLSecSignatureImpl::releaseDOM() const
168 {
169     if (getDOM()) {
170         // This should save off the DOM
171         UnknownElementImpl::releaseDOM();
172         
173         // Release the associated signature.
174         if (m_signature) {
175             XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
176             m_signature=NULL;
177         }
178     }
179 }
180
181 XMLObject* XMLSecSignatureImpl::clone() const
182 {
183     return cloneSignature();
184 }
185
186 Signature* XMLSecSignatureImpl::cloneSignature() const
187 {
188     XMLSecSignatureImpl* ret=new XMLSecSignatureImpl();
189
190     ret->m_c14n=XMLString::replicate(m_c14n);
191     ret->m_sm=XMLString::replicate(m_sm);
192     if (m_key)
193         ret->m_key=m_key->clone();
194     if (m_keyInfo)
195         ret->m_keyInfo=m_keyInfo->cloneKeyInfo();
196
197     // If there's no XML locally, serialize this object into the new one, otherwise just copy it over.
198     if (m_xml.empty())
199         serialize(ret->m_xml);
200     else
201         ret->m_xml=m_xml;
202
203     return ret;
204 }
205
206 void XMLSecSignatureImpl::sign(const Credential* credential)
207 {
208     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");
209     log.debug("applying signature");
210
211     if (!m_signature)
212         throw SignatureException("Only a marshalled Signature object can be signed.");
213     else if (!m_reference)
214         throw SignatureException("No ContentReference object set for signature creation.");
215
216     XSECCryptoKey* key = credential ? credential->getPrivateKey() : m_key;
217     if (!key)
218         throw SignatureException("No signing key available for signature creation.");
219
220     try {
221         log.debug("creating signature reference(s)");
222         DSIGReferenceList* refs = m_signature->getReferenceList();
223         while (refs && refs->getSize())
224             delete refs->removeReference(0);
225         m_reference->createReferences(m_signature);
226         
227         log.debug("computing signature");
228         m_signature->setSigningKey(key->clone());
229         m_signature->sign();
230     }
231     catch(XSECException& e) {
232         auto_ptr_char temp(e.getMsg());
233         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());
234     }
235     catch(XSECCryptoException& e) {
236         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());
237     }
238 }
239
240 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, const vector<Signature*>* sigs, const Credential* credential) const
241 {
242 #ifdef _DEBUG
243     xmltooling::NDC ndc("marshall");
244 #endif
245     
246     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
247     log.debug("marshalling ds:Signature");
248
249     DOMElement* cachedDOM=getDOM();
250     if (cachedDOM) {
251         if (!document || document==cachedDOM->getOwnerDocument()) {
252             log.debug("Signature has a usable cached DOM, reusing it");
253             if (document)
254                 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
255             releaseParentDOM(true);
256             return cachedDOM;
257         }
258         
259         // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
260         // Without an adoptNode option to maintain the child pointers, we have to either import the
261         // DOM while somehow reassigning all the nested references (which amounts to a complete
262         // *unmarshall* operation), or we just release the existing DOM and hope that we can get
263         // it back. This depends on all objects being able to preserve their DOM at all costs.
264         releaseChildrenDOM(true);
265         releaseDOM();
266     }
267     
268     // If we get here, we didn't have a usable DOM.
269     bool bindDocument=false;
270     if (m_xml.empty()) {
271         // Fresh signature, so we just create an empty one.
272         log.debug("creating empty Signature element");
273         if (!document) {
274             document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();
275             bindDocument=true;
276         }
277         DSIGSignature* temp=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
278         temp->setDSIGNSPrefix(XMLSIG_PREFIX);
279         cachedDOM=temp->createBlankSignature(document, getCanonicalizationMethod(), getSignatureAlgorithm());
280         m_signature = temp;
281     }
282     else {
283         // We need to reparse the XML we saved off into a new DOM.
284         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
285         Wrapper4InputSource dsrc(&src,false);
286         log.debug("parsing Signature XML back into DOM tree");
287         DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
288         if (document) {
289             // The caller insists on using his own document, so we now have to import the thing
290             // into it. Then we're just dumping the one we built.
291             log.debug("reimporting new DOM into caller-supplied document");
292             cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));
293             internalDoc->release();
294         }
295         else {
296             // We just bind the document we built to the object as the result.
297             cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());
298             document=internalDoc;
299             bindDocument=true;
300         }
301
302         // Now reload the signature from the DOM.
303         try {
304             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
305                 document, cachedDOM
306                 );
307             m_signature->load();
308         }
309         catch(XSECException& e) {
310             if (bindDocument)
311                 document->release();
312             auto_ptr_char temp(e.getMsg());
313             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
314         }
315         catch(XSECCryptoException& e) {
316             if (bindDocument)
317                 document->release();
318             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
319         }
320     }
321     
322     // Marshall KeyInfo data.
323     if (credential) {
324         delete m_keyInfo;
325         m_keyInfo = NULL;
326         m_keyInfo = credential->getKeyInfo();
327     }
328     if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
329         m_keyInfo->marshall(cachedDOM);
330     }
331
332     // Recache the DOM and clear the serialized copy.
333     setDocumentElement(document, cachedDOM);
334     log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not ");
335     setDOM(cachedDOM, bindDocument);
336     releaseParentDOM(true);
337     m_xml.erase();
338     return cachedDOM;
339 }
340
341 DOMElement* XMLSecSignatureImpl::marshall(DOMElement* parentElement, const vector<Signature*>* sigs, const Credential* credential) const
342 {
343 #ifdef _DEBUG
344     xmltooling::NDC ndc("marshall");
345 #endif
346     
347     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
348     log.debug("marshalling ds:Signature");
349
350     DOMElement* cachedDOM=getDOM();
351     if (cachedDOM) {
352         if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
353             log.debug("Signature has a usable cached DOM, reusing it");
354             if (parentElement!=cachedDOM->getParentNode()) {
355                 parentElement->appendChild(cachedDOM);
356                 releaseParentDOM(true);
357             }
358             return cachedDOM;
359         }
360         
361         // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
362         // Without an adoptNode option to maintain the child pointers, we have to either import the
363         // DOM while somehow reassigning all the nested references (which amounts to a complete
364         // *unmarshall* operation), or we just release the existing DOM and hope that we can get
365         // it back. This depends on all objects being able to preserve their DOM at all costs.
366         releaseChildrenDOM(true);
367         releaseDOM();
368     }
369     
370     // If we get here, we didn't have a usable DOM.
371     if (m_xml.empty()) {
372         // Fresh signature, so we just create an empty one.
373         log.debug("creating empty Signature element");
374         DSIGSignature* temp=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
375         temp->setDSIGNSPrefix(XMLSIG_PREFIX);
376         cachedDOM=temp->createBlankSignature(parentElement->getOwnerDocument(), getCanonicalizationMethod(), getSignatureAlgorithm());
377         m_signature = temp;
378     }
379     else {
380         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
381         Wrapper4InputSource dsrc(&src,false);
382         log.debug("parsing XML back into DOM tree");
383         DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
384         
385         log.debug("reimporting new DOM into caller-supplied document");
386         cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));
387         internalDoc->release();
388
389         // Now reload the signature from the DOM.
390         try {
391             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
392                 parentElement->getOwnerDocument(), cachedDOM
393                 );
394             m_signature->load();
395         }
396         catch(XSECException& e) {
397             auto_ptr_char temp(e.getMsg());
398             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
399         }
400         catch(XSECCryptoException& e) {
401             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
402         }
403     }
404
405     // Marshall KeyInfo data.
406     if (credential) {
407         delete m_keyInfo;
408         m_keyInfo = NULL;
409         m_keyInfo = credential->getKeyInfo();
410     }
411     if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
412         m_keyInfo->marshall(cachedDOM);
413     }
414
415     // Recache the DOM and clear the serialized copy.
416     parentElement->appendChild(cachedDOM);
417     log.debug("caching DOM for Signature");
418     setDOM(cachedDOM, false);
419     releaseParentDOM(true);
420     m_xml.erase();
421     return cachedDOM;
422 }
423
424 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)
425 {
426     Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature").debug("unmarshalling ds:Signature");
427
428     try {
429         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
430             element->getOwnerDocument(), element
431             );
432         m_signature->load();
433     }
434     catch(XSECException& e) {
435         auto_ptr_char temp(e.getMsg());
436         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
437     }
438     catch(XSECCryptoException& e) {
439         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
440     }
441
442     setDOM(element, bindDocument);
443     return this;
444 }
445
446 #ifdef HAVE_COVARIANT_RETURNS
447 Signature*
448 #else
449 XMLObject*
450 #endif
451 SignatureBuilder::buildObject(
452     const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const xmltooling::QName* schemaType
453     ) const
454 {
455     if (!XMLString::equals(nsURI,XMLSIG_NS) || !XMLString::equals(localName,Signature::LOCAL_NAME))
456         throw XMLObjectException("XMLSecSignatureBuilder requires standard Signature element name.");
457     return buildObject();
458 }
459
460 #ifdef HAVE_COVARIANT_RETURNS
461 Signature*
462 #else
463 XMLObject*
464 #endif
465 SignatureBuilder::buildObject() const
466 {
467     return new XMLSecSignatureImpl();
468 }
469
470 Signature* SignatureBuilder::buildSignature() {
471     const SignatureBuilder* b = dynamic_cast<const SignatureBuilder*>(
472         XMLObjectBuilder::getBuilder(xmltooling::QName(xmlconstants::XMLSIG_NS,Signature::LOCAL_NAME))
473         );
474     if (b) {
475 #ifdef HAVE_COVARIANT_RETURNS
476         return b->buildObject();
477 #else
478         return dynamic_cast<Signature*>(b->buildObject());
479 #endif
480     }
481     throw XMLObjectException("Unable to obtain typed builder for Signature.");
482 }
483
484 const XMLCh Signature::LOCAL_NAME[] = UNICODE_LITERAL_9(S,i,g,n,a,t,u,r,e);
485
486 // Raw signature methods.
487
488 unsigned int Signature::createRawSignature(
489     XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* in, unsigned int in_len, char* out, unsigned int out_len
490     )
491 {
492     try {
493         XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
494         if (!handler) {
495             auto_ptr_char alg(sigAlgorithm);
496             throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
497         }
498         
499         // Move input into a safeBuffer to source the transform chain.
500         safeBuffer sb,sbout;
501         sb.sbStrncpyIn(in,in_len);
502         TXFMSB* sbt = new TXFMSB(NULL);
503         sbt->setInput(sb, in_len);
504         TXFMChain tx(sbt);
505         
506         // Sign the chain.
507         unsigned int siglen = handler->signToSafeBuffer(&tx, sigAlgorithm, key, out_len-1, sbout);
508         if (siglen >= out_len)
509             throw SignatureException("Signature size exceeded output buffer size.");
510         
511         // Push all non-whitespace into buffer.
512         unsigned int ret_len = 0;
513         const char* source = sbout.rawCharBuffer();
514         while (siglen--) {
515             if (isspace(*source))
516                 ++source;
517             else {
518                 *out++ = *source++;
519                 ++ret_len;
520             }
521         }
522         *out = 0;
523         return ret_len;
524     }
525     catch(XSECException& e) {
526         auto_ptr_char temp(e.getMsg());
527         throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + temp.get());
528     }
529     catch(XSECCryptoException& e) {
530         throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + e.getMsg());
531     }
532 }
533
534 bool Signature::verifyRawSignature(
535     XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* signature, const char* in, unsigned int in_len
536     )
537 {
538     try {
539         XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
540         if (!handler) {
541             auto_ptr_char alg(sigAlgorithm);
542             throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
543         }
544         
545         // Move input into a safeBuffer to source the transform chain.
546         safeBuffer sb;
547         sb.sbStrncpyIn(in,in_len);
548         TXFMSB* sbt = new TXFMSB(NULL);
549         sbt->setInput(sb, in_len);
550         TXFMChain tx(sbt);
551         
552         // Verify the chain.
553         return handler->verifyBase64Signature(&tx, sigAlgorithm, signature, 0, key);
554     }
555     catch(XSECException& e) {
556         auto_ptr_char temp(e.getMsg());
557         throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + temp.get());
558     }
559     catch(XSECCryptoException& e) {
560         throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + e.getMsg());
561     }
562 }