tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / xmlsecurity / source / gpg / xmlsignature_gpgimpl.cxx
blobfd2c5d1ebfbd78dee432f9a8c911175092d0e0eb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <cppuhelper/supportsservice.hxx>
24 #include <gpg/xmlsignature_gpgimpl.hxx>
26 #if defined _MSC_VER && defined __clang__
27 #pragma clang diagnostic push
28 #pragma clang diagnostic ignored "-Wundef"
29 #endif
30 #include <gpgme.h>
31 #if defined _MSC_VER && defined __clang__
32 #pragma clang diagnostic pop
33 #endif
34 #include <context.h>
35 #include <data.h>
36 #include <signingresult.h>
37 #include <importresult.h>
39 #include <xmlelementwrapper_xmlsecimpl.hxx>
40 #include <xmlsec/xmlstreamio.hxx>
41 #include <xmlsec/errorcallback.hxx>
42 #include <xmlsec/xmltree.h>
43 #include <xmlsec/base64.h>
44 #include <xmlsec/xmldsig.h>
45 #include <xmlsec/xmlsec.h>
47 #include "SecurityEnvironment.hxx"
49 using namespace css::uno;
50 using namespace css::lang;
51 using namespace css::xml::wrapper;
52 using namespace css::xml::crypto;
54 XMLSignature_GpgImpl::XMLSignature_GpgImpl() {
57 XMLSignature_GpgImpl::~XMLSignature_GpgImpl() {
60 /* XXMLSignature */
61 Reference< XXMLSignatureTemplate >
62 SAL_CALL XMLSignature_GpgImpl::generate(
63 const Reference< XXMLSignatureTemplate >& aTemplate ,
64 const Reference< XSecurityEnvironment >& aEnvironment
67 xmlSecDSigCtxPtr pDsigCtx = nullptr ;
68 xmlNodePtr pNode = nullptr ;
70 if( !aTemplate.is() )
71 throw RuntimeException() ;
73 if( !aEnvironment.is() )
74 throw RuntimeException() ;
76 //Get the xml node
77 Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
78 if( !xElement.is() ) {
79 throw RuntimeException() ;
82 XMLElementWrapper_XmlSecImpl* pElement =
83 dynamic_cast<XMLElementWrapper_XmlSecImpl*>(xElement.get());
84 if( pElement == nullptr ) {
85 throw RuntimeException() ;
88 pNode = pElement->getNativeElement() ;
90 //Get the stream/URI binding
91 Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
92 if( xUriBinding.is() ) {
93 //Register the stream input callbacks into libxml2
94 if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
95 throw RuntimeException() ;
98 //Get Keys Manager
99 SecurityEnvironmentGpg* pSecEnv =
100 dynamic_cast<SecurityEnvironmentGpg*>(aEnvironment.get());
101 if( pSecEnv == nullptr )
102 throw RuntimeException() ;
104 setErrorRecorder();
106 //Create Signature context
107 pDsigCtx = xmlSecDSigCtxCreate( nullptr ) ;
108 if( pDsigCtx == nullptr )
110 clearErrorRecorder();
111 return aTemplate;
114 // set intended operation to sign - several asserts inside libxmlsec
115 // wanting that for digest / transforms
116 pDsigCtx->operation = xmlSecTransformOperationSign;
118 // we default to SHA512 for all digests - nss crypto does not have it...
119 //pDsigCtx->defDigestMethodId = xmlSecTransformSha512Id;
121 // Calculate digest for all references
122 xmlNodePtr cur = xmlSecGetNextElementNode(pNode->children);
123 if( cur != nullptr )
124 cur = xmlSecGetNextElementNode(cur->children);
125 while( cur != nullptr )
127 // some of those children I suppose should be reference elements
128 if( xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs) )
130 xmlSecDSigReferenceCtxPtr pDsigRefCtx =
131 xmlSecDSigReferenceCtxCreate(pDsigCtx,
132 xmlSecDSigReferenceOriginSignedInfo);
133 if(pDsigRefCtx == nullptr)
134 throw RuntimeException();
136 // add this one to the list
137 if( xmlSecPtrListAdd(&(pDsigCtx->signedInfoReferences),
138 pDsigRefCtx) < 0 )
140 // TODO resource handling
141 xmlSecDSigReferenceCtxDestroy(pDsigRefCtx);
142 throw RuntimeException();
145 if( xmlSecDSigReferenceCtxProcessNode(pDsigRefCtx, cur) < 0 )
146 throw RuntimeException();
148 // final check - all good?
149 if(pDsigRefCtx->status != xmlSecDSigStatusSucceeded)
151 pDsigCtx->status = xmlSecDSigStatusInvalid;
152 return aTemplate; // TODO - harder error?
156 cur = xmlSecGetNextElementNode(cur->next);
159 // get me a digestible buffer from the signature template!
160 // -------------------------------------------------------
162 // run the transformations over SignedInfo element (first child of
163 // pNode)
164 xmlSecNodeSetPtr nodeset = nullptr;
165 cur = xmlSecGetNextElementNode(pNode->children);
166 // TODO assert that...
167 nodeset = xmlSecNodeSetGetChildren(pNode->doc, cur, 1, 0);
168 if(nodeset == nullptr)
169 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
171 if( xmlSecTransformCtxXmlExecute(&(pDsigCtx->transformCtx), nodeset) < 0 )
172 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
174 // now extract the keyid from PGPData
175 // walk xml tree to PGPData node - go to children, first is
176 // SignedInfo, 2nd is signaturevalue, 3rd is KeyInfo
177 // 1st child is PGPData, 1st grandchild is PGPKeyID
178 cur = xmlSecGetNextElementNode(pNode->children);
179 // TODO error handling
180 cur = xmlSecGetNextElementNode(cur->next);
181 cur = xmlSecGetNextElementNode(cur->next);
182 cur = xmlSecGetNextElementNode(cur->children);
183 // check that this is now PGPData
184 if(!xmlSecCheckNodeName(cur, xmlSecNodePGPData, xmlSecDSigNs))
185 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
186 // check that this is now PGPKeyID
187 cur = xmlSecGetNextElementNode(cur->children);
188 static const xmlChar xmlSecNodePGPKeyID[] = "PGPKeyID";
189 if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyID, xmlSecDSigNs))
190 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
192 GpgME::Context& rCtx=pSecEnv->getGpgContext();
193 rCtx.setKeyListMode(GPGME_KEYLIST_MODE_LOCAL);
194 GpgME::Error err;
195 xmlChar* pKey=xmlNodeGetContent(cur);
196 xmlSecSize nWritten;
197 int nRet = xmlSecBase64Decode_ex(pKey, reinterpret_cast<xmlSecByte*>(pKey), xmlStrlen(pKey), &nWritten);
198 if(nRet < 0)
199 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
201 rCtx.clearSigningKeys(); // tdf#108828 Clear keys from previous unsuccessful sessions
202 if( rCtx.addSigningKey(
203 rCtx.key(
204 reinterpret_cast<char*>(pKey), err, true)) )
205 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
207 xmlFree(pKey);
209 // good, ctx is setup now, let's sign the lot
210 GpgME::Data data_in(
211 reinterpret_cast<char*>(xmlSecBufferGetData(pDsigCtx->transformCtx.result)),
212 xmlSecBufferGetSize(pDsigCtx->transformCtx.result), false);
213 GpgME::Data data_out;
215 SAL_INFO("xmlsecurity.xmlsec.gpg", "Generating signature for: " << xmlSecBufferGetData(pDsigCtx->transformCtx.result));
217 // we base64-encode anyway
218 rCtx.setArmor(false);
219 GpgME::SigningResult sign_res=rCtx.sign(data_in, data_out,
220 GpgME::Detached);
221 off_t result = data_out.seek(0,SEEK_SET);
222 (void) result;
223 assert(result == 0);
224 int len=0, curr=0; char buf;
225 while( (curr=data_out.read(&buf, 1)) )
226 len += curr;
228 if(sign_res.error() || !len)
229 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
231 // write signed data to xml
232 xmlChar* signature = static_cast<xmlChar*>(xmlMalloc(len + 1));
233 if(signature == nullptr)
234 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
235 result = data_out.seek(0,SEEK_SET);
236 assert(result == 0);
237 if( data_out.read(signature, len) != len )
238 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
240 // conversion to base64
241 xmlChar* signatureEncoded=nullptr;
242 if( !(signatureEncoded=xmlSecBase64Encode(reinterpret_cast<xmlSecByte*>(signature), len, 79)) )
243 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
244 xmlFree(signature);
246 // walk xml tree to sign value node - go to children, first is
247 // SignedInfo, 2nd is signaturevalue
248 cur = xmlSecGetNextElementNode(pNode->children);
249 cur = xmlSecGetNextElementNode(cur->next);
251 // TODO some assert would be good...
252 xmlNodeSetContentLen(cur, signatureEncoded, xmlStrlen(signatureEncoded));
253 xmlFree(signatureEncoded);
255 aTemplate->setStatus(SecurityOperationStatus_OPERATION_SUCCEEDED);
257 // done
258 xmlSecDSigCtxDestroy( pDsigCtx ) ;
260 //Unregistered the stream/URI binding
261 if( xUriBinding.is() )
262 xmlUnregisterStreamInputCallbacks() ;
264 clearErrorRecorder();
265 return aTemplate ;
268 /* XXMLSignature */
269 Reference< XXMLSignatureTemplate >
270 SAL_CALL XMLSignature_GpgImpl::validate(
271 const Reference< XXMLSignatureTemplate >& aTemplate ,
272 const Reference< XXMLSecurityContext >& aSecurityCtx
274 xmlSecDSigCtxPtr pDsigCtx = nullptr ;
275 xmlNodePtr pNode = nullptr ;
277 if( !aTemplate.is() )
278 throw RuntimeException() ;
280 if( !aSecurityCtx.is() )
281 throw RuntimeException() ;
283 //Get the xml node
284 Reference< XXMLElementWrapper > xElement = aTemplate->getTemplate() ;
285 if( !xElement.is() )
286 throw RuntimeException() ;
288 XMLElementWrapper_XmlSecImpl* pElement =
289 dynamic_cast<XMLElementWrapper_XmlSecImpl*>(xElement.get());
290 if( pElement == nullptr )
291 throw RuntimeException() ;
293 pNode = pElement->getNativeElement() ;
295 //Get the stream/URI binding
296 Reference< XUriBinding > xUriBinding = aTemplate->getBinding() ;
297 if( xUriBinding.is() ) {
298 //Register the stream input callbacks into libxml2
299 if( xmlRegisterStreamInputCallbacks( xUriBinding ) < 0 )
300 throw RuntimeException() ;
303 setErrorRecorder();
305 sal_Int32 nSecurityEnvironment = aSecurityCtx->getSecurityEnvironmentNumber();
306 sal_Int32 i;
308 for (i=0; i<nSecurityEnvironment; ++i)
310 Reference< XSecurityEnvironment > aEnvironment = aSecurityCtx->getSecurityEnvironmentByIndex(i);
312 SecurityEnvironmentGpg* pSecEnv =
313 dynamic_cast<SecurityEnvironmentGpg*>(aEnvironment.get());
314 if( pSecEnv == nullptr )
315 throw RuntimeException() ;
317 // TODO figure out key from pSecEnv!
318 // unclear how/where that is transported in nss impl...
320 //Create Signature context
321 pDsigCtx = xmlSecDSigCtxCreate( nullptr ) ;
322 if( pDsigCtx == nullptr )
324 clearErrorRecorder();
325 return aTemplate;
328 // set intended operation to verify - several asserts inside libxmlsec
329 // wanting that for digest / transforms
330 pDsigCtx->operation = xmlSecTransformOperationVerify;
332 // reset status - to be set later
333 pDsigCtx->status = xmlSecDSigStatusUnknown;
335 // get me a digestible buffer from the SignatureInfo node!
336 // -------------------------------------------------------
338 // run the transformations - first child node is required to
339 // be SignatureInfo
340 xmlSecNodeSetPtr nodeset = nullptr;
341 xmlNodePtr cur = xmlSecGetNextElementNode(pNode->children);
342 // TODO assert that...
343 nodeset = xmlSecNodeSetGetChildren(pNode->doc, cur, 1, 0);
344 if(nodeset == nullptr)
345 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
347 // TODO assert we really have the SignatureInfo here?
348 if( xmlSecTransformCtxXmlExecute(&(pDsigCtx->transformCtx), nodeset) < 0 )
349 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
351 // Validate the template via gpgme
352 GpgME::Context& rCtx=pSecEnv->getGpgContext();
354 GpgME::Data data_text(
355 reinterpret_cast<char*>(xmlSecBufferGetData(pDsigCtx->transformCtx.result)),
356 xmlSecBufferGetSize(pDsigCtx->transformCtx.result), false);
358 SAL_INFO("xmlsecurity.xmlsec.gpg", "Validating SignatureInfo: " << xmlSecBufferGetData(pDsigCtx->transformCtx.result));
360 // walk xml tree to sign value node - go to children, first is
361 // SignedInfo, 2nd is signaturevalue
362 cur = xmlSecGetNextElementNode(pNode->children);
363 cur = xmlSecGetNextElementNode(cur->next);
365 if(!xmlSecCheckNodeName(cur, xmlSecNodeSignatureValue, xmlSecDSigNs))
366 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
367 xmlChar* pSignatureValue=xmlNodeGetContent(cur);
368 xmlSecSize nSigSize;
369 int nRet = xmlSecBase64Decode_ex(pSignatureValue, reinterpret_cast<xmlSecByte*>(pSignatureValue), xmlStrlen(pSignatureValue), &nSigSize);
370 if( nRet < 0)
371 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
373 GpgME::Data data_signature(
374 reinterpret_cast<char*>(pSignatureValue),
375 nSigSize, false);
377 GpgME::VerificationResult verify_res=rCtx.verifyDetachedSignature(
378 data_signature, data_text);
380 // TODO: needs some more error handling, needs checking _all_ signatures
381 if( verify_res.isNull() || verify_res.numSignatures() == 0
382 // there is at least 1 signature and it is anything else than fully valid
383 || ( (verify_res.numSignatures() > 0)
384 && verify_res.signature(0).status().encodedError() > 0 ) )
386 // let's try again, but this time import the public key
387 // payload (avoiding that in a first cut for being a bit
388 // speedier. also prevents all too easy poisoning/sha1
389 // fingerprint collision attacks)
391 // walk xml tree to PGPData node - go to children, first is
392 // SignedInfo, 2nd is signaturevalue, 3rd is KeyInfo
393 // 1st child is PGPData, 1st or 2nd grandchild is PGPKeyPacket
394 cur = xmlSecGetNextElementNode(pNode->children);
395 // TODO error handling
396 cur = xmlSecGetNextElementNode(cur->next);
397 cur = xmlSecGetNextElementNode(cur->next);
398 cur = xmlSecGetNextElementNode(cur->children);
399 // check that this is now PGPData
400 if(!xmlSecCheckNodeName(cur, xmlSecNodePGPData, xmlSecDSigNs))
401 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
402 // check that this is now PGPKeyPacket
403 cur = xmlSecGetNextElementNode(cur->children);
404 static const xmlChar xmlSecNodePGPKeyPacket[] = "PGPKeyPacket";
405 if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyPacket, xmlSecDSigNs))
407 // not this one, maybe the next?
408 cur = xmlSecGetNextElementNode(cur->next);
409 if(!xmlSecCheckNodeName(cur, xmlSecNodePGPKeyPacket, xmlSecDSigNs))
411 // ok, giving up
412 clearErrorRecorder();
413 xmlFree(pSignatureValue);
415 return aTemplate;
419 // got a key packet, import & re-validate
420 xmlChar* pKeyPacket=xmlNodeGetContent(cur);
421 xmlSecSize nKeyLen;
422 nRet = xmlSecBase64Decode_ex(pKeyPacket, reinterpret_cast<xmlSecByte*>(pKeyPacket), xmlStrlen(pKeyPacket), &nKeyLen);
423 if( nRet < 0)
424 throw RuntimeException(u"The GpgME library failed to initialize for the OpenPGP protocol."_ustr);
426 GpgME::Data data_key(
427 reinterpret_cast<char*>(pKeyPacket),
428 nKeyLen, false);
430 rCtx.importKeys(data_key);
431 xmlFree(pKeyPacket);
433 // and re-run (rewind text and signature streams to position 0)
434 (void)data_text.seek(0,SEEK_SET);
435 (void)data_signature.seek(0,SEEK_SET);
436 verify_res=rCtx.verifyDetachedSignature(data_signature, data_text);
438 // TODO: needs some more error handling, needs checking _all_ signatures
439 if( verify_res.isNull() || verify_res.numSignatures() == 0
440 // there is at least 1 signature and it is anything else than valid
441 || ( (verify_res.numSignatures() > 0)
442 && verify_res.signature(0).status().encodedError() > 0 ) )
444 clearErrorRecorder();
445 xmlFree(pSignatureValue);
447 return aTemplate;
451 xmlFree(pSignatureValue);
453 // now verify digest for all references
454 cur = xmlSecGetNextElementNode(pNode->children);
455 if( cur != nullptr )
456 cur = xmlSecGetNextElementNode(cur->children);
457 while( cur != nullptr )
459 // some of those children I suppose should be reference elements
460 if( xmlSecCheckNodeName(cur, xmlSecNodeReference, xmlSecDSigNs) )
462 xmlSecDSigReferenceCtxPtr pDsigRefCtx =
463 xmlSecDSigReferenceCtxCreate(pDsigCtx,
464 xmlSecDSigReferenceOriginSignedInfo);
465 if(pDsigRefCtx == nullptr)
466 throw RuntimeException();
468 // add this one to the list
469 if( xmlSecPtrListAdd(&(pDsigCtx->signedInfoReferences),
470 pDsigRefCtx) < 0 )
472 // TODO resource handling
473 xmlSecDSigReferenceCtxDestroy(pDsigRefCtx);
474 throw RuntimeException();
477 if( xmlSecDSigReferenceCtxProcessNode(pDsigRefCtx, cur) < 0 )
478 throw RuntimeException();
480 // final check - all good?
481 if(pDsigRefCtx->status != xmlSecDSigStatusSucceeded)
483 pDsigCtx->status = xmlSecDSigStatusInvalid;
484 return aTemplate; // TODO - harder error?
488 cur = xmlSecGetNextElementNode(cur->next);
491 // TODO - also verify manifest (only relevant for ooxml)?
492 aTemplate->setStatus(SecurityOperationStatus_OPERATION_SUCCEEDED);
494 // done
495 xmlSecDSigCtxDestroy( pDsigCtx ) ;
498 //Unregistered the stream/URI binding
499 if( xUriBinding.is() )
500 xmlUnregisterStreamInputCallbacks() ;
502 clearErrorRecorder();
503 return aTemplate ;
506 /* XServiceInfo */
507 OUString SAL_CALL XMLSignature_GpgImpl::getImplementationName() {
508 return impl_getImplementationName() ;
511 /* XServiceInfo */
512 sal_Bool SAL_CALL XMLSignature_GpgImpl::supportsService( const OUString& serviceName) {
513 return cppu::supportsService(this, serviceName);
516 /* XServiceInfo */
517 Sequence< OUString > SAL_CALL XMLSignature_GpgImpl::getSupportedServiceNames() {
518 return impl_getSupportedServiceNames() ;
521 //Helper for XServiceInfo
522 Sequence< OUString > XMLSignature_GpgImpl::impl_getSupportedServiceNames() {
523 Sequence<OUString> seqServiceNames { u"com.sun.star.xml.crypto.XMLSignature"_ustr };
524 return seqServiceNames ;
527 OUString XMLSignature_GpgImpl::impl_getImplementationName() {
528 return u"com.sun.star.xml.security.bridge.xmlsec.XMLSignature_GpgImpl"_ustr ;
531 //Helper for registry
532 Reference< XInterface > XMLSignature_GpgImpl::impl_createInstance( const Reference< XMultiServiceFactory >& ) {
533 return Reference< XInterface >( *new XMLSignature_GpgImpl ) ;
536 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */