SECStatus VerifySignedData(const CERTSignedData* sd, const CERTCertificate* cert, void* pkcs11PinArg) { if (!sd || !sd->data.data || !sd->signatureAlgorithm.algorithm.data || !sd->signature.data || !cert) { PR_NOT_REACHED("invalid args to VerifySignedData"); PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } // See bug 921585. if (sd->data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } // convert sig->len from bit counts to byte count. SECItem sig = sd->signature; DER_ConvertBitString(&sig); // Use SECKEY_ExtractPublicKey instead of CERT_ExtractPublicKey because // CERT_ExtractPublicKey would try to do (EC)DSA parameter inheritance, using // the classic (wrong) NSS path building logic. We intentionally do not // support parameter inheritance. ScopedSECKEYPublicKey pubKey(SECKEY_ExtractPublicKey(&cert->subjectPublicKeyInfo)); if (!pubKey) { return SECFailure; } SECOidTag hashAlg; if (VFY_VerifyDataWithAlgorithmID(sd->data.data, static_cast<int>(sd->data.len), pubKey.get(), &sig, &sd->signatureAlgorithm, &hashAlg, pkcs11PinArg) != SECSuccess) { return SECFailure; } // TODO: Ideally, we would do this check before we call // VFY_VerifyDataWithAlgorithmID. But, VFY_VerifyDataWithAlgorithmID gives us // the hash algorithm so it is more convenient to do things in this order. uint32_t policy; if (NSS_GetAlgorithmPolicy(hashAlg, &policy) != SECSuccess) { return SECFailure; } // XXX: I'm not sure why there isn't NSS_USE_ALG_IN_SSL_SIGNATURE, but there // isn't. Since we don't know the context in which we're being called, be as // strict as we can be given the NSS API that is available. static const uint32_t requiredPolicy = NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE; if ((policy & requiredPolicy) != requiredPolicy) { PR_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 0); return SECFailure; } return SECSuccess; }
NS_IMETHODIMP nsDataSignatureVerifier::VerifyData(const nsACString & aData, const nsACString & aSignature, const nsACString & aPublicKey, bool *_retval) { // Allocate an arena to handle the majority of the allocations PRArenaPool *arena; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) return NS_ERROR_OUT_OF_MEMORY; // Base 64 decode the key SECItem keyItem; PORT_Memset(&keyItem, 0, sizeof(SECItem)); if (!NSSBase64_DecodeBuffer(arena, &keyItem, nsPromiseFlatCString(aPublicKey).get(), aPublicKey.Length())) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Extract the public key from the data CERTSubjectPublicKeyInfo *pki = SECKEY_DecodeDERSubjectPublicKeyInfo(&keyItem); if (!pki) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } SECKEYPublicKey *publicKey = SECKEY_ExtractPublicKey(pki); SECKEY_DestroySubjectPublicKeyInfo(pki); pki = nullptr; if (!publicKey) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Base 64 decode the signature SECItem signatureItem; PORT_Memset(&signatureItem, 0, sizeof(SECItem)); if (!NSSBase64_DecodeBuffer(arena, &signatureItem, nsPromiseFlatCString(aSignature).get(), aSignature.Length())) { SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Decode the signature and algorithm CERTSignedData sigData; PORT_Memset(&sigData, 0, sizeof(CERTSignedData)); SECStatus ss = SEC_QuickDERDecodeItem(arena, &sigData, CERT_SignatureDataTemplate, &signatureItem); if (ss != SECSuccess) { SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Perform the final verification DER_ConvertBitString(&(sigData.signature)); ss = VFY_VerifyDataWithAlgorithmID((const unsigned char*)nsPromiseFlatCString(aData).get(), aData.Length(), publicKey, &(sigData.signature), &(sigData.signatureAlgorithm), NULL, NULL); // Clean up remaining objects SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); *_retval = (ss == SECSuccess); return NS_OK; }