michael@0: /* michael@0: * Verification stuff. michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include "cryptohi.h" michael@0: #include "sechash.h" michael@0: #include "keyhi.h" michael@0: #include "secasn1.h" michael@0: #include "secoid.h" michael@0: #include "pk11func.h" michael@0: #include "pkcs1sig.h" michael@0: #include "secdig.h" michael@0: #include "secerr.h" michael@0: #include "keyi.h" michael@0: michael@0: /* michael@0: ** Recover the DigestInfo from an RSA PKCS#1 signature. michael@0: ** michael@0: ** If givenDigestAlg != SEC_OID_UNKNOWN, copy givenDigestAlg to digestAlgOut. michael@0: ** Otherwise, parse the DigestInfo structure and store the decoded digest michael@0: ** algorithm into digestAlgOut. michael@0: ** michael@0: ** Store the encoded DigestInfo into digestInfo. michael@0: ** Store the DigestInfo length into digestInfoLen. michael@0: ** michael@0: ** This function does *not* verify that the AlgorithmIdentifier in the michael@0: ** DigestInfo identifies givenDigestAlg or that the DigestInfo is encoded michael@0: ** correctly; verifyPKCS1DigestInfo does that. michael@0: ** michael@0: ** XXX this is assuming that the signature algorithm has WITH_RSA_ENCRYPTION michael@0: */ michael@0: static SECStatus michael@0: recoverPKCS1DigestInfo(SECOidTag givenDigestAlg, michael@0: /*out*/ SECOidTag* digestAlgOut, michael@0: /*out*/ unsigned char** digestInfo, michael@0: /*out*/ unsigned int* digestInfoLen, michael@0: SECKEYPublicKey* key, michael@0: const SECItem* sig, void* wincx) michael@0: { michael@0: SGNDigestInfo* di = NULL; michael@0: SECItem it; michael@0: PRBool rv = SECSuccess; michael@0: michael@0: PORT_Assert(digestAlgOut); michael@0: PORT_Assert(digestInfo); michael@0: PORT_Assert(digestInfoLen); michael@0: PORT_Assert(key); michael@0: PORT_Assert(key->keyType == rsaKey); michael@0: PORT_Assert(sig); michael@0: michael@0: it.data = NULL; michael@0: it.len = SECKEY_PublicKeyStrength(key); michael@0: if (it.len != 0) { michael@0: it.data = (unsigned char *)PORT_Alloc(it.len); michael@0: } michael@0: if (it.len == 0 || it.data == NULL ) { michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: if (rv == SECSuccess) { michael@0: /* decrypt the block */ michael@0: rv = PK11_VerifyRecover(key, sig, &it, wincx); michael@0: } michael@0: michael@0: if (rv == SECSuccess) { michael@0: if (givenDigestAlg != SEC_OID_UNKNOWN) { michael@0: /* We don't need to parse the DigestInfo if the caller gave us the michael@0: * digest algorithm to use. Later verifyPKCS1DigestInfo will verify michael@0: * that the DigestInfo identifies the given digest algorithm and michael@0: * that the DigestInfo is encoded absolutely correctly. michael@0: */ michael@0: *digestInfoLen = it.len; michael@0: *digestInfo = (unsigned char*)it.data; michael@0: *digestAlgOut = givenDigestAlg; michael@0: return SECSuccess; michael@0: } michael@0: } michael@0: michael@0: if (rv == SECSuccess) { michael@0: /* The caller didn't specify a digest algorithm to use, so choose the michael@0: * digest algorithm by parsing the AlgorithmIdentifier within the michael@0: * DigestInfo. michael@0: */ michael@0: di = SGN_DecodeDigestInfo(&it); michael@0: if (!di) { michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: michael@0: if (rv == SECSuccess) { michael@0: *digestAlgOut = SECOID_GetAlgorithmTag(&di->digestAlgorithm); michael@0: if (*digestAlgOut == SEC_OID_UNKNOWN) { michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: michael@0: if (di) { michael@0: SGN_DestroyDigestInfo(di); michael@0: } michael@0: michael@0: if (rv == SECSuccess) { michael@0: *digestInfoLen = it.len; michael@0: *digestInfo = (unsigned char*)it.data; michael@0: } else { michael@0: if (it.data) { michael@0: PORT_Free(it.data); michael@0: } michael@0: *digestInfo = NULL; michael@0: *digestInfoLen = 0; michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: struct VFYContextStr { michael@0: SECOidTag hashAlg; /* the hash algorithm */ michael@0: SECKEYPublicKey *key; michael@0: /* michael@0: * This buffer holds either the digest or the full signature michael@0: * depending on the type of the signature (key->keyType). It is michael@0: * defined as a union to make sure it always has enough space. michael@0: * michael@0: * Use the "buffer" union member to reference the buffer. michael@0: * Note: do not take the size of the "buffer" union member. Take michael@0: * the size of the union or some other union member instead. michael@0: */ michael@0: union { michael@0: unsigned char buffer[1]; michael@0: michael@0: /* the full DSA signature... 40 bytes */ michael@0: unsigned char dsasig[DSA_MAX_SIGNATURE_LEN]; michael@0: /* the full ECDSA signature */ michael@0: unsigned char ecdsasig[2 * MAX_ECKEY_LEN]; michael@0: } u; michael@0: unsigned int pkcs1RSADigestInfoLen; michael@0: /* the encoded DigestInfo from a RSA PKCS#1 signature */ michael@0: unsigned char *pkcs1RSADigestInfo; michael@0: void * wincx; michael@0: void *hashcx; michael@0: const SECHashObject *hashobj; michael@0: SECOidTag encAlg; /* enc alg */ michael@0: PRBool hasSignature; /* true if the signature was provided in the michael@0: * VFY_CreateContext call. If false, the michael@0: * signature must be provided with a michael@0: * VFY_EndWithSignature call. */ michael@0: }; michael@0: michael@0: static SECStatus michael@0: verifyPKCS1DigestInfo(const VFYContext* cx, const SECItem* digest) michael@0: { michael@0: SECItem pkcs1DigestInfo; michael@0: pkcs1DigestInfo.data = cx->pkcs1RSADigestInfo; michael@0: pkcs1DigestInfo.len = cx->pkcs1RSADigestInfoLen; michael@0: return _SGN_VerifyPKCS1DigestInfo( michael@0: cx->hashAlg, digest, &pkcs1DigestInfo, michael@0: PR_TRUE /*XXX: unsafeAllowMissingParameters*/); michael@0: } michael@0: michael@0: /* michael@0: * decode the ECDSA or DSA signature from it's DER wrapping. michael@0: * The unwrapped/raw signature is placed in the buffer pointed michael@0: * to by dsig and has enough room for len bytes. michael@0: */ michael@0: static SECStatus michael@0: decodeECorDSASignature(SECOidTag algid, const SECItem *sig, unsigned char *dsig, michael@0: unsigned int len) { michael@0: SECItem *dsasig = NULL; /* also used for ECDSA */ michael@0: SECStatus rv=SECSuccess; michael@0: michael@0: if ((algid != SEC_OID_ANSIX9_DSA_SIGNATURE) && michael@0: (algid != SEC_OID_ANSIX962_EC_PUBLIC_KEY) ) { michael@0: if (sig->len != len) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return SECFailure; michael@0: } michael@0: michael@0: PORT_Memcpy(dsig, sig->data, sig->len); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: if (algid == SEC_OID_ANSIX962_EC_PUBLIC_KEY) { michael@0: if (len > MAX_ECKEY_LEN * 2) { michael@0: PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: dsasig = DSAU_DecodeDerSigToLen((SECItem *)sig, len); michael@0: michael@0: if ((dsasig == NULL) || (dsasig->len != len)) { michael@0: rv = SECFailure; michael@0: } else { michael@0: PORT_Memcpy(dsig, dsasig->data, dsasig->len); michael@0: } michael@0: michael@0: if (dsasig != NULL) SECITEM_FreeItem(dsasig, PR_TRUE); michael@0: if (rv == SECFailure) PORT_SetError(SEC_ERROR_BAD_DER); michael@0: return rv; michael@0: } michael@0: michael@0: const SEC_ASN1Template hashParameterTemplate[] = michael@0: { michael@0: { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) }, michael@0: { SEC_ASN1_OBJECT_ID, 0 }, michael@0: { SEC_ASN1_SKIP_REST }, michael@0: { 0, } michael@0: }; michael@0: michael@0: /* michael@0: * Pulls the hash algorithm, signing algorithm, and key type out of a michael@0: * composite algorithm. michael@0: * michael@0: * sigAlg: the composite algorithm to dissect. michael@0: * hashalg: address of a SECOidTag which will be set with the hash algorithm. michael@0: * encalg: address of a SECOidTag which will be set with the signing alg. michael@0: * michael@0: * Returns: SECSuccess if the algorithm was acceptable, SECFailure if the michael@0: * algorithm was not found or was not a signing algorithm. michael@0: */ michael@0: SECStatus michael@0: sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg, michael@0: const SECItem *param, SECOidTag *encalg, SECOidTag *hashalg) michael@0: { michael@0: int len; michael@0: PLArenaPool *arena; michael@0: SECStatus rv; michael@0: SECItem oid; michael@0: michael@0: PR_ASSERT(hashalg!=NULL); michael@0: PR_ASSERT(encalg!=NULL); michael@0: michael@0: switch (sigAlg) { michael@0: /* We probably shouldn't be generating MD2 signatures either */ michael@0: case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: michael@0: *hashalg = SEC_OID_MD2; michael@0: break; michael@0: case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: michael@0: *hashalg = SEC_OID_MD5; michael@0: break; michael@0: case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: michael@0: case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: michael@0: *hashalg = SEC_OID_SHA1; michael@0: break; michael@0: case SEC_OID_PKCS1_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: michael@0: *hashalg = SEC_OID_UNKNOWN; /* get it from the RSA signature */ michael@0: break; michael@0: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: michael@0: case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST: michael@0: *hashalg = SEC_OID_SHA224; michael@0: break; michael@0: case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: michael@0: case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: michael@0: *hashalg = SEC_OID_SHA256; michael@0: break; michael@0: case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: michael@0: case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: michael@0: *hashalg = SEC_OID_SHA384; michael@0: break; michael@0: case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: michael@0: case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: michael@0: *hashalg = SEC_OID_SHA512; michael@0: break; michael@0: michael@0: /* what about normal DSA? */ michael@0: case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: michael@0: case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: michael@0: *hashalg = SEC_OID_SHA1; michael@0: break; michael@0: case SEC_OID_MISSI_DSS: michael@0: case SEC_OID_MISSI_KEA_DSS: michael@0: case SEC_OID_MISSI_KEA_DSS_OLD: michael@0: case SEC_OID_MISSI_DSS_OLD: michael@0: *hashalg = SEC_OID_SHA1; michael@0: break; michael@0: case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST: michael@0: /* This is an EC algorithm. Recommended means the largest michael@0: * hash algorithm that is not reduced by the keysize of michael@0: * the EC algorithm. Note that key strength is in bytes and michael@0: * algorithms are specified in bits. Never use an algorithm michael@0: * weaker than sha1. */ michael@0: len = SECKEY_PublicKeyStrength(key); michael@0: if (len < 28) { /* 28 bytes == 224 bits */ michael@0: *hashalg = SEC_OID_SHA1; michael@0: } else if (len < 32) { /* 32 bytes == 256 bits */ michael@0: *hashalg = SEC_OID_SHA224; michael@0: } else if (len < 48) { /* 48 bytes == 384 bits */ michael@0: *hashalg = SEC_OID_SHA256; michael@0: } else if (len < 64) { /* 48 bytes == 512 bits */ michael@0: *hashalg = SEC_OID_SHA384; michael@0: } else { michael@0: /* use the largest in this case */ michael@0: *hashalg = SEC_OID_SHA512; michael@0: } michael@0: break; michael@0: case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST: michael@0: if (param == NULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); michael@0: return SECFailure; michael@0: } michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (arena == NULL) { michael@0: return SECFailure; michael@0: } michael@0: rv = SEC_QuickDERDecodeItem(arena, &oid, hashParameterTemplate, param); michael@0: if (rv == SECSuccess) { michael@0: *hashalg = SECOID_FindOIDTag(&oid); michael@0: } michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: return rv; michael@0: } michael@0: /* only accept hash algorithms */ michael@0: if (HASH_GetHashTypeByOidTag(*hashalg) == HASH_AlgNULL) { michael@0: /* error set by HASH_GetHashTypeByOidTag */ michael@0: return SECFailure; michael@0: } michael@0: break; michael@0: /* we don't implement MD4 hashes */ michael@0: case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: michael@0: default: michael@0: PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); michael@0: return SECFailure; michael@0: } michael@0: /* get the "encryption" algorithm */ michael@0: switch (sigAlg) { michael@0: case SEC_OID_PKCS1_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: michael@0: case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: michael@0: case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: michael@0: case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: michael@0: *encalg = SEC_OID_PKCS1_RSA_ENCRYPTION; michael@0: break; michael@0: case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: michael@0: *encalg = SEC_OID_PKCS1_RSA_PSS_SIGNATURE; michael@0: break; michael@0: michael@0: /* what about normal DSA? */ michael@0: case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST: michael@0: case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST: michael@0: case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST: michael@0: case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST: michael@0: *encalg = SEC_OID_ANSIX9_DSA_SIGNATURE; michael@0: break; michael@0: case SEC_OID_MISSI_DSS: michael@0: case SEC_OID_MISSI_KEA_DSS: michael@0: case SEC_OID_MISSI_KEA_DSS_OLD: michael@0: case SEC_OID_MISSI_DSS_OLD: michael@0: *encalg = SEC_OID_MISSI_DSS; michael@0: break; michael@0: case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: michael@0: case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST: michael@0: case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST: michael@0: *encalg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; michael@0: break; michael@0: /* we don't implement MD4 hashes */ michael@0: case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: michael@0: default: michael@0: PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); michael@0: return SECFailure; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* michael@0: * we can verify signatures that come from 2 different sources: michael@0: * one in with the signature contains a signature oid, and the other michael@0: * in which the signature is managed by a Public key (encAlg) oid michael@0: * and a hash oid. The latter is the more basic, so that's what michael@0: * our base vfyCreate function takes. michael@0: * michael@0: * There is one noteworthy corner case, if we are using an RSA key, and the michael@0: * signature block is provided, then the hashAlg can be specified as michael@0: * SEC_OID_UNKNOWN. In this case, verify will use the hash oid supplied michael@0: * in the RSA signature block. michael@0: */ michael@0: static VFYContext * michael@0: vfy_CreateContext(const SECKEYPublicKey *key, const SECItem *sig, michael@0: SECOidTag encAlg, SECOidTag hashAlg, SECOidTag *hash, void *wincx) michael@0: { michael@0: VFYContext *cx; michael@0: SECStatus rv; michael@0: unsigned int sigLen; michael@0: KeyType type; michael@0: michael@0: /* make sure the encryption algorithm matches the key type */ michael@0: /* RSA-PSS algorithm can be used with both rsaKey and rsaPssKey */ michael@0: type = seckey_GetKeyType(encAlg); michael@0: if ((key->keyType != type) && michael@0: ((key->keyType != rsaKey) || (type != rsaPssKey))) { michael@0: PORT_SetError(SEC_ERROR_PKCS7_KEYALG_MISMATCH); michael@0: return NULL; michael@0: } michael@0: michael@0: cx = (VFYContext*) PORT_ZAlloc(sizeof(VFYContext)); michael@0: if (cx == NULL) { michael@0: goto loser; michael@0: } michael@0: michael@0: cx->wincx = wincx; michael@0: cx->hasSignature = (sig != NULL); michael@0: cx->encAlg = encAlg; michael@0: cx->hashAlg = hashAlg; michael@0: cx->key = SECKEY_CopyPublicKey(key); michael@0: cx->pkcs1RSADigestInfo = NULL; michael@0: rv = SECSuccess; michael@0: if (sig) { michael@0: switch (type) { michael@0: case rsaKey: michael@0: rv = recoverPKCS1DigestInfo(hashAlg, &cx->hashAlg, michael@0: &cx->pkcs1RSADigestInfo, michael@0: &cx->pkcs1RSADigestInfoLen, michael@0: cx->key, michael@0: sig, wincx); michael@0: break; michael@0: case dsaKey: michael@0: case ecKey: michael@0: sigLen = SECKEY_SignatureLen(key); michael@0: if (sigLen == 0) { michael@0: /* error set by SECKEY_SignatureLen */ michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: rv = decodeECorDSASignature(encAlg, sig, cx->u.buffer, sigLen); michael@0: break; michael@0: default: michael@0: rv = SECFailure; michael@0: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (rv) goto loser; michael@0: michael@0: /* check hash alg again, RSA may have changed it.*/ michael@0: if (HASH_GetHashTypeByOidTag(cx->hashAlg) == HASH_AlgNULL) { michael@0: /* error set by HASH_GetHashTypeByOidTag */ michael@0: goto loser; michael@0: } michael@0: michael@0: if (hash) { michael@0: *hash = cx->hashAlg; michael@0: } michael@0: return cx; michael@0: michael@0: loser: michael@0: if (cx) { michael@0: VFY_DestroyContext(cx, PR_TRUE); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: VFYContext * michael@0: VFY_CreateContext(SECKEYPublicKey *key, SECItem *sig, SECOidTag sigAlg, michael@0: void *wincx) michael@0: { michael@0: SECOidTag encAlg, hashAlg; michael@0: SECStatus rv = sec_DecodeSigAlg(key, sigAlg, NULL, &encAlg, &hashAlg); michael@0: if (rv != SECSuccess) { michael@0: return NULL; michael@0: } michael@0: return vfy_CreateContext(key, sig, encAlg, hashAlg, NULL, wincx); michael@0: } michael@0: michael@0: VFYContext * michael@0: VFY_CreateContextDirect(const SECKEYPublicKey *key, const SECItem *sig, michael@0: SECOidTag encAlg, SECOidTag hashAlg, michael@0: SECOidTag *hash, void *wincx) michael@0: { michael@0: return vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx); michael@0: } michael@0: michael@0: VFYContext * michael@0: VFY_CreateContextWithAlgorithmID(const SECKEYPublicKey *key, const SECItem *sig, michael@0: const SECAlgorithmID *sigAlgorithm, SECOidTag *hash, void *wincx) michael@0: { michael@0: SECOidTag encAlg, hashAlg; michael@0: SECStatus rv = sec_DecodeSigAlg(key, michael@0: SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm), michael@0: &sigAlgorithm->parameters, &encAlg, &hashAlg); michael@0: if (rv != SECSuccess) { michael@0: return NULL; michael@0: } michael@0: return vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx); michael@0: } michael@0: michael@0: void michael@0: VFY_DestroyContext(VFYContext *cx, PRBool freeit) michael@0: { michael@0: if (cx) { michael@0: if (cx->hashcx != NULL) { michael@0: (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE); michael@0: cx->hashcx = NULL; michael@0: } michael@0: if (cx->key) { michael@0: SECKEY_DestroyPublicKey(cx->key); michael@0: } michael@0: if (cx->pkcs1RSADigestInfo) { michael@0: PORT_Free(cx->pkcs1RSADigestInfo); michael@0: } michael@0: if (freeit) { michael@0: PORT_ZFree(cx, sizeof(VFYContext)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_Begin(VFYContext *cx) michael@0: { michael@0: if (cx->hashcx != NULL) { michael@0: (*cx->hashobj->destroy)(cx->hashcx, PR_TRUE); michael@0: cx->hashcx = NULL; michael@0: } michael@0: michael@0: cx->hashobj = HASH_GetHashObjectByOidTag(cx->hashAlg); michael@0: if (!cx->hashobj) michael@0: return SECFailure; /* error code is set */ michael@0: michael@0: cx->hashcx = (*cx->hashobj->create)(); michael@0: if (cx->hashcx == NULL) michael@0: return SECFailure; michael@0: michael@0: (*cx->hashobj->begin)(cx->hashcx); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_Update(VFYContext *cx, const unsigned char *input, unsigned inputLen) michael@0: { michael@0: if (cx->hashcx == NULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: (*cx->hashobj->update)(cx->hashcx, input, inputLen); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_EndWithSignature(VFYContext *cx, SECItem *sig) michael@0: { michael@0: unsigned char final[HASH_LENGTH_MAX]; michael@0: unsigned part; michael@0: SECItem hash,dsasig; /* dsasig is also used for ECDSA */ michael@0: SECStatus rv; michael@0: michael@0: if ((cx->hasSignature == PR_FALSE) && (sig == NULL)) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (cx->hashcx == NULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: (*cx->hashobj->end)(cx->hashcx, final, &part, sizeof(final)); michael@0: switch (cx->key->keyType) { michael@0: case ecKey: michael@0: case dsaKey: michael@0: dsasig.data = cx->u.buffer; michael@0: dsasig.len = SECKEY_SignatureLen(cx->key); michael@0: if (dsasig.len == 0) { michael@0: return SECFailure; michael@0: } michael@0: if (sig) { michael@0: rv = decodeECorDSASignature(cx->encAlg, sig, dsasig.data, michael@0: dsasig.len); michael@0: if (rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: hash.data = final; michael@0: hash.len = part; michael@0: if (PK11_Verify(cx->key,&dsasig,&hash,cx->wincx) != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: return SECFailure; michael@0: } michael@0: break; michael@0: case rsaKey: michael@0: { michael@0: SECItem digest; michael@0: digest.data = final; michael@0: digest.len = part; michael@0: if (sig) { michael@0: SECOidTag hashid; michael@0: PORT_Assert(cx->hashAlg != SEC_OID_UNKNOWN); michael@0: rv = recoverPKCS1DigestInfo(cx->hashAlg, &hashid, michael@0: &cx->pkcs1RSADigestInfo, michael@0: &cx->pkcs1RSADigestInfoLen, michael@0: cx->key, michael@0: sig, cx->wincx); michael@0: PORT_Assert(cx->hashAlg == hashid); michael@0: if (rv != SECSuccess) { michael@0: return SECFailure; michael@0: } michael@0: } michael@0: return verifyPKCS1DigestInfo(cx, &digest); michael@0: } michael@0: default: michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: return SECFailure; /* shouldn't happen */ michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_End(VFYContext *cx) michael@0: { michael@0: return VFY_EndWithSignature(cx,NULL); michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: /* michael@0: * Verify that a previously-computed digest matches a signature. michael@0: */ michael@0: static SECStatus michael@0: vfy_VerifyDigest(const SECItem *digest, const SECKEYPublicKey *key, michael@0: const SECItem *sig, SECOidTag encAlg, SECOidTag hashAlg, michael@0: void *wincx) michael@0: { michael@0: SECStatus rv; michael@0: VFYContext *cx; michael@0: SECItem dsasig; /* also used for ECDSA */ michael@0: michael@0: rv = SECFailure; michael@0: michael@0: cx = vfy_CreateContext(key, sig, encAlg, hashAlg, NULL, wincx); michael@0: if (cx != NULL) { michael@0: switch (key->keyType) { michael@0: case rsaKey: michael@0: rv = verifyPKCS1DigestInfo(cx, digest); michael@0: break; michael@0: case dsaKey: michael@0: case ecKey: michael@0: dsasig.data = cx->u.buffer; michael@0: dsasig.len = SECKEY_SignatureLen(cx->key); michael@0: if (dsasig.len == 0) { michael@0: break; michael@0: } michael@0: if (PK11_Verify(cx->key, &dsasig, (SECItem *)digest, cx->wincx) michael@0: != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: } else { michael@0: rv = SECSuccess; michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: VFY_DestroyContext(cx, PR_TRUE); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_VerifyDigestDirect(const SECItem *digest, const SECKEYPublicKey *key, michael@0: const SECItem *sig, SECOidTag encAlg, michael@0: SECOidTag hashAlg, void *wincx) michael@0: { michael@0: return vfy_VerifyDigest(digest, key, sig, encAlg, hashAlg, wincx); michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_VerifyDigest(SECItem *digest, SECKEYPublicKey *key, SECItem *sig, michael@0: SECOidTag algid, void *wincx) michael@0: { michael@0: SECOidTag encAlg, hashAlg; michael@0: SECStatus rv = sec_DecodeSigAlg(key, algid, NULL, &encAlg, &hashAlg); michael@0: if (rv != SECSuccess) { michael@0: return SECFailure; michael@0: } michael@0: return vfy_VerifyDigest(digest, key, sig, encAlg, hashAlg, wincx); michael@0: } michael@0: michael@0: /* michael@0: * this function takes an optional hash oid, which the digest function michael@0: * will be compared with our target hash value. michael@0: */ michael@0: SECStatus michael@0: VFY_VerifyDigestWithAlgorithmID(const SECItem *digest, michael@0: const SECKEYPublicKey *key, const SECItem *sig, michael@0: const SECAlgorithmID *sigAlgorithm, michael@0: SECOidTag hashCmp, void *wincx) michael@0: { michael@0: SECOidTag encAlg, hashAlg; michael@0: SECStatus rv = sec_DecodeSigAlg(key, michael@0: SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm), michael@0: &sigAlgorithm->parameters, &encAlg, &hashAlg); michael@0: if (rv != SECSuccess) { michael@0: return rv; michael@0: } michael@0: if ( hashCmp != SEC_OID_UNKNOWN && michael@0: hashAlg != SEC_OID_UNKNOWN && michael@0: hashCmp != hashAlg) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: return SECFailure; michael@0: } michael@0: return vfy_VerifyDigest(digest, key, sig, encAlg, hashAlg, wincx); michael@0: } michael@0: michael@0: static SECStatus michael@0: vfy_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key, michael@0: const SECItem *sig, SECOidTag encAlg, SECOidTag hashAlg, michael@0: SECOidTag *hash, void *wincx) michael@0: { michael@0: SECStatus rv; michael@0: VFYContext *cx; michael@0: michael@0: cx = vfy_CreateContext(key, sig, encAlg, hashAlg, hash, wincx); michael@0: if (cx == NULL) michael@0: return SECFailure; michael@0: michael@0: rv = VFY_Begin(cx); michael@0: if (rv == SECSuccess) { michael@0: rv = VFY_Update(cx, (unsigned char *)buf, len); michael@0: if (rv == SECSuccess) michael@0: rv = VFY_End(cx); michael@0: } michael@0: michael@0: VFY_DestroyContext(cx, PR_TRUE); michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_VerifyDataDirect(const unsigned char *buf, int len, michael@0: const SECKEYPublicKey *key, const SECItem *sig, michael@0: SECOidTag encAlg, SECOidTag hashAlg, michael@0: SECOidTag *hash, void *wincx) michael@0: { michael@0: return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx); michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_VerifyData(const unsigned char *buf, int len, const SECKEYPublicKey *key, michael@0: const SECItem *sig, SECOidTag algid, void *wincx) michael@0: { michael@0: SECOidTag encAlg, hashAlg; michael@0: SECStatus rv = sec_DecodeSigAlg(key, algid, NULL, &encAlg, &hashAlg); michael@0: if (rv != SECSuccess) { michael@0: return rv; michael@0: } michael@0: return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, NULL, wincx); michael@0: } michael@0: michael@0: SECStatus michael@0: VFY_VerifyDataWithAlgorithmID(const unsigned char *buf, int len, michael@0: const SECKEYPublicKey *key, michael@0: const SECItem *sig, michael@0: const SECAlgorithmID *sigAlgorithm, michael@0: SECOidTag *hash, void *wincx) michael@0: { michael@0: SECOidTag encAlg, hashAlg; michael@0: SECOidTag sigAlg = SECOID_GetAlgorithmTag((SECAlgorithmID *)sigAlgorithm); michael@0: SECStatus rv = sec_DecodeSigAlg(key, sigAlg, michael@0: &sigAlgorithm->parameters, &encAlg, &hashAlg); michael@0: if (rv != SECSuccess) { michael@0: return rv; michael@0: } michael@0: return vfy_VerifyData(buf, len, key, sig, encAlg, hashAlg, hash, wincx); michael@0: }