OInterfaceContainerHelper3 needs to be thread-safe
[LibreOffice.git] / svl / source / crypto / cryptosign.cxx
blobcb7f01ea3700695c896bba873443db97b96033ac
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/.
8 */
10 #include <svl/cryptosign.hxx>
11 #include <svl/sigstruct.hxx>
12 #include <config_features.h>
14 #include <rtl/character.hxx>
15 #include <rtl/strbuf.hxx>
16 #include <rtl/string.hxx>
17 #include <sal/log.hxx>
18 #include <tools/datetime.hxx>
19 #include <tools/stream.hxx>
20 #include <comphelper/base64.hxx>
21 #include <comphelper/hash.hxx>
22 #include <comphelper/processfactory.hxx>
23 #include <comphelper/random.hxx>
24 #include <com/sun/star/security/XCertificate.hpp>
25 #include <com/sun/star/uno/Sequence.hxx>
26 #include <filter/msfilter/mscodec.hxx>
27 #include <o3tl/char16_t2wchar_t.hxx>
29 #if HAVE_FEATURE_NSS && !defined(_WIN32)
30 // NSS headers for PDF signing
31 #include <cert.h>
32 #include <hasht.h>
33 #include <secerr.h>
34 #include <sechash.h>
35 #include <cms.h>
36 #include <cmst.h>
38 // We use curl for RFC3161 time stamp requests
39 #include <curl/curl.h>
40 #endif
42 #ifdef _WIN32
43 // WinCrypt headers for PDF signing
44 // Note: this uses Windows 7 APIs and requires the relevant data types
45 #include <prewin.h>
46 #include <wincrypt.h>
47 #include <postwin.h>
48 #include <comphelper/windowserrorstring.hxx>
49 #endif
51 #if HAVE_FEATURE_NSS
53 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
54 #include <com/sun/star/xml/crypto/DigestID.hpp>
55 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
56 #include <mutex>
58 // Is this length truly the maximum possible, or just a number that
59 // seemed large enough when the author tested this (with some type of
60 // certificates)? I suspect the latter.
62 // Used to be 0x4000 = 16384, but a sample signed PDF (produced by
63 // some other software) provided by the customer has a signature
64 // content that is 30000 bytes. The SampleSignedPDFDocument.pdf from
65 // Adobe has one that is 21942 bytes. So let's be careful. Pity this
66 // can't be dynamic, at least not without restructuring the code. Also
67 // note that the checks in the code for this being too small
68 // apparently are broken, if this overflows you end up with an invalid
69 // PDF. Need to fix that.
71 #define MAX_SIGNATURE_CONTENT_LENGTH 50000
72 #endif
74 using namespace com::sun::star;
76 namespace {
78 #if HAVE_FEATURE_NSS
79 void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
81 static const char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
82 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
83 rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
84 rBuffer.append( pHexDigits[ nInt & 15 ] );
86 #endif // HAVE_FEATURE_NSS
88 #if HAVE_FEATURE_NSS && !defined(_WIN32)
90 char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg)
92 return PL_strdup(static_cast<char *>(arg));
95 // ASN.1 used in the (much simpler) time stamp request. From RFC3161
96 // and other sources.
99 AlgorithmIdentifier ::= SEQUENCE {
100 algorithm OBJECT IDENTIFIER,
101 parameters ANY DEFINED BY algorithm OPTIONAL }
102 -- contains a value of the type
103 -- registered for use with the
104 -- algorithm object identifier value
106 MessageImprint ::= SEQUENCE {
107 hashAlgorithm AlgorithmIdentifier,
108 hashedMessage OCTET STRING }
111 struct MessageImprint {
112 SECAlgorithmID hashAlgorithm;
113 SECItem hashedMessage;
117 Extension ::= SEQUENCE {
118 extnID OBJECT IDENTIFIER,
119 critical BOOLEAN DEFAULT FALSE,
120 extnValue OCTET STRING }
123 struct Extension {
124 SECItem extnID;
125 SECItem critical;
126 SECItem extnValue;
130 Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
134 TSAPolicyId ::= OBJECT IDENTIFIER
136 TimeStampReq ::= SEQUENCE {
137 version INTEGER { v1(1) },
138 messageImprint MessageImprint,
139 --a hash algorithm OID and the hash value of the data to be
140 --time-stamped
141 reqPolicy TSAPolicyId OPTIONAL,
142 nonce INTEGER OPTIONAL,
143 certReq BOOLEAN DEFAULT FALSE,
144 extensions [0] IMPLICIT Extensions OPTIONAL }
147 struct TimeStampReq {
148 SECItem version;
149 MessageImprint messageImprint;
150 SECItem reqPolicy;
151 SECItem nonce;
152 SECItem certReq;
153 Extension *extensions;
157 * General name, defined by RFC 3280.
159 struct GeneralName
161 CERTName name;
165 * List of general names (only one for now), defined by RFC 3280.
167 struct GeneralNames
169 GeneralName names;
173 * Supplies different fields to identify a certificate, defined by RFC 5035.
175 struct IssuerSerial
177 GeneralNames issuer;
178 SECItem serialNumber;
182 * Supplies different fields that are used to identify certificates, defined by
183 * RFC 5035.
185 struct ESSCertIDv2
187 SECAlgorithmID hashAlgorithm;
188 SECItem certHash;
189 IssuerSerial issuerSerial;
193 * This attribute uses the ESSCertIDv2 structure, defined by RFC 5035.
195 struct SigningCertificateV2
197 ESSCertIDv2** certs;
199 SigningCertificateV2()
200 : certs(nullptr)
206 * GeneralName ::= CHOICE {
207 * otherName [0] OtherName,
208 * rfc822Name [1] IA5String,
209 * dNSName [2] IA5String,
210 * x400Address [3] ORAddress,
211 * directoryName [4] Name,
212 * ediPartyName [5] EDIPartyName,
213 * uniformResourceIdentifier [6] IA5String,
214 * iPAddress [7] OCTET STRING,
215 * registeredID [8] OBJECT IDENTIFIER
218 const SEC_ASN1Template GeneralNameTemplate[] =
220 {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(GeneralName)},
221 {SEC_ASN1_INLINE, offsetof(GeneralName, name), CERT_NameTemplate, 0},
222 {0, 0, nullptr, 0}
226 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
228 const SEC_ASN1Template GeneralNamesTemplate[] =
230 {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(GeneralNames)},
231 {SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 4, offsetof(GeneralNames, names), GeneralNameTemplate, 0},
232 {0, 0, nullptr, 0}
236 * IssuerSerial ::= SEQUENCE {
237 * issuer GeneralNames,
238 * serialNumber CertificateSerialNumber
241 const SEC_ASN1Template IssuerSerialTemplate[] =
243 {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(IssuerSerial)},
244 {SEC_ASN1_INLINE, offsetof(IssuerSerial, issuer), GeneralNamesTemplate, 0},
245 {SEC_ASN1_INTEGER, offsetof(IssuerSerial, serialNumber), nullptr, 0},
246 {0, 0, nullptr, 0}
251 * Hash ::= OCTET STRING
253 * ESSCertIDv2 ::= SEQUENCE {
254 * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256},
255 * certHash Hash,
256 * issuerSerial IssuerSerial OPTIONAL
260 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
262 const SEC_ASN1Template ESSCertIDv2Template[] =
264 {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(ESSCertIDv2)},
265 {SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(ESSCertIDv2, hashAlgorithm), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate), 0},
266 {SEC_ASN1_OCTET_STRING, offsetof(ESSCertIDv2, certHash), nullptr, 0},
267 {SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(ESSCertIDv2, issuerSerial), IssuerSerialTemplate, 0},
268 {0, 0, nullptr, 0}
272 * SigningCertificateV2 ::= SEQUENCE {
275 const SEC_ASN1Template SigningCertificateV2Template[] =
277 {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(SigningCertificateV2)},
278 {SEC_ASN1_SEQUENCE_OF, offsetof(SigningCertificateV2, certs), ESSCertIDv2Template, 0},
279 {0, 0, nullptr, 0}
282 struct PKIStatusInfo {
283 SECItem status;
284 SECItem statusString;
285 SECItem failInfo;
288 const SEC_ASN1Template PKIStatusInfo_Template[] =
290 { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(PKIStatusInfo) },
291 { SEC_ASN1_INTEGER, offsetof(PKIStatusInfo, status), nullptr, 0 },
292 { SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, statusString), nullptr, 0 },
293 { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, failInfo), nullptr, 0 },
294 { 0, 0, nullptr, 0 }
297 const SEC_ASN1Template Any_Template[] =
299 { SEC_ASN1_ANY, 0, nullptr, sizeof(SECItem) }
302 struct TimeStampResp {
303 PKIStatusInfo status;
304 SECItem timeStampToken;
307 const SEC_ASN1Template TimeStampResp_Template[] =
309 { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(TimeStampResp) },
310 { SEC_ASN1_INLINE, offsetof(TimeStampResp, status), PKIStatusInfo_Template, 0 },
311 { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, offsetof(TimeStampResp, timeStampToken), Any_Template, 0 },
312 { 0, 0, nullptr, 0 }
315 const SEC_ASN1Template MessageImprint_Template[] =
317 { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(MessageImprint) },
318 { SEC_ASN1_INLINE, offsetof(MessageImprint, hashAlgorithm), SECOID_AlgorithmIDTemplate, 0 },
319 { SEC_ASN1_OCTET_STRING, offsetof(MessageImprint, hashedMessage), nullptr, 0 },
320 { 0, 0, nullptr, 0 }
323 const SEC_ASN1Template Extension_Template[] =
325 { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(Extension) },
326 { SEC_ASN1_OBJECT_ID, offsetof(Extension, extnID), nullptr, 0 },
327 { SEC_ASN1_BOOLEAN, offsetof(Extension, critical), nullptr, 0 },
328 { SEC_ASN1_OCTET_STRING, offsetof(Extension, extnValue), nullptr, 0 },
329 { 0, 0, nullptr, 0 }
332 const SEC_ASN1Template Extensions_Template[] =
334 { SEC_ASN1_SEQUENCE_OF, 0, Extension_Template, 0 }
337 const SEC_ASN1Template TimeStampReq_Template[] =
339 { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(TimeStampReq) },
340 { SEC_ASN1_INTEGER, offsetof(TimeStampReq, version), nullptr, 0 },
341 { SEC_ASN1_INLINE, offsetof(TimeStampReq, messageImprint), MessageImprint_Template, 0 },
342 { SEC_ASN1_OBJECT_ID | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, reqPolicy), nullptr, 0 },
343 { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, nonce), nullptr, 0 },
344 { SEC_ASN1_BOOLEAN | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, certReq), nullptr, 0 },
345 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(TimeStampReq, extensions), Extensions_Template, 0 },
346 { 0, 0, nullptr, 0 }
349 size_t AppendToBuffer(char const *ptr, size_t size, size_t nmemb, void *userdata)
351 OStringBuffer *pBuffer = static_cast<OStringBuffer*>(userdata);
352 pBuffer->append(ptr, size*nmemb);
354 return size*nmemb;
357 OUString PKIStatusToString(int n)
359 switch (n)
361 case 0: return "granted";
362 case 1: return "grantedWithMods";
363 case 2: return "rejection";
364 case 3: return "waiting";
365 case 4: return "revocationWarning";
366 case 5: return "revocationNotification";
367 default: return "unknown (" + OUString::number(n) + ")";
371 OUString PKIStatusInfoToString(const PKIStatusInfo& rStatusInfo)
373 OUString result = "{status=";
374 if (rStatusInfo.status.len == 1)
375 result += PKIStatusToString(rStatusInfo.status.data[0]);
376 else
377 result += "unknown (len=" + OUString::number(rStatusInfo.status.len);
379 // FIXME: Perhaps look at rStatusInfo.statusString.data but note
380 // that we of course can't assume it contains proper UTF-8. After
381 // all, it is data from an external source. Also, RFC3161 claims
382 // it should be a SEQUENCE (1..MAX) OF UTF8String, but another
383 // source claimed it would be a single UTF8String, hmm?
385 // FIXME: Worth it to decode failInfo to cleartext, probably not at least as long as this is only for a SAL_INFO
387 result += "}";
389 return result;
392 // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are
393 // not exported from libsmime, so copy them here. Sigh.
395 SECStatus
396 my_SEC_StringToOID(SECItem *to, const char *from, PRUint32 len)
398 PRUint32 decimal_numbers = 0;
399 PRUint32 result_bytes = 0;
400 SECStatus rv;
401 PRUint8 result[1024];
403 static const PRUint32 max_decimal = 0xffffffff / 10;
404 static const char OIDstring[] = {"OID."};
406 if (!from || !to) {
407 PORT_SetError(SEC_ERROR_INVALID_ARGS);
408 return SECFailure;
410 if (!len) {
411 len = PL_strlen(from);
413 if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4)) {
414 from += 4; /* skip leading "OID." if present */
415 len -= 4;
417 if (!len) {
418 bad_data:
419 PORT_SetError(SEC_ERROR_BAD_DATA);
420 return SECFailure;
422 do {
423 PRUint32 decimal = 0;
424 while (len > 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from))) {
425 PRUint32 addend = *from++ - '0';
426 --len;
427 if (decimal > max_decimal) /* overflow */
428 goto bad_data;
429 decimal = (decimal * 10) + addend;
430 if (decimal < addend) /* overflow */
431 goto bad_data;
433 if (len != 0 && *from != '.') {
434 goto bad_data;
436 if (decimal_numbers == 0) {
437 if (decimal > 2)
438 goto bad_data;
439 result[0] = decimal * 40;
440 result_bytes = 1;
441 } else if (decimal_numbers == 1) {
442 if (decimal > 40)
443 goto bad_data;
444 result[0] += decimal;
445 } else {
446 /* encode the decimal number, */
447 PRUint8 * rp;
448 PRUint32 num_bytes = 0;
449 PRUint32 tmp = decimal;
450 while (tmp) {
451 num_bytes++;
452 tmp >>= 7;
454 if (!num_bytes )
455 ++num_bytes; /* use one byte for a zero value */
456 if (num_bytes + result_bytes > sizeof result)
457 goto bad_data;
458 tmp = num_bytes;
459 rp = result + result_bytes - 1;
460 rp[tmp] = static_cast<PRUint8>(decimal & 0x7f);
461 decimal >>= 7;
462 while (--tmp > 0) {
463 rp[tmp] = static_cast<PRUint8>(decimal | 0x80);
464 decimal >>= 7;
466 result_bytes += num_bytes;
468 ++decimal_numbers;
469 if (len > 0) { /* skip trailing '.' */
470 ++from;
471 --len;
473 } while (len > 0);
474 /* now result contains result_bytes of data */
475 if (to->data && to->len >= result_bytes) {
476 to->len = result_bytes;
477 PORT_Memcpy(to->data, result, to->len);
478 rv = SECSuccess;
479 } else {
480 SECItem result_item = {siBuffer, nullptr, 0 };
481 result_item.data = result;
482 result_item.len = result_bytes;
483 rv = SECITEM_CopyItem(nullptr, to, &result_item);
485 return rv;
488 NSSCMSAttribute *
489 my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
491 SECOidData *oid;
492 NSSCMSAttribute *attr1, *attr2;
494 if (attrs == nullptr)
495 return nullptr;
497 oid = SECOID_FindOIDByTag(oidtag);
498 if (oid == nullptr)
499 return nullptr;
501 while ((attr1 = *attrs++) != nullptr) {
502 if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
503 oid->oid.data,
504 oid->oid.len) == 0)
505 break;
508 if (attr1 == nullptr)
509 return nullptr;
511 if (!only)
512 return attr1;
514 while ((attr2 = *attrs++) != nullptr) {
515 if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
516 oid->oid.data,
517 oid->oid.len) == 0)
518 break;
521 if (attr2 != nullptr)
522 return nullptr;
524 return attr1;
527 SECStatus
528 my_NSS_CMSArray_Add(PLArenaPool *poolp, void ***array, void *obj)
530 int n = 0;
531 void **dest;
533 PORT_Assert(array != NULL);
534 if (array == nullptr)
535 return SECFailure;
537 if (*array == nullptr) {
538 dest = static_cast<void **>(PORT_ArenaAlloc(poolp, 2 * sizeof(void *)));
539 } else {
540 void **p = *array;
541 while (*p++)
542 n++;
543 dest = static_cast<void **>(PORT_ArenaGrow (poolp,
544 *array,
545 (n + 1) * sizeof(void *),
546 (n + 2) * sizeof(void *)));
549 if (dest == nullptr)
550 return SECFailure;
552 dest[n] = obj;
553 dest[n+1] = nullptr;
554 *array = dest;
555 return SECSuccess;
558 SECOidTag
559 my_NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
561 SECOidData *typetag;
563 typetag = SECOID_FindOID(&(attr->type));
564 if (typetag == nullptr)
565 return SEC_OID_UNKNOWN;
567 return typetag->offset;
570 SECStatus
571 my_NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr)
573 NSSCMSAttribute *oattr;
574 void *mark;
575 SECOidTag type;
577 mark = PORT_ArenaMark(poolp);
579 /* find oidtag of attr */
580 type = my_NSS_CMSAttribute_GetType(attr);
582 /* see if we have one already */
583 oattr = my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
584 PORT_Assert (oattr == NULL);
585 if (oattr != nullptr)
586 goto loser; /* XXX or would it be better to replace it? */
588 /* no, shove it in */
589 if (my_NSS_CMSArray_Add(poolp, reinterpret_cast<void ***>(attrs), static_cast<void *>(attr)) != SECSuccess)
590 goto loser;
592 PORT_ArenaUnmark(poolp, mark);
593 return SECSuccess;
595 loser:
596 PORT_ArenaRelease(poolp, mark);
597 return SECFailure;
600 SECStatus
601 my_NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
603 return my_NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
606 SECStatus
607 my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
609 return my_NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
612 NSSCMSMessage *CreateCMSMessage(const PRTime* time,
613 NSSCMSSignedData **cms_sd,
614 NSSCMSSignerInfo **cms_signer,
615 CERTCertificate *cert,
616 SECItem *digest)
618 NSSCMSMessage *result = NSS_CMSMessage_Create(nullptr);
619 if (!result)
621 SAL_WARN("svl.crypto", "NSS_CMSMessage_Create failed");
622 return nullptr;
625 *cms_sd = NSS_CMSSignedData_Create(result);
626 if (!*cms_sd)
628 SAL_WARN("svl.crypto", "NSS_CMSSignedData_Create failed");
629 NSS_CMSMessage_Destroy(result);
630 return nullptr;
633 NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(result);
634 if (NSS_CMSContentInfo_SetContent_SignedData(result, cms_cinfo, *cms_sd) != SECSuccess)
636 SAL_WARN("svl.crypto", "NSS_CMSContentInfo_SetContent_SignedData failed");
637 NSS_CMSSignedData_Destroy(*cms_sd);
638 NSS_CMSMessage_Destroy(result);
639 return nullptr;
642 cms_cinfo = NSS_CMSSignedData_GetContentInfo(*cms_sd);
644 // Attach NULL data as detached data
645 if (NSS_CMSContentInfo_SetContent_Data(result, cms_cinfo, nullptr, PR_TRUE) != SECSuccess)
647 SAL_WARN("svl.crypto", "NSS_CMSContentInfo_SetContent_Data failed");
648 NSS_CMSSignedData_Destroy(*cms_sd);
649 NSS_CMSMessage_Destroy(result);
650 return nullptr;
653 *cms_signer = NSS_CMSSignerInfo_Create(result, cert, SEC_OID_SHA256);
654 if (!*cms_signer)
656 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_Create failed");
657 NSS_CMSSignedData_Destroy(*cms_sd);
658 NSS_CMSMessage_Destroy(result);
659 return nullptr;
662 if (time && NSS_CMSSignerInfo_AddSigningTime(*cms_signer, *time) != SECSuccess)
664 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_AddSigningTime failed");
665 NSS_CMSSignedData_Destroy(*cms_sd);
666 NSS_CMSMessage_Destroy(result);
667 return nullptr;
670 if (NSS_CMSSignerInfo_IncludeCerts(*cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess)
672 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_IncludeCerts failed");
673 NSS_CMSSignedData_Destroy(*cms_sd);
674 NSS_CMSMessage_Destroy(result);
675 return nullptr;
678 if (NSS_CMSSignedData_AddCertificate(*cms_sd, cert) != SECSuccess)
680 SAL_WARN("svl.crypto", "NSS_CMSSignedData_AddCertificate failed");
681 NSS_CMSSignedData_Destroy(*cms_sd);
682 NSS_CMSMessage_Destroy(result);
683 return nullptr;
686 if (NSS_CMSSignedData_AddSignerInfo(*cms_sd, *cms_signer) != SECSuccess)
688 SAL_WARN("svl.crypto", "NSS_CMSSignedData_AddSignerInfo failed");
689 NSS_CMSSignedData_Destroy(*cms_sd);
690 NSS_CMSMessage_Destroy(result);
691 return nullptr;
694 if (NSS_CMSSignedData_SetDigestValue(*cms_sd, SEC_OID_SHA256, digest) != SECSuccess)
696 SAL_WARN("svl.crypto", "NSS_CMSSignedData_SetDigestValue failed");
697 NSS_CMSSignedData_Destroy(*cms_sd);
698 NSS_CMSMessage_Destroy(result);
699 return nullptr;
702 return result;
705 #endif // HAVE_FEATURE_NSS && !_WIN32
707 } // Anonymous namespace
709 #ifdef _WIN32
710 namespace
713 /// Counts how many bytes are needed to encode a given length.
714 size_t GetDERLengthOfLength(size_t nLength)
716 size_t nRet = 1;
718 if(nLength > 127)
720 while (nLength >> (nRet * 8))
721 ++nRet;
722 // Long form means one additional byte: the length of the length and
723 // the length itself.
724 ++nRet;
726 return nRet;
729 /// Writes the length part of the header.
730 void WriteDERLength(SvStream& rStream, size_t nLength)
732 size_t nLengthOfLength = GetDERLengthOfLength(nLength);
733 if (nLengthOfLength == 1)
735 // We can use the short form.
736 rStream.WriteUInt8(nLength);
737 return;
740 // 0x80 means that the we use the long form: the first byte is the length
741 // of length with the highest bit set to 1, not the actual length.
742 rStream.WriteUInt8(0x80 | (nLengthOfLength - 1));
743 for (size_t i = 1; i < nLengthOfLength; ++i)
744 rStream.WriteUInt8(nLength >> ((nLengthOfLength - i - 1) * 8));
747 const unsigned nASN1_INTEGER = 0x02;
748 const unsigned nASN1_OCTET_STRING = 0x04;
749 const unsigned nASN1_NULL = 0x05;
750 const unsigned nASN1_OBJECT_IDENTIFIER = 0x06;
751 const unsigned nASN1_SEQUENCE = 0x10;
752 /// An explicit tag on a constructed value.
753 const unsigned nASN1_TAGGED_CONSTRUCTED = 0xa0;
754 const unsigned nASN1_CONSTRUCTED = 0x20;
756 /// Create payload for the 'signing-certificate' signed attribute.
757 bool CreateSigningCertificateAttribute(void const * pDerEncoded, int nDerEncoded, PCCERT_CONTEXT pCertContext, SvStream& rEncodedCertificate)
759 // CryptEncodeObjectEx() does not support encoding arbitrary ASN.1
760 // structures, like SigningCertificateV2 from RFC 5035, so let's build it
761 // manually.
763 // Count the certificate hash and put it to aHash.
764 // 2.16.840.1.101.3.4.2.1, i.e. sha256.
765 std::vector<unsigned char> aSHA256{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
767 HCRYPTPROV hProv = 0;
768 if (!CryptAcquireContextW(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
770 SAL_WARN("svl.crypto", "CryptAcquireContext() failed");
771 return false;
774 HCRYPTHASH hHash = 0;
775 if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
777 SAL_WARN("svl.crypto", "CryptCreateHash() failed");
778 return false;
781 if (!CryptHashData(hHash, static_cast<const BYTE*>(pDerEncoded), nDerEncoded, 0))
783 SAL_WARN("svl.crypto", "CryptHashData() failed");
784 return false;
787 DWORD nHash = 0;
788 if (!CryptGetHashParam(hHash, HP_HASHVAL, nullptr, &nHash, 0))
790 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash length");
791 return false;
794 std::vector<unsigned char> aHash(nHash);
795 if (!CryptGetHashParam(hHash, HP_HASHVAL, aHash.data(), &nHash, 0))
797 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash");
798 return false;
801 CryptDestroyHash(hHash);
802 CryptReleaseContext(hProv, 0);
804 // Collect info for IssuerSerial.
805 BYTE* pIssuer = pCertContext->pCertInfo->Issuer.pbData;
806 DWORD nIssuer = pCertContext->pCertInfo->Issuer.cbData;
807 BYTE* pSerial = pCertContext->pCertInfo->SerialNumber.pbData;
808 DWORD nSerial = pCertContext->pCertInfo->SerialNumber.cbData;
809 // pSerial is LE, aSerial is BE.
810 std::vector<BYTE> aSerial(nSerial);
811 for (size_t i = 0; i < nSerial; ++i)
812 aSerial[i] = *(pSerial + nSerial - i - 1);
814 // We now have all the info to count the lengths.
815 // The layout of the payload is:
816 // SEQUENCE: SigningCertificateV2
817 // SEQUENCE: SEQUENCE OF ESSCertIDv2
818 // SEQUENCE: ESSCertIDv2
819 // SEQUENCE: AlgorithmIdentifier
820 // OBJECT: algorithm
821 // NULL: parameters
822 // OCTET STRING: certHash
823 // SEQUENCE: IssuerSerial
824 // SEQUENCE: GeneralNames
825 // cont [ 4 ]: Name
826 // SEQUENCE: Issuer blob
827 // INTEGER: CertificateSerialNumber
829 size_t nAlgorithm = 1 + GetDERLengthOfLength(aSHA256.size()) + aSHA256.size();
830 size_t nParameters = 1 + GetDERLengthOfLength(1);
831 size_t nAlgorithmIdentifier = 1 + GetDERLengthOfLength(nAlgorithm + nParameters) + nAlgorithm + nParameters;
832 size_t nCertHash = 1 + GetDERLengthOfLength(aHash.size()) + aHash.size();
833 size_t nName = 1 + GetDERLengthOfLength(nIssuer) + nIssuer;
834 size_t nGeneralNames = 1 + GetDERLengthOfLength(nName) + nName;
835 size_t nCertificateSerialNumber = 1 + GetDERLengthOfLength(nSerial) + nSerial;
836 size_t nIssuerSerial = 1 + GetDERLengthOfLength(nGeneralNames + nCertificateSerialNumber) + nGeneralNames + nCertificateSerialNumber;
837 size_t nESSCertIDv2 = 1 + GetDERLengthOfLength(nAlgorithmIdentifier + nCertHash + nIssuerSerial) + nAlgorithmIdentifier + nCertHash + nIssuerSerial;
838 size_t nESSCertIDv2s = 1 + GetDERLengthOfLength(nESSCertIDv2) + nESSCertIDv2;
840 // Write SigningCertificateV2.
841 rEncodedCertificate.WriteUInt8(nASN1_SEQUENCE | nASN1_CONSTRUCTED);
842 WriteDERLength(rEncodedCertificate, nESSCertIDv2s);
843 // Write SEQUENCE OF ESSCertIDv2.
844 rEncodedCertificate.WriteUInt8(nASN1_SEQUENCE | nASN1_CONSTRUCTED);
845 WriteDERLength(rEncodedCertificate, nESSCertIDv2);
846 // Write ESSCertIDv2.
847 rEncodedCertificate.WriteUInt8(nASN1_SEQUENCE | nASN1_CONSTRUCTED);
848 WriteDERLength(rEncodedCertificate, nAlgorithmIdentifier + nCertHash + nIssuerSerial);
849 // Write AlgorithmIdentifier.
850 rEncodedCertificate.WriteUInt8(nASN1_SEQUENCE | nASN1_CONSTRUCTED);
851 WriteDERLength(rEncodedCertificate, nAlgorithm + nParameters);
852 // Write algorithm.
853 rEncodedCertificate.WriteUInt8(nASN1_OBJECT_IDENTIFIER);
854 WriteDERLength(rEncodedCertificate, aSHA256.size());
855 rEncodedCertificate.WriteBytes(aSHA256.data(), aSHA256.size());
856 // Write parameters.
857 rEncodedCertificate.WriteUInt8(nASN1_NULL);
858 rEncodedCertificate.WriteUInt8(0);
859 // Write certHash.
860 rEncodedCertificate.WriteUInt8(nASN1_OCTET_STRING);
861 WriteDERLength(rEncodedCertificate, aHash.size());
862 rEncodedCertificate.WriteBytes(aHash.data(), aHash.size());
863 // Write IssuerSerial.
864 rEncodedCertificate.WriteUInt8(nASN1_SEQUENCE | nASN1_CONSTRUCTED);
865 WriteDERLength(rEncodedCertificate, nGeneralNames + nCertificateSerialNumber);
866 // Write GeneralNames.
867 rEncodedCertificate.WriteUInt8(nASN1_SEQUENCE | nASN1_CONSTRUCTED);
868 WriteDERLength(rEncodedCertificate, nName);
869 // Write Name.
870 rEncodedCertificate.WriteUInt8(nASN1_TAGGED_CONSTRUCTED | 4);
871 WriteDERLength(rEncodedCertificate, nIssuer);
872 rEncodedCertificate.WriteBytes(pIssuer, nIssuer);
873 // Write CertificateSerialNumber.
874 rEncodedCertificate.WriteUInt8(nASN1_INTEGER);
875 WriteDERLength(rEncodedCertificate, nSerial);
876 rEncodedCertificate.WriteBytes(aSerial.data(), aSerial.size());
878 return true;
880 } // anonymous namespace
881 #endif //_WIN32
883 namespace svl::crypto {
885 static int AsHex(char ch)
887 int nRet = 0;
888 if (rtl::isAsciiDigit(static_cast<unsigned char>(ch)))
889 nRet = ch - '0';
890 else
892 if (ch >= 'a' && ch <= 'f')
893 nRet = ch - 'a';
894 else if (ch >= 'A' && ch <= 'F')
895 nRet = ch - 'A';
896 else
897 return -1;
898 nRet += 10;
900 return nRet;
903 std::vector<unsigned char> DecodeHexString(const OString& rHex)
905 std::vector<unsigned char> aRet;
906 size_t nHexLen = rHex.getLength();
908 int nByte = 0;
909 int nCount = 2;
910 for (size_t i = 0; i < nHexLen; ++i)
912 nByte = nByte << 4;
913 sal_Int8 nParsed = AsHex(rHex[i]);
914 if (nParsed == -1)
916 SAL_WARN("svl.crypto", "DecodeHexString: invalid hex value");
917 return aRet;
919 nByte += nParsed;
920 --nCount;
921 if (!nCount)
923 aRet.push_back(nByte);
924 nCount = 2;
925 nByte = 0;
930 return aRet;
934 #if defined(SVL_CRYPTO_NSS) || defined(SVL_CRYPTO_MSCRYPTO)
936 bool Signing::Sign(OStringBuffer& rCMSHexBuffer)
938 // Create the PKCS#7 object.
939 css::uno::Sequence<sal_Int8> aDerEncoded = m_xCertificate->getEncoded();
940 if (!aDerEncoded.hasElements())
942 SAL_WARN("svl.crypto", "Crypto::Signing: empty certificate");
943 return false;
946 #ifndef _WIN32
948 CERTCertificate *cert = CERT_DecodeCertFromPackage(reinterpret_cast<char *>(aDerEncoded.getArray()), aDerEncoded.getLength());
950 if (!cert)
952 SAL_WARN("svl.crypto", "CERT_DecodeCertFromPackage failed");
953 return false;
956 std::vector<unsigned char> aHashResult;
958 comphelper::Hash aHash(comphelper::HashType::SHA256);
960 for (const auto& pair : m_dataBlocks)
961 aHash.update(static_cast<const unsigned char*>(pair.first), pair.second);
963 aHashResult = aHash.finalize();
965 SECItem digest;
966 digest.data = aHashResult.data();
967 digest.len = aHashResult.size();
969 PRTime now = PR_Now();
970 NSSCMSSignedData *cms_sd;
971 NSSCMSSignerInfo *cms_signer;
972 NSSCMSMessage *cms_msg = CreateCMSMessage(nullptr, &cms_sd, &cms_signer, cert, &digest);
973 if (!cms_msg)
974 return false;
976 OString pass(OUStringToOString( m_aSignPassword, RTL_TEXTENCODING_UTF8 ));
978 TimeStampReq src;
979 OStringBuffer response_buffer;
980 TimeStampResp response;
981 SECItem response_item;
982 NSSCMSAttribute timestamp;
983 SECItem values[2];
984 SECItem *valuesp[2];
985 valuesp[0] = values;
986 valuesp[1] = nullptr;
987 SECOidData typetag;
989 if( !m_aSignTSA.isEmpty() )
991 // Create another CMS message with the same contents as cms_msg, because it doesn't seem
992 // possible to encode a message twice (once to get something to timestamp, and then after
993 // adding the timestamp attribute).
995 NSSCMSSignedData *ts_cms_sd;
996 NSSCMSSignerInfo *ts_cms_signer;
997 NSSCMSMessage *ts_cms_msg = CreateCMSMessage(&now, &ts_cms_sd, &ts_cms_signer, cert, &digest);
998 if (!ts_cms_msg)
1000 return false;
1003 SECItem ts_cms_output;
1004 ts_cms_output.data = nullptr;
1005 ts_cms_output.len = 0;
1006 PLArenaPool *ts_arena = PORT_NewArena(10000);
1007 NSSCMSEncoderContext *ts_cms_ecx;
1008 ts_cms_ecx = NSS_CMSEncoder_Start(ts_cms_msg, nullptr, nullptr, &ts_cms_output, ts_arena, PDFSigningPKCS7PasswordCallback,
1009 const_cast<char*>(pass.getStr()), nullptr, nullptr, nullptr, nullptr);
1011 if (NSS_CMSEncoder_Finish(ts_cms_ecx) != SECSuccess)
1013 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Finish failed");
1014 return false;
1017 // I have compared the ts_cms_output produced here with the cms_output produced below when
1018 // not actually calling my_NSS_CMSSignerInfo_AddUnauthAttr()), and they are identical.
1020 std::vector<unsigned char> aTsHashResult = comphelper::Hash::calculateHash(ts_cms_signer->encDigest.data, ts_cms_signer->encDigest.len, comphelper::HashType::SHA256);
1021 SECItem ts_digest;
1022 ts_digest.type = siBuffer;
1023 ts_digest.data = aTsHashResult.data();
1024 ts_digest.len = aTsHashResult.size();
1026 unsigned char cOne = 1;
1027 src.version.type = siUnsignedInteger;
1028 src.version.data = &cOne;
1029 src.version.len = sizeof(cOne);
1031 src.messageImprint.hashAlgorithm.algorithm.data = nullptr;
1032 src.messageImprint.hashAlgorithm.parameters.data = nullptr;
1033 SECOID_SetAlgorithmID(nullptr, &src.messageImprint.hashAlgorithm, SEC_OID_SHA256, nullptr);
1034 src.messageImprint.hashedMessage = ts_digest;
1036 src.reqPolicy.type = siBuffer;
1037 src.reqPolicy.data = nullptr;
1038 src.reqPolicy.len = 0;
1040 unsigned int nNonce = comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32);
1041 src.nonce.type = siUnsignedInteger;
1042 src.nonce.data = reinterpret_cast<unsigned char*>(&nNonce);
1043 src.nonce.len = sizeof(nNonce);
1045 src.certReq.type = siUnsignedInteger;
1046 src.certReq.data = &cOne;
1047 src.certReq.len = sizeof(cOne);
1049 src.extensions = nullptr;
1051 SECItem* timestamp_request = SEC_ASN1EncodeItem(nullptr, nullptr, &src, TimeStampReq_Template);
1052 if (timestamp_request == nullptr)
1054 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem failed");
1055 return false;
1058 if (timestamp_request->data == nullptr)
1060 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem succeeded but got NULL data");
1061 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1062 return false;
1065 SAL_INFO("svl.crypto", "request length=" << timestamp_request->len);
1067 // Send time stamp request to TSA server, receive response
1069 CURL* curl = curl_easy_init();
1070 CURLcode rc;
1071 struct curl_slist* slist = nullptr;
1073 if (!curl)
1075 SAL_WARN("svl.crypto", "curl_easy_init failed");
1076 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1077 return false;
1080 SAL_INFO("svl.crypto", "Setting curl to verbose: " << (curl_easy_setopt(curl, CURLOPT_VERBOSE, 1) == CURLE_OK ? "OK" : "FAIL"));
1082 if ((rc = curl_easy_setopt(curl, CURLOPT_URL, OUStringToOString(m_aSignTSA, RTL_TEXTENCODING_UTF8).getStr())) != CURLE_OK)
1084 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_URL) failed: " << curl_easy_strerror(rc));
1085 curl_easy_cleanup(curl);
1086 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1087 return false;
1090 slist = curl_slist_append(slist, "Content-Type: application/timestamp-query");
1091 slist = curl_slist_append(slist, "Accept: application/timestamp-reply");
1093 if ((rc = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist)) != CURLE_OK)
1095 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_HTTPHEADER) failed: " << curl_easy_strerror(rc));
1096 curl_slist_free_all(slist);
1097 curl_easy_cleanup(curl);
1098 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1099 return false;
1102 if ((rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<tools::Long>(timestamp_request->len))) != CURLE_OK ||
1103 (rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, timestamp_request->data)) != CURLE_OK)
1105 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed: " << curl_easy_strerror(rc));
1106 curl_easy_cleanup(curl);
1107 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1108 return false;
1111 if ((rc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer)) != CURLE_OK ||
1112 (rc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, AppendToBuffer)) != CURLE_OK)
1114 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed: " << curl_easy_strerror(rc));
1115 curl_easy_cleanup(curl);
1116 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1117 return false;
1120 if ((rc = curl_easy_setopt(curl, CURLOPT_POST, 1)) != CURLE_OK)
1122 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_POST) failed: " << curl_easy_strerror(rc));
1123 curl_easy_cleanup(curl);
1124 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1125 return false;
1128 char error_buffer[CURL_ERROR_SIZE];
1129 if ((rc = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer)) != CURLE_OK)
1131 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_ERRORBUFFER) failed: " << curl_easy_strerror(rc));
1132 curl_easy_cleanup(curl);
1133 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1134 return false;
1137 // Use a ten second timeout
1138 if ((rc = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10)) != CURLE_OK ||
1139 (rc = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10)) != CURLE_OK)
1141 SAL_WARN("svl.crypto", "curl_easy_setopt(CURLOPT_TIMEOUT or CURLOPT_CONNECTTIMEOUT) failed: " << curl_easy_strerror(rc));
1142 curl_easy_cleanup(curl);
1143 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1144 return false;
1147 if (curl_easy_perform(curl) != CURLE_OK)
1149 SAL_WARN("svl.crypto", "curl_easy_perform failed: " << error_buffer);
1150 curl_easy_cleanup(curl);
1151 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1152 return false;
1155 SAL_INFO("svl.crypto", "PDF signing: got response, length=" << response_buffer.getLength());
1157 curl_slist_free_all(slist);
1158 curl_easy_cleanup(curl);
1159 SECITEM_FreeItem(timestamp_request, PR_TRUE);
1161 memset(&response, 0, sizeof(response));
1163 response_item.type = siBuffer;
1164 response_item.data = reinterpret_cast<unsigned char*>(const_cast<char*>(response_buffer.getStr()));
1165 response_item.len = response_buffer.getLength();
1167 if (SEC_ASN1DecodeItem(nullptr, &response, TimeStampResp_Template, &response_item) != SECSuccess)
1169 SAL_WARN("svl.crypto", "SEC_ASN1DecodeItem failed");
1170 return false;
1173 SAL_INFO("svl.crypto", "TimeStampResp received and decoded, status=" << PKIStatusInfoToString(response.status));
1175 if (response.status.status.len != 1 ||
1176 (response.status.status.data[0] != 0 && response.status.status.data[0] != 1))
1178 SAL_WARN("svl.crypto", "Timestamp request was not granted");
1179 return false;
1182 // timestamp.type filled in below
1184 // Not sure if we actually need two entries in the values array, now when valuesp is an
1185 // array too, the pointer to the values array followed by a null pointer. But I don't feel
1186 // like experimenting.
1187 values[0] = response.timeStampToken;
1188 values[1].type = siBuffer;
1189 values[1].data = nullptr;
1190 values[1].len = 0;
1192 timestamp.values = valuesp;
1194 typetag.oid.data = nullptr;
1195 // id-aa-timeStampToken OBJECT IDENTIFIER ::= { iso(1)
1196 // member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
1197 // smime(16) aa(2) 14 }
1198 if (my_SEC_StringToOID(&typetag.oid, "1.2.840.113549.1.9.16.2.14", 0) != SECSuccess)
1200 SAL_WARN("svl.crypto", "SEC_StringToOID failed");
1201 return false;
1203 typetag.offset = SEC_OID_UNKNOWN; // ???
1204 typetag.desc = "id-aa-timeStampToken";
1205 typetag.mechanism = CKM_SHA_1; // ???
1206 typetag.supportedExtension = UNSUPPORTED_CERT_EXTENSION; // ???
1207 timestamp.typeTag = &typetag;
1209 timestamp.type = typetag.oid; // ???
1211 timestamp.encoded = PR_TRUE; // ???
1213 if (my_NSS_CMSSignerInfo_AddUnauthAttr(cms_signer, &timestamp) != SECSuccess)
1215 SAL_WARN("svl.crypto", "NSS_CMSSignerInfo_AddUnauthAttr failed");
1216 return false;
1220 // Add the signing certificate as a signed attribute.
1221 ESSCertIDv2* aCertIDs[2];
1222 ESSCertIDv2 aCertID;
1223 // Write ESSCertIDv2.hashAlgorithm.
1224 aCertID.hashAlgorithm.algorithm.data = nullptr;
1225 aCertID.hashAlgorithm.parameters.data = nullptr;
1226 SECOID_SetAlgorithmID(nullptr, &aCertID.hashAlgorithm, SEC_OID_SHA256, nullptr);
1227 // Write ESSCertIDv2.certHash.
1228 SECItem aCertHashItem;
1229 auto pDerEncoded = reinterpret_cast<const unsigned char *>(aDerEncoded.getArray());
1230 std::vector<unsigned char> aCertHashResult = comphelper::Hash::calculateHash(pDerEncoded, aDerEncoded.getLength(), comphelper::HashType::SHA256);
1231 aCertHashItem.type = siBuffer;
1232 aCertHashItem.data = aCertHashResult.data();
1233 aCertHashItem.len = aCertHashResult.size();
1234 aCertID.certHash = aCertHashItem;
1235 // Write ESSCertIDv2.issuerSerial.
1236 IssuerSerial aSerial;
1237 GeneralName aName;
1238 aName.name = cert->issuer;
1239 aSerial.issuer.names = aName;
1240 aSerial.serialNumber = cert->serialNumber;
1241 aCertID.issuerSerial = aSerial;
1242 // Write SigningCertificateV2.certs.
1243 aCertIDs[0] = &aCertID;
1244 aCertIDs[1] = nullptr;
1245 SigningCertificateV2 aCertificate;
1246 aCertificate.certs = &aCertIDs[0];
1247 SECItem* pEncodedCertificate = SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate, SigningCertificateV2Template);
1248 if (!pEncodedCertificate)
1250 SAL_WARN("svl.crypto", "SEC_ASN1EncodeItem() failed");
1251 return false;
1254 NSSCMSAttribute aAttribute;
1255 SECItem aAttributeValues[2];
1256 SECItem* pAttributeValues[2];
1257 pAttributeValues[0] = aAttributeValues;
1258 pAttributeValues[1] = nullptr;
1259 aAttributeValues[0] = *pEncodedCertificate;
1260 aAttributeValues[1].type = siBuffer;
1261 aAttributeValues[1].data = nullptr;
1262 aAttributeValues[1].len = 0;
1263 aAttribute.values = pAttributeValues;
1265 SECOidData aOidData;
1266 aOidData.oid.data = nullptr;
1268 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
1269 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
1270 * smime(16) id-aa(2) 47 }
1272 if (my_SEC_StringToOID(&aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess)
1274 SAL_WARN("svl.crypto", "my_SEC_StringToOID() failed");
1275 return false;
1277 aOidData.offset = SEC_OID_UNKNOWN;
1278 aOidData.desc = "id-aa-signingCertificateV2";
1279 aOidData.mechanism = CKM_SHA_1;
1280 aOidData.supportedExtension = UNSUPPORTED_CERT_EXTENSION;
1281 aAttribute.typeTag = &aOidData;
1282 aAttribute.type = aOidData.oid;
1283 aAttribute.encoded = PR_TRUE;
1285 if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer, &aAttribute) != SECSuccess)
1287 SAL_WARN("svl.crypto", "my_NSS_CMSSignerInfo_AddAuthAttr() failed");
1288 return false;
1291 SECItem cms_output;
1292 cms_output.data = nullptr;
1293 cms_output.len = 0;
1294 PLArenaPool *arena = PORT_NewArena(10000);
1295 NSSCMSEncoderContext *cms_ecx;
1297 // Possibly it would work to even just pass NULL for the password callback function and its
1298 // argument here. After all, at least with the hardware token and associated software I tested
1299 // with, the software itself pops up a dialog asking for the PIN (password). But I am not going
1300 // to test it and risk locking up my token...
1302 cms_ecx = NSS_CMSEncoder_Start(cms_msg, nullptr, nullptr, &cms_output, arena, PDFSigningPKCS7PasswordCallback,
1303 const_cast<char*>(pass.getStr()), nullptr, nullptr, nullptr, nullptr);
1305 if (!cms_ecx)
1307 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Start failed");
1308 return false;
1311 if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess)
1313 SAL_WARN("svl.crypto", "NSS_CMSEncoder_Finish failed");
1314 return false;
1317 if (cms_output.len*2 > MAX_SIGNATURE_CONTENT_LENGTH)
1319 SAL_WARN("svl.crypto", "Signature requires more space (" << cms_output.len*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH << ")");
1320 NSS_CMSMessage_Destroy(cms_msg);
1321 return false;
1324 for (unsigned int i = 0; i < cms_output.len ; i++)
1325 appendHex(cms_output.data[i], rCMSHexBuffer);
1327 SECITEM_FreeItem(pEncodedCertificate, PR_TRUE);
1328 NSS_CMSMessage_Destroy(cms_msg);
1330 return true;
1332 #else // _WIN32
1333 PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, reinterpret_cast<const BYTE*>(aDerEncoded.getArray()), aDerEncoded.getLength());
1334 if (pCertContext == nullptr)
1336 SAL_WARN("svl.crypto", "CertCreateCertificateContext failed: " << WindowsErrorString(GetLastError()));
1337 return false;
1340 CRYPT_SIGN_MESSAGE_PARA aPara = {};
1341 aPara.cbSize = sizeof(aPara);
1342 aPara.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
1343 aPara.pSigningCert = pCertContext;
1344 aPara.HashAlgorithm.pszObjId = const_cast<LPSTR>(szOID_NIST_sha256);
1345 aPara.HashAlgorithm.Parameters.cbData = 0;
1346 aPara.cMsgCert = 1;
1347 aPara.rgpMsgCert = &pCertContext;
1349 NCRYPT_KEY_HANDLE hCryptKey = 0;
1350 DWORD dwFlags = CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG;
1351 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE* phCryptProvOrNCryptKey = &hCryptKey;
1352 DWORD nKeySpec;
1353 BOOL bFreeNeeded;
1355 if (!CryptAcquireCertificatePrivateKey(pCertContext,
1356 dwFlags,
1357 nullptr,
1358 phCryptProvOrNCryptKey,
1359 &nKeySpec,
1360 &bFreeNeeded))
1362 SAL_WARN("svl.crypto", "CryptAcquireCertificatePrivateKey failed: " << WindowsErrorString(GetLastError()));
1363 CertFreeCertificateContext(pCertContext);
1364 return false;
1366 assert(!bFreeNeeded);
1368 CMSG_SIGNER_ENCODE_INFO aSignerInfo = {};
1369 aSignerInfo.cbSize = sizeof(aSignerInfo);
1370 aSignerInfo.pCertInfo = pCertContext->pCertInfo;
1371 aSignerInfo.hNCryptKey = hCryptKey;
1372 aSignerInfo.dwKeySpec = nKeySpec;
1373 aSignerInfo.HashAlgorithm.pszObjId = const_cast<LPSTR>(szOID_NIST_sha256);
1374 aSignerInfo.HashAlgorithm.Parameters.cbData = 0;
1376 // Add the signing certificate as a signed attribute.
1377 CRYPT_INTEGER_BLOB aCertificateBlob;
1378 SvMemoryStream aEncodedCertificate;
1379 if (!CreateSigningCertificateAttribute(aDerEncoded.getArray(), aDerEncoded.getLength(), pCertContext, aEncodedCertificate))
1381 SAL_WARN("svl.crypto", "CreateSigningCertificateAttribute() failed");
1382 return false;
1384 aCertificateBlob.pbData = const_cast<BYTE*>(static_cast<const BYTE*>(aEncodedCertificate.GetData()));
1385 aCertificateBlob.cbData = aEncodedCertificate.GetSize();
1386 CRYPT_ATTRIBUTE aCertificateAttribute;
1388 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
1389 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
1390 * smime(16) id-aa(2) 47 }
1392 aCertificateAttribute.pszObjId = const_cast<LPSTR>("1.2.840.113549.1.9.16.2.47");
1393 aCertificateAttribute.cValue = 1;
1394 aCertificateAttribute.rgValue = &aCertificateBlob;
1395 aSignerInfo.cAuthAttr = 1;
1396 aSignerInfo.rgAuthAttr = &aCertificateAttribute;
1398 CMSG_SIGNED_ENCODE_INFO aSignedInfo = {};
1399 aSignedInfo.cbSize = sizeof(aSignedInfo);
1400 aSignedInfo.cSigners = 1;
1401 aSignedInfo.rgSigners = &aSignerInfo;
1403 CERT_BLOB aCertBlob;
1405 aCertBlob.cbData = pCertContext->cbCertEncoded;
1406 aCertBlob.pbData = pCertContext->pbCertEncoded;
1408 aSignedInfo.cCertEncoded = 1;
1409 aSignedInfo.rgCertEncoded = &aCertBlob;
1411 HCRYPTMSG hMsg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1412 CMSG_DETACHED_FLAG,
1413 CMSG_SIGNED,
1414 &aSignedInfo,
1415 nullptr,
1416 nullptr);
1417 if (!hMsg)
1419 SAL_WARN("svl.crypto", "CryptMsgOpenToEncode failed: " << WindowsErrorString(GetLastError()));
1420 CertFreeCertificateContext(pCertContext);
1421 return false;
1424 for (size_t i = 0; i < m_dataBlocks.size(); ++i)
1426 const bool last = (i == m_dataBlocks.size() - 1);
1427 if (!CryptMsgUpdate(hMsg, static_cast<const BYTE *>(m_dataBlocks[i].first), m_dataBlocks[i].second, last))
1429 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
1430 CryptMsgClose(hMsg);
1431 CertFreeCertificateContext(pCertContext);
1432 return false;
1436 PCRYPT_TIMESTAMP_CONTEXT pTsContext = nullptr;
1438 if( !m_aSignTSA.isEmpty() )
1440 HCRYPTMSG hDecodedMsg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1441 CMSG_DETACHED_FLAG,
1442 CMSG_SIGNED,
1443 NULL,
1444 nullptr,
1445 nullptr);
1446 if (!hDecodedMsg)
1448 SAL_WARN("svl.crypto", "CryptMsgOpenToDecode failed: " << WindowsErrorString(GetLastError()));
1449 CryptMsgClose(hMsg);
1450 CertFreeCertificateContext(pCertContext);
1451 return false;
1454 DWORD nTsSigLen = 0;
1456 if (!CryptMsgGetParam(hMsg, CMSG_BARE_CONTENT_PARAM, 0, nullptr, &nTsSigLen))
1458 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1459 CryptMsgClose(hDecodedMsg);
1460 CryptMsgClose(hMsg);
1461 CertFreeCertificateContext(pCertContext);
1462 return false;
1465 SAL_INFO("svl.crypto", "nTsSigLen=" << nTsSigLen);
1467 std::unique_ptr<BYTE[]> pTsSig(new BYTE[nTsSigLen]);
1469 if (!CryptMsgGetParam(hMsg, CMSG_BARE_CONTENT_PARAM, 0, pTsSig.get(), &nTsSigLen))
1471 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1472 CryptMsgClose(hDecodedMsg);
1473 CryptMsgClose(hMsg);
1474 CertFreeCertificateContext(pCertContext);
1475 return false;
1478 if (!CryptMsgUpdate(hDecodedMsg, pTsSig.get(), nTsSigLen, TRUE))
1480 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
1481 CryptMsgClose(hDecodedMsg);
1482 CryptMsgClose(hMsg);
1483 CertFreeCertificateContext(pCertContext);
1484 return false;
1487 DWORD nDecodedSignerInfoLen = 0;
1488 if (!CryptMsgGetParam(hDecodedMsg, CMSG_SIGNER_INFO_PARAM, 0, nullptr, &nDecodedSignerInfoLen))
1490 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
1491 CryptMsgClose(hDecodedMsg);
1492 CryptMsgClose(hMsg);
1493 CertFreeCertificateContext(pCertContext);
1494 return false;
1497 std::unique_ptr<BYTE[]> pDecodedSignerInfoBuf(new BYTE[nDecodedSignerInfoLen]);
1499 if (!CryptMsgGetParam(hDecodedMsg, CMSG_SIGNER_INFO_PARAM, 0, pDecodedSignerInfoBuf.get(), &nDecodedSignerInfoLen))
1501 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
1502 CryptMsgClose(hDecodedMsg);
1503 CryptMsgClose(hMsg);
1504 CertFreeCertificateContext(pCertContext);
1505 return false;
1508 CMSG_SIGNER_INFO *pDecodedSignerInfo = reinterpret_cast<CMSG_SIGNER_INFO *>(pDecodedSignerInfoBuf.get());
1510 CRYPT_TIMESTAMP_PARA aTsPara;
1511 unsigned int nNonce = comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32);
1513 aTsPara.pszTSAPolicyId = nullptr;
1514 aTsPara.fRequestCerts = TRUE;
1515 aTsPara.Nonce.cbData = sizeof(nNonce);
1516 aTsPara.Nonce.pbData = reinterpret_cast<BYTE *>(&nNonce);
1517 aTsPara.cExtension = 0;
1518 aTsPara.rgExtension = nullptr;
1520 if (!CryptRetrieveTimeStamp(o3tl::toW(m_aSignTSA.getStr()),
1522 10000,
1523 szOID_NIST_sha256,
1524 &aTsPara,
1525 pDecodedSignerInfo->EncryptedHash.pbData,
1526 pDecodedSignerInfo->EncryptedHash.cbData,
1527 &pTsContext,
1528 nullptr,
1529 nullptr))
1531 SAL_WARN("svl.crypto", "CryptRetrieveTimeStamp failed: " << WindowsErrorString(GetLastError()));
1532 CryptMsgClose(hDecodedMsg);
1533 CryptMsgClose(hMsg);
1534 CertFreeCertificateContext(pCertContext);
1535 return false;
1538 SAL_INFO("svl.crypto", "Time stamp size is " << pTsContext->cbEncoded << " bytes");
1540 // I tried to use CryptMsgControl() with CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR to add the
1541 // timestamp, but that failed with "The parameter is incorrect". Probably it is too late to
1542 // modify the message once its data has already been encoded as part of the
1543 // CryptMsgGetParam() with CMSG_BARE_CONTENT_PARAM above. So close the message and re-do its
1544 // creation steps, but now with an amended aSignerInfo.
1546 CRYPT_INTEGER_BLOB aTimestampBlob;
1547 aTimestampBlob.cbData = pTsContext->cbEncoded;
1548 aTimestampBlob.pbData = pTsContext->pbEncoded;
1550 CRYPT_ATTRIBUTE aTimestampAttribute;
1551 aTimestampAttribute.pszObjId = const_cast<LPSTR>(
1552 "1.2.840.113549.1.9.16.2.14");
1553 aTimestampAttribute.cValue = 1;
1554 aTimestampAttribute.rgValue = &aTimestampBlob;
1556 aSignerInfo.cUnauthAttr = 1;
1557 aSignerInfo.rgUnauthAttr = &aTimestampAttribute;
1559 CryptMsgClose(hMsg);
1561 hMsg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1562 CMSG_DETACHED_FLAG,
1563 CMSG_SIGNED,
1564 &aSignedInfo,
1565 nullptr,
1566 nullptr);
1568 for (size_t i = 0; i < m_dataBlocks.size(); ++i)
1570 const bool last = (i == m_dataBlocks.size() - 1);
1571 if (!hMsg ||
1572 !CryptMsgUpdate(hMsg, static_cast<const BYTE *>(m_dataBlocks[i].first), m_dataBlocks[i].second, last))
1574 SAL_WARN("svl.crypto", "Re-creating the message failed: " << WindowsErrorString(GetLastError()));
1575 CryptMemFree(pTsContext);
1576 CryptMsgClose(hDecodedMsg);
1577 CryptMsgClose(hMsg);
1578 CertFreeCertificateContext(pCertContext);
1579 return false;
1583 CryptMsgClose(hDecodedMsg);
1586 DWORD nSigLen = 0;
1588 if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, nullptr, &nSigLen))
1590 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1591 if (pTsContext)
1592 CryptMemFree(pTsContext);
1593 CryptMsgClose(hMsg);
1594 CertFreeCertificateContext(pCertContext);
1595 return false;
1598 if (nSigLen*2 > MAX_SIGNATURE_CONTENT_LENGTH)
1600 SAL_WARN("svl.crypto", "Signature requires more space (" << nSigLen*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH << ")");
1601 if (pTsContext)
1602 CryptMemFree(pTsContext);
1603 CryptMsgClose(hMsg);
1604 CertFreeCertificateContext(pCertContext);
1605 return false;
1608 SAL_INFO("svl.crypto", "Signature size is " << nSigLen << " bytes");
1609 std::unique_ptr<BYTE[]> pSig(new BYTE[nSigLen]);
1611 if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, pSig.get(), &nSigLen))
1613 SAL_WARN("svl.crypto", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
1614 if (pTsContext)
1615 CryptMemFree(pTsContext);
1616 CryptMsgClose(hMsg);
1617 CertFreeCertificateContext(pCertContext);
1618 return false;
1621 // Release resources
1622 if (pTsContext)
1623 CryptMemFree(pTsContext);
1624 CryptMsgClose(hMsg);
1625 CertFreeCertificateContext(pCertContext);
1627 for (unsigned int i = 0; i < nSigLen ; i++)
1628 appendHex(pSig[i], rCMSHexBuffer);
1630 return true;
1631 #endif
1633 #else
1634 bool Signing::Sign(OStringBuffer&)
1636 return false;
1638 #endif //!SVL_CRYPTO_NSS && !SVL_CRYPTO_MSCRYPTO
1641 namespace
1643 #ifdef SVL_CRYPTO_NSS
1644 /// Similar to NSS_CMSAttributeArray_FindAttrByOidTag(), but works directly with a SECOidData.
1645 NSSCMSAttribute* CMSAttributeArray_FindAttrByOidData(NSSCMSAttribute** attrs, SECOidData const * oid, PRBool only)
1647 NSSCMSAttribute* attr1, *attr2;
1649 if (attrs == nullptr)
1650 return nullptr;
1652 if (oid == nullptr)
1653 return nullptr;
1655 while ((attr1 = *attrs++) != nullptr)
1657 if (attr1->type.len == oid->oid.len && PORT_Memcmp(attr1->type.data,
1658 oid->oid.data,
1659 oid->oid.len) == 0)
1660 break;
1663 if (attr1 == nullptr)
1664 return nullptr;
1666 if (!only)
1667 return attr1;
1669 while ((attr2 = *attrs++) != nullptr)
1671 if (attr2->type.len == oid->oid.len && PORT_Memcmp(attr2->type.data,
1672 oid->oid.data,
1673 oid->oid.len) == 0)
1674 break;
1677 if (attr2 != nullptr)
1678 return nullptr;
1680 return attr1;
1683 /// Same as SEC_StringToOID(), which is private to us.
1684 SECStatus StringToOID(SECItem* to, const char* from, PRUint32 len)
1686 PRUint32 decimal_numbers = 0;
1687 PRUint32 result_bytes = 0;
1688 SECStatus rv;
1689 PRUint8 result[1024];
1691 static const PRUint32 max_decimal = 0xffffffff / 10;
1692 static const char OIDstring[] = {"OID."};
1694 if (!from || !to)
1696 PORT_SetError(SEC_ERROR_INVALID_ARGS);
1697 return SECFailure;
1699 if (!len)
1701 len = PL_strlen(from);
1703 if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4))
1705 from += 4; /* skip leading "OID." if present */
1706 len -= 4;
1708 if (!len)
1710 bad_data:
1711 PORT_SetError(SEC_ERROR_BAD_DATA);
1712 return SECFailure;
1716 PRUint32 decimal = 0;
1717 while (len > 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from)))
1719 PRUint32 addend = *from++ - '0';
1720 --len;
1721 if (decimal > max_decimal) /* overflow */
1722 goto bad_data;
1723 decimal = (decimal * 10) + addend;
1724 if (decimal < addend) /* overflow */
1725 goto bad_data;
1727 if (len != 0 && *from != '.')
1729 goto bad_data;
1731 if (decimal_numbers == 0)
1733 if (decimal > 2)
1734 goto bad_data;
1735 result[0] = decimal * 40;
1736 result_bytes = 1;
1738 else if (decimal_numbers == 1)
1740 if (decimal > 40)
1741 goto bad_data;
1742 result[0] += decimal;
1744 else
1746 /* encode the decimal number, */
1747 PRUint8* rp;
1748 PRUint32 num_bytes = 0;
1749 PRUint32 tmp = decimal;
1750 while (tmp)
1752 num_bytes++;
1753 tmp >>= 7;
1755 if (!num_bytes)
1756 ++num_bytes; /* use one byte for a zero value */
1757 if (static_cast<size_t>(num_bytes) + result_bytes > sizeof result)
1758 goto bad_data;
1759 tmp = num_bytes;
1760 rp = result + result_bytes - 1;
1761 rp[tmp] = static_cast<PRUint8>(decimal & 0x7f);
1762 decimal >>= 7;
1763 while (--tmp > 0)
1765 rp[tmp] = static_cast<PRUint8>(decimal | 0x80);
1766 decimal >>= 7;
1768 result_bytes += num_bytes;
1770 ++decimal_numbers;
1771 if (len > 0) /* skip trailing '.' */
1773 ++from;
1774 --len;
1777 while (len > 0);
1778 /* now result contains result_bytes of data */
1779 if (to->data && to->len >= result_bytes)
1781 to->len = result_bytes;
1782 PORT_Memcpy(to->data, result, to->len);
1783 rv = SECSuccess;
1785 else
1787 SECItem result_item = {siBuffer, nullptr, 0 };
1788 result_item.data = result;
1789 result_item.len = result_bytes;
1790 rv = SECITEM_CopyItem(nullptr, to, &result_item);
1792 return rv;
1794 #elif defined SVL_CRYPTO_MSCRYPTO
1795 /// Verifies a non-detached signature using CryptoAPI.
1796 bool VerifyNonDetachedSignature(const std::vector<unsigned char>& aData, const std::vector<BYTE>& rExpectedHash)
1798 HCRYPTPROV hProv = 0;
1799 if (!CryptAcquireContextW(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
1801 SAL_WARN("svl.crypto", "CryptAcquireContext() failed");
1802 return false;
1805 HCRYPTHASH hHash = 0;
1806 if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
1808 SAL_WARN("svl.crypto", "CryptCreateHash() failed");
1809 return false;
1812 if (!CryptHashData(hHash, aData.data(), aData.size(), 0))
1814 SAL_WARN("svl.crypto", "CryptHashData() failed");
1815 return false;
1818 DWORD nActualHash = 0;
1819 if (!CryptGetHashParam(hHash, HP_HASHVAL, nullptr, &nActualHash, 0))
1821 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash length");
1822 return false;
1825 std::vector<unsigned char> aActualHash(nActualHash);
1826 if (!CryptGetHashParam(hHash, HP_HASHVAL, aActualHash.data(), &nActualHash, 0))
1828 SAL_WARN("svl.crypto", "CryptGetHashParam() failed to provide the hash");
1829 return false;
1832 CryptDestroyHash(hHash);
1833 CryptReleaseContext(hProv, 0);
1835 return aActualHash.size() == rExpectedHash.size() &&
1836 !std::memcmp(aActualHash.data(), rExpectedHash.data(), aActualHash.size());
1839 OUString GetSubjectName(PCCERT_CONTEXT pCertContext)
1841 OUString subjectName;
1843 // Get Subject name size.
1844 DWORD dwData = CertGetNameStringW(pCertContext,
1845 CERT_NAME_SIMPLE_DISPLAY_TYPE,
1847 nullptr,
1848 nullptr,
1850 if (!dwData)
1852 SAL_WARN("svl.crypto", "ValidateSignature: CertGetNameString failed");
1853 return subjectName;
1856 // Allocate memory for subject name.
1857 LPWSTR szName = static_cast<LPWSTR>(
1858 LocalAlloc(LPTR, dwData * sizeof(WCHAR)));
1859 if (!szName)
1861 SAL_WARN("svl.crypto", "ValidateSignature: Unable to allocate memory for subject name");
1862 return subjectName;
1865 // Get subject name.
1866 if (!CertGetNameStringW(pCertContext,
1867 CERT_NAME_SIMPLE_DISPLAY_TYPE,
1869 nullptr,
1870 szName,
1871 dwData))
1873 LocalFree(szName);
1874 SAL_WARN("svl.crypto", "ValidateSignature: CertGetNameString failed");
1875 return subjectName;
1878 subjectName = o3tl::toU(szName);
1879 LocalFree(szName);
1881 return subjectName;
1884 #endif
1887 #ifdef SVL_CRYPTO_NSS
1888 namespace
1890 void ensureNssInit()
1892 // e.g. tdf#122599 ensure NSS library is initialized for NSS_CMSMessage_CreateFromDER
1893 css::uno::Reference<css::xml::crypto::XNSSInitializer>
1894 xNSSInitializer = css::xml::crypto::NSSInitializer::create(comphelper::getProcessComponentContext());
1896 // this calls NSS_Init
1897 xNSSInitializer->getDigestContext(css::xml::crypto::DigestID::SHA256,
1898 uno::Sequence<beans::NamedValue>());
1901 #endif
1903 bool Signing::Verify(const std::vector<unsigned char>& aData,
1904 const bool bNonDetached,
1905 const std::vector<unsigned char>& aSignature,
1906 SignatureInformation& rInformation)
1908 #ifdef SVL_CRYPTO_NSS
1909 // ensure NSS_Init() is called before using NSS_CMSMessage_CreateFromDER
1910 static std::once_flag aInitOnce;
1911 std::call_once(aInitOnce, ensureNssInit);
1913 // Validate the signature.
1914 SECItem aSignatureItem;
1915 aSignatureItem.data = const_cast<unsigned char*>(aSignature.data());
1916 aSignatureItem.len = aSignature.size();
1917 NSSCMSMessage* pCMSMessage = NSS_CMSMessage_CreateFromDER(&aSignatureItem,
1918 /*cb=*/nullptr,
1919 /*cb_arg=*/nullptr,
1920 /*pwfn=*/nullptr,
1921 /*pwfn_arg=*/nullptr,
1922 /*decrypt_key_cb=*/nullptr,
1923 /*decrypt_key_cb_arg=*/nullptr);
1924 if (!NSS_CMSMessage_IsSigned(pCMSMessage))
1926 SAL_WARN("svl.crypto", "ValidateSignature: message is not signed");
1927 return false;
1930 NSSCMSContentInfo* pCMSContentInfo = NSS_CMSMessage_ContentLevel(pCMSMessage, 0);
1931 if (!pCMSContentInfo)
1933 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSMessage_ContentLevel() failed");
1934 return false;
1937 auto pCMSSignedData = static_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(pCMSContentInfo));
1938 if (!pCMSSignedData)
1940 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSContentInfo_GetContent() failed");
1941 return false;
1944 // Import certificates from the signed data temporarily, so it'll be
1945 // possible to verify the signature, even if we didn't have the certificate
1946 // previously.
1947 std::vector<CERTCertificate*> aDocumentCertificates;
1948 for (size_t i = 0; pCMSSignedData->rawCerts[i]; ++i)
1949 aDocumentCertificates.push_back(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), pCMSSignedData->rawCerts[i], nullptr, 0, 0));
1951 NSSCMSSignerInfo* pCMSSignerInfo = NSS_CMSSignedData_GetSignerInfo(pCMSSignedData, 0);
1952 if (!pCMSSignerInfo)
1954 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSSignedData_GetSignerInfo() failed");
1955 return false;
1958 SECItem aAlgorithm = NSS_CMSSignedData_GetDigestAlgs(pCMSSignedData)[0]->algorithm;
1959 SECOidTag eOidTag = SECOID_FindOIDTag(&aAlgorithm);
1961 // Map a sign algorithm to a digest algorithm.
1962 // See NSS_CMSUtil_MapSignAlgs(), which is private to us.
1963 switch (eOidTag)
1965 case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
1966 eOidTag = SEC_OID_SHA1;
1967 break;
1968 case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
1969 eOidTag = SEC_OID_SHA256;
1970 break;
1971 case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
1972 eOidTag = SEC_OID_SHA512;
1973 break;
1974 default:
1975 break;
1978 HASH_HashType eHashType = HASH_GetHashTypeByOidTag(eOidTag);
1979 HASHContext* pHASHContext = HASH_Create(eHashType);
1980 if (!pHASHContext)
1982 SAL_WARN("svl.crypto", "ValidateSignature: HASH_Create() failed");
1983 return false;
1986 // We have a hash, update it with the byte ranges.
1987 HASH_Update(pHASHContext, aData.data(), aData.size());
1989 // Find out what is the expected length of the hash.
1990 unsigned int nMaxResultLen = 0;
1991 switch (eOidTag)
1993 case SEC_OID_SHA1:
1994 nMaxResultLen = comphelper::SHA1_HASH_LENGTH;
1995 rInformation.nDigestID = xml::crypto::DigestID::SHA1;
1996 break;
1997 case SEC_OID_SHA256:
1998 nMaxResultLen = comphelper::SHA256_HASH_LENGTH;
1999 rInformation.nDigestID = xml::crypto::DigestID::SHA256;
2000 break;
2001 case SEC_OID_SHA512:
2002 nMaxResultLen = comphelper::SHA512_HASH_LENGTH;
2003 rInformation.nDigestID = xml::crypto::DigestID::SHA512;
2004 break;
2005 default:
2006 SAL_WARN("svl.crypto", "ValidateSignature: unrecognized algorithm");
2007 return false;
2010 auto pActualResultBuffer = static_cast<unsigned char*>(PORT_Alloc(nMaxResultLen));
2011 unsigned int nActualResultLen;
2012 HASH_End(pHASHContext, pActualResultBuffer, &nActualResultLen, nMaxResultLen);
2014 CERTCertificate* pCertificate = NSS_CMSSignerInfo_GetSigningCertificate(pCMSSignerInfo, CERT_GetDefaultCertDB());
2015 if (!pCertificate)
2017 SAL_WARN("svl.crypto", "ValidateSignature: NSS_CMSSignerInfo_GetSigningCertificate() failed");
2018 return false;
2020 else
2022 uno::Sequence<sal_Int8> aDerCert(pCertificate->derCert.len);
2023 for (size_t i = 0; i < pCertificate->derCert.len; ++i)
2024 aDerCert[i] = pCertificate->derCert.data[i];
2025 OUStringBuffer aBuffer;
2026 comphelper::Base64::encode(aBuffer, aDerCert);
2027 SignatureInformation::X509Data temp;
2028 temp.emplace_back();
2029 temp.back().X509Certificate = aBuffer.makeStringAndClear();
2030 temp.back().X509Subject = OUString(pCertificate->subjectName, PL_strlen(pCertificate->subjectName), RTL_TEXTENCODING_UTF8);
2031 rInformation.X509Datas.clear();
2032 rInformation.X509Datas.emplace_back(temp);
2035 PRTime nSigningTime;
2036 // This may fail, in which case the date should be taken from the PDF's dictionary's "M" key,
2037 // so not critical for PDF at least.
2038 if (NSS_CMSSignerInfo_GetSigningTime(pCMSSignerInfo, &nSigningTime) == SECSuccess)
2040 // First convert the UTC UNIX timestamp to a tools::DateTime.
2041 // nSigningTime is in microseconds.
2042 DateTime aDateTime = DateTime::CreateFromUnixTime(static_cast<double>(nSigningTime) / 1000000);
2044 // Then convert to a local UNO DateTime.
2045 aDateTime.ConvertToLocalTime();
2046 rInformation.stDateTime = aDateTime.GetUNODateTime();
2047 if (rInformation.ouDateTime.isEmpty())
2049 OUStringBuffer rBuffer;
2050 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetYear()));
2051 rBuffer.append('-');
2052 if (aDateTime.GetMonth() < 10)
2053 rBuffer.append('0');
2054 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetMonth()));
2055 rBuffer.append('-');
2056 if (aDateTime.GetDay() < 10)
2057 rBuffer.append('0');
2058 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetDay()));
2059 rInformation.ouDateTime = rBuffer.makeStringAndClear();
2063 // Check if we have a signing certificate attribute.
2064 SECOidData aOidData;
2065 aOidData.oid.data = nullptr;
2067 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
2068 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
2069 * smime(16) id-aa(2) 47 }
2071 if (StringToOID(&aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess)
2073 SAL_WARN("svl.crypto", "StringToOID() failed");
2074 return false;
2076 aOidData.offset = SEC_OID_UNKNOWN;
2077 aOidData.desc = "id-aa-signingCertificateV2";
2078 aOidData.mechanism = CKM_SHA_1;
2079 aOidData.supportedExtension = UNSUPPORTED_CERT_EXTENSION;
2080 NSSCMSAttribute* pAttribute = CMSAttributeArray_FindAttrByOidData(pCMSSignerInfo->authAttr, &aOidData, PR_TRUE);
2081 if (pAttribute)
2082 rInformation.bHasSigningCertificate = true;
2084 SECItem* pContentInfoContentData = pCMSSignedData->contentInfo.content.data;
2085 if (bNonDetached && pContentInfoContentData && pContentInfoContentData->data)
2087 // Not a detached signature.
2088 if (!std::memcmp(pActualResultBuffer, pContentInfoContentData->data, nMaxResultLen) && nActualResultLen == pContentInfoContentData->len)
2089 rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
2091 else
2093 // Detached, the usual case.
2094 SECItem aActualResultItem;
2095 aActualResultItem.data = pActualResultBuffer;
2096 aActualResultItem.len = nActualResultLen;
2097 if (NSS_CMSSignerInfo_Verify(pCMSSignerInfo, &aActualResultItem, nullptr) == SECSuccess)
2098 rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
2101 // Everything went fine
2102 PORT_Free(pActualResultBuffer);
2103 HASH_Destroy(pHASHContext);
2104 NSS_CMSSignerInfo_Destroy(pCMSSignerInfo);
2105 for (auto pDocumentCertificate : aDocumentCertificates)
2106 CERT_DestroyCertificate(pDocumentCertificate);
2108 return true;
2110 #elif defined SVL_CRYPTO_MSCRYPTO
2111 // Open a message for decoding.
2112 HCRYPTMSG hMsg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
2113 CMSG_DETACHED_FLAG,
2115 NULL,
2116 nullptr,
2117 nullptr);
2118 if (!hMsg)
2120 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgOpenToDecode() failed");
2121 return false;
2124 // Update the message with the encoded header blob.
2125 if (!CryptMsgUpdate(hMsg, aSignature.data(), aSignature.size(), TRUE))
2127 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the header failed: " << WindowsErrorString(GetLastError()));
2128 return false;
2131 // Update the message with the content blob.
2132 if (!CryptMsgUpdate(hMsg, aData.data(), aData.size(), FALSE))
2134 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the content failed: " << WindowsErrorString(GetLastError()));
2135 return false;
2138 if (!CryptMsgUpdate(hMsg, nullptr, 0, TRUE))
2140 SAL_WARN("svl.crypto", "ValidateSignature, CryptMsgUpdate() for the last content failed: " << WindowsErrorString(GetLastError()));
2141 return false;
2144 // Get the CRYPT_ALGORITHM_IDENTIFIER from the message.
2145 DWORD nDigestID = 0;
2146 if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_HASH_ALGORITHM_PARAM, 0, nullptr, &nDigestID))
2148 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
2149 return false;
2151 std::unique_ptr<BYTE[]> pDigestBytes(new BYTE[nDigestID]);
2152 if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_HASH_ALGORITHM_PARAM, 0, pDigestBytes.get(), &nDigestID))
2154 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed: " << WindowsErrorString(GetLastError()));
2155 return false;
2157 auto pDigestID = reinterpret_cast<CRYPT_ALGORITHM_IDENTIFIER*>(pDigestBytes.get());
2158 if (OString(szOID_NIST_sha256) == pDigestID->pszObjId)
2159 rInformation.nDigestID = xml::crypto::DigestID::SHA256;
2160 else if (OString(szOID_RSA_SHA1RSA) == pDigestID->pszObjId || OString(szOID_OIWSEC_sha1) == pDigestID->pszObjId)
2161 rInformation.nDigestID = xml::crypto::DigestID::SHA1;
2162 else
2163 // Don't error out here, we can still verify the message digest correctly, just the digest ID won't be set.
2164 SAL_WARN("svl.crypto", "ValidateSignature: unhandled algorithm identifier '"<<pDigestID->pszObjId<<"'");
2166 // Get the signer CERT_INFO from the message.
2167 DWORD nSignerCertInfo = 0;
2168 if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, nullptr, &nSignerCertInfo))
2170 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2171 return false;
2173 std::unique_ptr<BYTE[]> pSignerCertInfoBuf(new BYTE[nSignerCertInfo]);
2174 if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, pSignerCertInfoBuf.get(), &nSignerCertInfo))
2176 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2177 return false;
2179 PCERT_INFO pSignerCertInfo = reinterpret_cast<PCERT_INFO>(pSignerCertInfoBuf.get());
2181 // Open a certificate store in memory using CERT_STORE_PROV_MSG, which
2182 // initializes it with the certificates from the message.
2183 HCERTSTORE hStoreHandle = CertOpenStore(CERT_STORE_PROV_MSG,
2184 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
2185 NULL,
2187 hMsg);
2188 if (!hStoreHandle)
2190 SAL_WARN("svl.crypto", "ValidateSignature: CertOpenStore() failed");
2191 return false;
2194 // Find the signer's certificate in the store.
2195 PCCERT_CONTEXT pSignerCertContext = CertGetSubjectCertificateFromStore(hStoreHandle,
2196 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
2197 pSignerCertInfo);
2198 if (!pSignerCertContext)
2200 SAL_WARN("svl.crypto", "ValidateSignature: CertGetSubjectCertificateFromStore() failed");
2201 return false;
2203 else
2205 // Write rInformation.ouX509Certificate.
2206 uno::Sequence<sal_Int8> aDerCert(pSignerCertContext->cbCertEncoded);
2207 for (size_t i = 0; i < pSignerCertContext->cbCertEncoded; ++i)
2208 aDerCert[i] = pSignerCertContext->pbCertEncoded[i];
2209 OUStringBuffer aBuffer;
2210 comphelper::Base64::encode(aBuffer, aDerCert);
2211 SignatureInformation::X509Data temp;
2212 temp.emplace_back();
2213 temp.back().X509Certificate = aBuffer.makeStringAndClear();
2214 temp.back().X509Subject = GetSubjectName(pSignerCertContext);
2215 rInformation.X509Datas.clear();
2216 rInformation.X509Datas.emplace_back(temp);
2219 if (bNonDetached)
2221 // Not a detached signature.
2222 DWORD nContentParam = 0;
2223 if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, nullptr, &nContentParam))
2225 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2226 return false;
2229 std::vector<BYTE> aContentParam(nContentParam);
2230 if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, aContentParam.data(), &nContentParam))
2232 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() failed");
2233 return false;
2236 if (VerifyNonDetachedSignature(aData, aContentParam))
2237 rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
2239 else
2241 // Detached, the usual case.
2242 // Use the CERT_INFO from the signer certificate to verify the signature.
2243 if (CryptMsgControl(hMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE, pSignerCertContext->pCertInfo))
2244 rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
2247 // Check if we have a signing certificate attribute.
2248 DWORD nSignedAttributes = 0;
2249 if (CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, nullptr, &nSignedAttributes))
2251 std::unique_ptr<BYTE[]> pSignedAttributesBuf(new BYTE[nSignedAttributes]);
2252 if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, pSignedAttributesBuf.get(), &nSignedAttributes))
2254 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() authenticated failed");
2255 return false;
2257 auto pSignedAttributes = reinterpret_cast<PCRYPT_ATTRIBUTES>(pSignedAttributesBuf.get());
2258 for (size_t nAttr = 0; nAttr < pSignedAttributes->cAttr; ++nAttr)
2260 CRYPT_ATTRIBUTE& rAttr = pSignedAttributes->rgAttr[nAttr];
2262 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
2263 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
2264 * smime(16) id-aa(2) 47 }
2266 if (OString("1.2.840.113549.1.9.16.2.47") == rAttr.pszObjId)
2268 rInformation.bHasSigningCertificate = true;
2269 break;
2274 // Get the unauthorized attributes.
2275 nSignedAttributes = 0;
2276 if (CryptMsgGetParam(hMsg, CMSG_SIGNER_UNAUTH_ATTR_PARAM, 0, nullptr, &nSignedAttributes))
2278 std::unique_ptr<BYTE[]> pSignedAttributesBuf(new BYTE[nSignedAttributes]);
2279 if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_UNAUTH_ATTR_PARAM, 0, pSignedAttributesBuf.get(), &nSignedAttributes))
2281 SAL_WARN("svl.crypto", "ValidateSignature: CryptMsgGetParam() unauthenticated failed");
2282 return false;
2284 auto pSignedAttributes = reinterpret_cast<PCRYPT_ATTRIBUTES>(pSignedAttributesBuf.get());
2285 for (size_t nAttr = 0; nAttr < pSignedAttributes->cAttr; ++nAttr)
2287 CRYPT_ATTRIBUTE& rAttr = pSignedAttributes->rgAttr[nAttr];
2288 // Timestamp blob
2289 if (OString("1.2.840.113549.1.9.16.2.14") == rAttr.pszObjId)
2291 PCRYPT_TIMESTAMP_CONTEXT pTsContext;
2292 if (!CryptVerifyTimeStampSignature(rAttr.rgValue->pbData, rAttr.rgValue->cbData, nullptr, 0, nullptr, &pTsContext, nullptr, nullptr))
2294 SAL_WARN("svl.crypto", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
2295 break;
2298 DateTime aDateTime = DateTime::CreateFromWin32FileDateTime(pTsContext->pTimeStamp->ftTime.dwLowDateTime, pTsContext->pTimeStamp->ftTime.dwHighDateTime);
2300 // Then convert to a local UNO DateTime.
2301 aDateTime.ConvertToLocalTime();
2302 rInformation.stDateTime = aDateTime.GetUNODateTime();
2303 if (rInformation.ouDateTime.isEmpty())
2305 OUStringBuffer rBuffer;
2306 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetYear()));
2307 rBuffer.append('-');
2308 if (aDateTime.GetMonth() < 10)
2309 rBuffer.append('0');
2310 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetMonth()));
2311 rBuffer.append('-');
2312 if (aDateTime.GetDay() < 10)
2313 rBuffer.append('0');
2314 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetDay()));
2315 rInformation.ouDateTime = rBuffer.makeStringAndClear();
2317 break;
2322 CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
2323 CryptMsgClose(hMsg);
2324 return true;
2325 #else
2326 // Not implemented.
2327 (void)aData;
2328 (void)bNonDetached;
2329 (void)aSignature;
2330 (void)rInformation;
2331 return false;
2332 #endif
2335 bool Signing::Verify(SvStream& rStream,
2336 const std::vector<std::pair<size_t, size_t>>& aByteRanges,
2337 const bool bNonDetached,
2338 const std::vector<unsigned char>& aSignature,
2339 SignatureInformation& rInformation)
2341 #if defined(SVL_CRYPTO_NSS) || defined(SVL_CRYPTO_MSCRYPTO)
2343 std::vector<unsigned char> buffer;
2345 // Copy the byte ranges into a single buffer.
2346 for (const auto& rByteRange : aByteRanges)
2348 rStream.Seek(rByteRange.first);
2349 const size_t size = buffer.size();
2350 buffer.resize(size + rByteRange.second);
2351 rStream.ReadBytes(buffer.data() + size, rByteRange.second);
2354 return Verify(buffer, bNonDetached, aSignature, rInformation);
2356 #else
2357 // Not implemented.
2358 (void)rStream;
2359 (void)aByteRanges;
2360 (void)bNonDetached;
2361 (void)aSignature;
2362 (void)rInformation;
2363 return false;
2364 #endif
2369 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */