Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Alexander_Naumov:SLE12
mozilla-nss
nss-RSA_signature_forgery.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File nss-RSA_signature_forgery.patch of Package mozilla-nss
Fix for RSA signature forging bug MFSA-2014-73/CVE-2014-1568 (bmo#1064636, bmo#1069405) bnc#897890 diff --git a/lib/cryptohi/secvfy.c b/lib/cryptohi/secvfy.c --- a/lib/cryptohi/secvfy.c +++ b/lib/cryptohi/secvfy.c @@ -7,121 +7,165 @@ #include <stdio.h> #include "cryptohi.h" #include "sechash.h" #include "keyhi.h" #include "secasn1.h" #include "secoid.h" #include "pk11func.h" +#include "pkcs1sig.h" #include "secdig.h" #include "secerr.h" #include "keyi.h" /* -** Decrypt signature block using public key -** Store the hash algorithm oid tag in *tagp -** Store the digest in the digest buffer -** Store the digest length in *digestlen +** Recover the DigestInfo from an RSA PKCS#1 signature. +** +** If givenDigestAlg != SEC_OID_UNKNOWN, copy givenDigestAlg to digestAlgOut. +** Otherwise, parse the DigestInfo structure and store the decoded digest +** algorithm into digestAlgOut. +** +** Store the encoded DigestInfo into digestInfo. +** Store the DigestInfo length into digestInfoLen. +** +** This function does *not* verify that the AlgorithmIdentifier in the +** DigestInfo identifies givenDigestAlg or that the DigestInfo is encoded +** correctly; verifyPKCS1DigestInfo does that. +** ** XXX this is assuming that the signature algorithm has WITH_RSA_ENCRYPTION */ static SECStatus -DecryptSigBlock(SECOidTag *tagp, unsigned char *digest, - unsigned int *digestlen, unsigned int maxdigestlen, - SECKEYPublicKey *key, const SECItem *sig, char *wincx) +recoverPKCS1DigestInfo(SECOidTag givenDigestAlg, + /*out*/ SECOidTag* digestAlgOut, + /*out*/ unsigned char** digestInfo, + /*out*/ unsigned int* digestInfoLen, + SECKEYPublicKey* key, + const SECItem* sig, void* wincx) { - SGNDigestInfo *di = NULL; - unsigned char *buf = NULL; - SECStatus rv; - SECOidTag tag; - SECItem it; + SGNDigestInfo* di = NULL; + SECItem it; + PRBool rv = SECSuccess; - if (key == NULL) goto loser; + PORT_Assert(digestAlgOut); + PORT_Assert(digestInfo); + PORT_Assert(digestInfoLen); + PORT_Assert(key); + PORT_Assert(key->keyType == rsaKey); + PORT_Assert(sig); + it.data = NULL; it.len = SECKEY_PublicKeyStrength(key); - if (!it.len) goto loser; - it.data = buf = (unsigned char *)PORT_Alloc(it.len); - if (!buf) goto loser; + if (it.len != 0) { + it.data = (unsigned char *)PORT_Alloc(it.len); + } + if (it.len == 0 || it.data == NULL ) { + rv = SECFailure; + } - /* decrypt the block */ - rv = PK11_VerifyRecover(key, (SECItem *)sig, &it, wincx); - if (rv != SECSuccess) goto loser; + if (rv == SECSuccess) { + /* decrypt the block */ + rv = PK11_VerifyRecover(key, sig, &it, wincx); + } + + if (rv == SECSuccess) { + if (givenDigestAlg != SEC_OID_UNKNOWN) { + /* We don't need to parse the DigestInfo if the caller gave us the + * digest algorithm to use. Later verifyPKCS1DigestInfo will verify + * that the DigestInfo identifies the given digest algorithm and + * that the DigestInfo is encoded absolutely correctly. + */ + *digestInfoLen = it.len; + *digestInfo = (unsigned char*)it.data; + *digestAlgOut = givenDigestAlg; + return SECSuccess; + } + } - di = SGN_DecodeDigestInfo(&it); - if (di == NULL) goto sigloser; + if (rv == SECSuccess) { + /* The caller didn't specify a digest algorithm to use, so choose the + * digest algorithm by parsing the AlgorithmIdentifier within the + * DigestInfo. + */ + di = SGN_DecodeDigestInfo(&it); + if (!di) { + rv = SECFailure; + } + } - /* - ** Finally we have the digest info; now we can extract the algorithm - ** ID and the signature block - */ - tag = SECOID_GetAlgorithmTag(&di->digestAlgorithm); - /* Check that tag is an appropriate algorithm */ - if (tag == SEC_OID_UNKNOWN) { - goto sigloser; + if (rv == SECSuccess) { + *digestAlgOut = SECOID_GetAlgorithmTag(&di->digestAlgorithm); + if (*digestAlgOut == SEC_OID_UNKNOWN) { + rv = SECFailure; + } } - /* make sure the "parameters" are not too bogus. */ - if (di->digestAlgorithm.parameters.len > 2) { - goto sigloser; + + if (di) { + SGN_DestroyDigestInfo(di); } - if (di->digest.len > maxdigestlen) { - PORT_SetError(SEC_ERROR_OUTPUT_LEN); - goto loser; + + if (rv == SECSuccess) { + *digestInfoLen = it.len; + *digestInfo = (unsigned char*)it.data; + } else { + if (it.data) { + PORT_Free(it.data); + } + *digestInfo = NULL; + *digestInfoLen = 0; + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); } - PORT_Memcpy(digest, di->digest.data, di->digest.len); - *tagp = tag; - *digestlen = di->digest.len; - goto done; - sigloser: - PORT_SetError(SEC_ERROR_BAD_SIGNATURE); - - loser: - rv = SECFailure; - - done: - if (di != NULL) SGN_DestroyDigestInfo(di); - if (buf != NULL) PORT_Free(buf); - return rv; } - struct VFYContextStr { SECOidTag hashAlg; /* the hash algorithm */ SECKEYPublicKey *key; /* * This buffer holds either the digest or the full signature * depending on the type of the signature (key->keyType). It is * defined as a union to make sure it always has enough space. * * Use the "buffer" union member to reference the buffer. * Note: do not take the size of the "buffer" union member. Take * the size of the union or some other union member instead. */ union { unsigned char buffer[1]; - /* the digest in the decrypted RSA signature */ - unsigned char rsadigest[HASH_LENGTH_MAX]; /* the full DSA signature... 40 bytes */ unsigned char dsasig[DSA_MAX_SIGNATURE_LEN]; /* the full ECDSA signature */ unsigned char ecdsasig[2 * MAX_ECKEY_LEN]; } u; - unsigned int rsadigestlen; + unsigned int pkcs1RSADigestInfoLen; + /* the encoded DigestInfo from a RSA PKCS#1 signature */ + unsigned char *pkcs1RSADigestInfo; void * wincx; void *hashcx; const SECHashObject *hashobj; SECOidTag encAlg; /* enc alg */ PRBool hasSignature; /* true if the signature was provided in the * VFY_CreateContext call. If false, the * signature must be provided with a * VFY_EndWithSignature call. */ }; +static SECStatus +verifyPKCS1DigestInfo(const VFYContext* cx, const SECItem* digest) +{ + SECItem pkcs1DigestInfo; + pkcs1DigestInfo.data = cx->pkcs1RSADigestInfo; + pkcs1DigestInfo.len = cx->pkcs1RSADigestInfoLen; + return _SGN_VerifyPKCS1DigestInfo( + cx->hashAlg, digest, &pkcs1DigestInfo, + PR_TRUE /*XXX: unsafeAllowMissingParameters*/); +} + /* * decode the ECDSA or DSA signature from it's DER wrapping. * The unwrapped/raw signature is placed in the buffer pointed * to by dsig and has enough room for len bytes. */ static SECStatus decodeECorDSASignature(SECOidTag algid, const SECItem *sig, unsigned char *dsig, unsigned int len) { @@ -371,26 +415,26 @@ vfy_CreateContext(const SECKEYPublicKey goto loser; } cx->wincx = wincx; cx->hasSignature = (sig != NULL); cx->encAlg = encAlg; cx->hashAlg = hashAlg; cx->key = SECKEY_CopyPublicKey(key); + cx->pkcs1RSADigestInfo = NULL; rv = SECSuccess; if (sig) { switch (type) { case rsaKey: - rv = DecryptSigBlock(&cx->hashAlg, cx->u.buffer, &cx->rsadigestlen, - HASH_LENGTH_MAX, cx->key, sig, (char*)wincx); - if (cx->hashAlg != hashAlg && hashAlg != SEC_OID_UNKNOWN) { - PORT_SetError(SEC_ERROR_BAD_SIGNATURE); - rv = SECFailure; - } + rv = recoverPKCS1DigestInfo(hashAlg, &cx->hashAlg, + &cx->pkcs1RSADigestInfo, + &cx->pkcs1RSADigestInfoLen, + cx->key, + sig, wincx); break; case dsaKey: case ecKey: sigLen = SECKEY_SignatureLen(key); if (sigLen == 0) { /* error set by SECKEY_SignatureLen */ rv = SECFailure; break; @@ -464,16 +508,19 @@ VFY_DestroyContext(VFYContext *cx, PRBoo if (cx) { if (cx->hashcx != NULL) { (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE); cx->hashcx = NULL; } if (cx->key) { SECKEY_DestroyPublicKey(cx->key); } + if (cx->pkcs1RSADigestInfo) { + PORT_Free(cx->pkcs1RSADigestInfo); + } if (freeit) { PORT_ZFree(cx, sizeof(VFYContext)); } } } SECStatus VFY_Begin(VFYContext *cx) @@ -543,31 +590,35 @@ VFY_EndWithSignature(VFYContext *cx, SEC hash.data = final; hash.len = part; if (PK11_Verify(cx->key,&dsasig,&hash,cx->wincx) != SECSuccess) { PORT_SetError(SEC_ERROR_BAD_SIGNATURE); return SECFailure; } break; case rsaKey: + { + SECItem digest; + digest.data = final; + digest.len = part; if (sig) { - SECOidTag hashid = SEC_OID_UNKNOWN; - rv = DecryptSigBlock(&hashid, cx->u.buffer, &cx->rsadigestlen, - HASH_LENGTH_MAX, cx->key, sig, (char*)cx->wincx); - if ((rv != SECSuccess) || (hashid != cx->hashAlg)) { - PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + SECOidTag hashid; + PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN); + rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid, + &cx->pkcs1RSADigestInfo, + &cx->pkcs1RSADigestInfoLen, + cx->key, + sig, cx->wincx); + PORT_Assert(cx->hashAlg == hashid); + if (rv != SECSuccess) { return SECFailure; } } - if ((part != cx->rsadigestlen) || - PORT_Memcmp(final, cx->u.buffer, part)) { - PORT_SetError(SEC_ERROR_BAD_SIGNATURE); - return SECFailure; - } - break; + return verifyPKCS1DigestInfo(cx, &digest); + } default: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); return SECFailure; /* shouldn't happen */ } return SECSuccess; } SECStatus @@ -590,22 +641,17 @@ vfy_VerifyDigest(const SECItem *digest, SECItem dsasig; /* also used for ECDSA */ rv = SECFailure; cx = vfy_CreateContext(key, sig, encAlg, hashAlg, NULL, wincx); if (cx != NULL) { switch (key->keyType) { case rsaKey: - if ((digest->len != cx->rsadigestlen) || - PORT_Memcmp(digest->data, cx->u.buffer, digest->len)) { - PORT_SetError(SEC_ERROR_BAD_SIGNATURE); - } else { - rv = SECSuccess; - } + rv = verifyPKCS1DigestInfo(cx, digest); break; case dsaKey: case ecKey: dsasig.data = cx->u.buffer; dsasig.len = SECKEY_SignatureLen(cx->key); if (dsasig.len == 0) { break; } diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c --- a/lib/softoken/pkcs11c.c +++ b/lib/softoken/pkcs11c.c @@ -18,16 +18,17 @@ * that created or generated them. */ #include "seccomon.h" #include "secitem.h" #include "secport.h" #include "blapi.h" #include "pkcs11.h" #include "pkcs11i.h" +#include "pkcs1sig.h" #include "lowkeyi.h" #include "secder.h" #include "secdig.h" #include "lowpbe.h" /* We do PBE below */ #include "pkcs11t.h" #include "secoid.h" #include "alghmac.h" #include "softoken.h" @@ -2851,75 +2852,52 @@ sftk_hashCheckSign(SFTKHashVerifyInfo *i return SECFailure; } return RSA_HashCheckSign(info->hashOid, info->key, sig, sigLen, digest, digestLen); } SECStatus -RSA_HashCheckSign(SECOidTag hashOid, NSSLOWKEYPublicKey *key, +RSA_HashCheckSign(SECOidTag digestOid, NSSLOWKEYPublicKey *key, const unsigned char *sig, unsigned int sigLen, - const unsigned char *hash, unsigned int hashLen) + const unsigned char *digestData, unsigned int digestLen) { - SECItem it; - SGNDigestInfo *di = NULL; - SECStatus rv = SECSuccess; - - it.data = NULL; - it.len = nsslowkey_PublicModulusLen(key); - if (!it.len) { - goto loser; - } - - it.data = (unsigned char *)PORT_Alloc(it.len); - if (it.data == NULL) { - goto loser; - } - + unsigned char *pkcs1DigestInfoData; + SECItem pkcs1DigestInfo; + SECItem digest; + unsigned int bufferSize; + SECStatus rv; + + /* pkcs1DigestInfo.data must be less than key->u.rsa.modulus.len */ + bufferSize = key->u.rsa.modulus.len; + pkcs1DigestInfoData = PORT_ZAlloc(bufferSize); + if (!pkcs1DigestInfoData) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + pkcs1DigestInfo.data = pkcs1DigestInfoData; + pkcs1DigestInfo.len = bufferSize; + /* decrypt the block */ - rv = RSA_CheckSignRecover(&key->u.rsa, it.data, &it.len, it.len, sig, - sigLen); + rv = RSA_CheckSignRecover(&key->u.rsa, pkcs1DigestInfo.data, + &pkcs1DigestInfo.len, pkcs1DigestInfo.len, + sig, sigLen); if (rv != SECSuccess) { - goto loser; - } - - di = SGN_DecodeDigestInfo(&it); - if (di == NULL) { - goto loser; - } - if (di->digest.len != hashLen) { - goto loser; - } - - /* make sure the tag is OK */ - if (SECOID_GetAlgorithmTag(&di->digestAlgorithm) != hashOid) { - goto loser; - } - /* make sure the "parameters" are not too bogus. */ - if (di->digestAlgorithm.parameters.len > 2) { - goto loser; - } - /* Now check the signature */ - if (PORT_Memcmp(hash, di->digest.data, di->digest.len) == 0) { - goto done; - } - - loser: - PORT_SetError(SEC_ERROR_BAD_SIGNATURE); - rv = SECFailure; - - done: - if (it.data != NULL) { - PORT_Free(it.data); - } - if (di != NULL) { - SGN_DestroyDigestInfo(di); - } - + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + } else { + digest.data = (PRUint8*) digestData; + digest.len = digestLen; + rv = _SGN_VerifyPKCS1DigestInfo( + digestOid, &digest, &pkcs1DigestInfo, + PR_TRUE /*XXX: unsafeAllowMissingParameters*/); + } + + PORT_Free(pkcs1DigestInfoData); return rv; } static SECStatus sftk_RSACheckSign(NSSLOWKEYPublicKey *key, const unsigned char *sig, unsigned int sigLen, const unsigned char *digest, unsigned int digestLen) { diff --git a/lib/util/manifest.mn b/lib/util/manifest.mn --- a/lib/util/manifest.mn +++ b/lib/util/manifest.mn @@ -17,16 +17,17 @@ EXPORTS = \ nssrwlkt.h \ nssutil.h \ pkcs11.h \ pkcs11f.h \ pkcs11p.h \ pkcs11t.h \ pkcs11n.h \ pkcs11u.h \ + pkcs1sig.h \ portreg.h \ secasn1.h \ secasn1t.h \ seccomon.h \ secder.h \ secdert.h \ secdig.h \ secdigt.h \ @@ -53,16 +54,17 @@ CSRCS = \ dersubr.c \ dertime.c \ errstrs.c \ nssb64d.c \ nssb64e.c \ nssrwlk.c \ nssilock.c \ oidstring.c \ + pkcs1sig.c \ portreg.c \ secalgid.c \ secasn1d.c \ secasn1e.c \ secasn1u.c \ secitem.c \ secload.c \ secoid.c \ diff --git a/lib/util/nssutil.def b/lib/util/nssutil.def --- a/lib/util/nssutil.def +++ b/lib/util/nssutil.def @@ -266,8 +266,14 @@ NSSUTIL_QuoteSize; SECITEM_AllocArray; SECITEM_DupArray; SECITEM_FreeArray; SECITEM_ReallocItemV2; SECITEM_ZfreeArray; ;+ local: ;+ *; ;+}; +;+NSSUTIL_3.17.1 { # NSS Utilities 3.17.1 release +;+ global: +_SGN_VerifyPKCS1DigestInfo; +;+ local: +;+ *; +;+}; diff --git a/lib/util/pkcs1sig.c b/lib/util/pkcs1sig.c new file mode 100644 --- /dev/null +++ b/lib/util/pkcs1sig.c @@ -0,0 +1,169 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "pkcs1sig.h" +#include "hasht.h" +#include "secerr.h" +#include "secasn1t.h" +#include "secoid.h" + +typedef struct pkcs1PrefixStr pkcs1Prefix; +struct pkcs1PrefixStr { + unsigned int len; + PRUint8 *data; +}; + +typedef struct pkcs1PrefixesStr pkcs1Prefixes; +struct pkcs1PrefixesStr { + unsigned int digestLen; + pkcs1Prefix prefixWithParams; + pkcs1Prefix prefixWithoutParams; +}; + +/* The value for SGN_PKCS1_DIGESTINFO_MAX_PREFIX_LEN_EXCLUDING_OID is based on + * the possible prefix encodings as explained below. + */ +#define MAX_PREFIX_LEN_EXCLUDING_OID 10 + +static SECStatus +encodePrefix(const SECOidData *hashOid, unsigned int digestLen, + pkcs1Prefix *prefix, PRBool withParams) +{ + /* with params coding is: + * Sequence (2 bytes) { + * Sequence (2 bytes) { + * Oid (2 bytes) { + * Oid value (derOid->oid.len) + * } + * NULL (2 bytes) + * } + * OCTECT (2 bytes); + * + * without params coding is: + * Sequence (2 bytes) { + * Sequence (2 bytes) { + * Oid (2 bytes) { + * Oid value (derOid->oid.len) + * } + * } + * OCTECT (2 bytes); + */ + + unsigned int innerSeqLen = 2 + hashOid->oid.len; + unsigned int outerSeqLen = 2 + innerSeqLen + 2 + digestLen; + unsigned int extra = 0; + + if (withParams) { + innerSeqLen += 2; + outerSeqLen += 2; + extra = 2; + } + + if (innerSeqLen >= 128 || + outerSeqLen >= 128 || + (outerSeqLen + 2 - digestLen) > + (MAX_PREFIX_LEN_EXCLUDING_OID + hashOid->oid.len)) { + /* this is actually a library failure, It shouldn't happen */ + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + prefix->len = 6 + hashOid->oid.len + extra + 2; + prefix->data = PORT_Alloc(prefix->len); + if (!prefix->data) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + prefix->data[0] = SEC_ASN1_SEQUENCE|SEC_ASN1_CONSTRUCTED; + prefix->data[1] = outerSeqLen; + prefix->data[2] = SEC_ASN1_SEQUENCE|SEC_ASN1_CONSTRUCTED; + prefix->data[3] = innerSeqLen; + prefix->data[4] = SEC_ASN1_OBJECT_ID; + prefix->data[5] = hashOid->oid.len; + PORT_Memcpy(&prefix->data[6], hashOid->oid.data, hashOid->oid.len); + if (withParams) { + prefix->data[6 + hashOid->oid.len] = SEC_ASN1_NULL; + prefix->data[6 + hashOid->oid.len + 1] = 0; + } + prefix->data[6 + hashOid->oid.len + extra] = SEC_ASN1_OCTET_STRING; + prefix->data[6 + hashOid->oid.len + extra + 1] = digestLen; + + return SECSuccess; +} + +SECStatus +_SGN_VerifyPKCS1DigestInfo(SECOidTag digestAlg, + const SECItem* digest, + const SECItem* dataRecoveredFromSignature, + PRBool unsafeAllowMissingParameters) +{ + SECOidData *hashOid; + pkcs1Prefixes pp; + const pkcs1Prefix* expectedPrefix; + SECStatus rv, rv2, rv3; + + if (!digest || !digest->data || + !dataRecoveredFromSignature || !dataRecoveredFromSignature->data) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + hashOid = SECOID_FindOIDByTag(digestAlg); + if (hashOid == NULL) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + pp.digestLen = digest->len; + pp.prefixWithParams.data = NULL; + pp.prefixWithoutParams.data = NULL; + + rv2 = encodePrefix(hashOid, pp.digestLen, &pp.prefixWithParams, PR_TRUE); + rv3 = encodePrefix(hashOid, pp.digestLen, &pp.prefixWithoutParams, PR_FALSE); + + rv = SECSuccess; + if (rv2 != SECSuccess || rv3 != SECSuccess) { + rv = SECFailure; + } + + if (rv == SECSuccess) { + /* We don't attempt to avoid timing attacks on these comparisons because + * signature verification is a public key operation, not a private key + * operation. + */ + + if (dataRecoveredFromSignature->len == + pp.prefixWithParams.len + pp.digestLen) { + expectedPrefix = &pp.prefixWithParams; + } else if (unsafeAllowMissingParameters && + dataRecoveredFromSignature->len == + pp.prefixWithoutParams.len + pp.digestLen) { + expectedPrefix = &pp.prefixWithoutParams; + } else { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + } + } + + if (rv == SECSuccess) { + if (memcmp(dataRecoveredFromSignature->data, expectedPrefix->data, + expectedPrefix->len) || + memcmp(dataRecoveredFromSignature->data + expectedPrefix->len, + digest->data, digest->len)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + } + } + + if (pp.prefixWithParams.data) { + PORT_Free(pp.prefixWithParams.data); + } + if (pp.prefixWithoutParams.data) { + PORT_Free(pp.prefixWithoutParams.data); + } + + return rv; +} diff --git a/lib/util/pkcs1sig.h b/lib/util/pkcs1sig.h new file mode 100644 --- /dev/null +++ b/lib/util/pkcs1sig.h @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef _PKCS1SIG_H_ +#define _PKCS1SIG_H_ + +#include "hasht.h" +#include "seccomon.h" +#include "secoidt.h" + +/* SGN_VerifyPKCS1DigestInfo verifies that the length of the digest is correct + * for the given algorithm, then verifies that the recovered data from the + * PKCS#1 signature is a properly-formatted DigestInfo that identifies the + * given digest algorithm, then verifies that the digest in the DigestInfo + * matches the given digest. + * + * dataRecoveredFromSignature must be the result of calling PK11_VerifyRecover + * or equivalent. + * + * If unsafeAllowMissingParameters is true (not recommended), then a DigestInfo + * without the mandatory ASN.1 NULL parameter will also be accepted. + */ +SECStatus _SGN_VerifyPKCS1DigestInfo(SECOidTag digestAlg, + const SECItem* digest, + const SECItem* dataRecoveredFromSignature, + PRBool unsafeAllowMissingParameters); + +#endif /* _PKCS1SIG_H_ */
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor