| // Copyright 2011 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "crypto/signature_verifier.h" |
| |
| #include <cryptohi.h> |
| #include <keyhi.h> |
| #include <pk11pub.h> |
| #include <secerr.h> |
| #include <sechash.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| |
| #include "base/logging.h" |
| #include "crypto/nss_util.h" |
| #include "crypto/third_party/nss/chromium-nss.h" |
| |
| namespace crypto { |
| |
| namespace { |
| |
| HASH_HashType ToNSSHashType(SignatureVerifier::HashAlgorithm hash_alg) { |
| switch (hash_alg) { |
| case SignatureVerifier::SHA1: |
| return HASH_AlgSHA1; |
| case SignatureVerifier::SHA256: |
| return HASH_AlgSHA256; |
| } |
| return HASH_AlgNULL; |
| } |
| |
| SECOidTag ToNSSSignatureType(SignatureVerifier::SignatureAlgorithm sig_alg) { |
| switch (sig_alg) { |
| case SignatureVerifier::RSA_PKCS1_SHA1: |
| return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; |
| case SignatureVerifier::RSA_PKCS1_SHA256: |
| return SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; |
| case SignatureVerifier::ECDSA_SHA256: |
| return SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; |
| } |
| return SEC_OID_UNKNOWN; |
| } |
| |
| SECStatus VerifyRSAPSS_End(SECKEYPublicKey* public_key, |
| HASHContext* hash_context, |
| HASH_HashType mask_hash_alg, |
| unsigned int salt_len, |
| const unsigned char* signature, |
| unsigned int signature_len) { |
| unsigned int hash_len = HASH_ResultLenContext(hash_context); |
| std::vector<unsigned char> hash(hash_len); |
| HASH_End(hash_context, &hash[0], &hash_len, hash.size()); |
| |
| unsigned int modulus_len = SECKEY_PublicKeyStrength(public_key); |
| if (signature_len != modulus_len) { |
| PORT_SetError(SEC_ERROR_BAD_SIGNATURE); |
| return SECFailure; |
| } |
| std::vector<unsigned char> enc(signature_len); |
| SECStatus rv = PK11_PubEncryptRaw(public_key, &enc[0], |
| const_cast<unsigned char*>(signature), |
| signature_len, NULL); |
| if (rv != SECSuccess) { |
| LOG(WARNING) << "PK11_PubEncryptRaw failed"; |
| return rv; |
| } |
| return emsa_pss_verify(&hash[0], &enc[0], enc.size(), |
| HASH_GetType(hash_context), mask_hash_alg, |
| salt_len); |
| } |
| |
| } // namespace |
| |
| SignatureVerifier::SignatureVerifier() |
| : vfy_context_(NULL), |
| hash_alg_(SHA1), |
| mask_hash_alg_(SHA1), |
| salt_len_(0), |
| public_key_(NULL), |
| hash_context_(NULL) { |
| EnsureNSSInit(); |
| } |
| |
| SignatureVerifier::~SignatureVerifier() { |
| Reset(); |
| } |
| |
| bool SignatureVerifier::VerifyInit(SignatureAlgorithm signature_algorithm, |
| const uint8_t* signature, |
| int signature_len, |
| const uint8_t* public_key_info, |
| int public_key_info_len) { |
| if (vfy_context_ || hash_context_) |
| return false; |
| |
| signature_.assign(signature, signature + signature_len); |
| |
| SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info, |
| public_key_info_len); |
| if (!public_key) |
| return false; |
| |
| SECItem sig; |
| sig.type = siBuffer; |
| sig.data = const_cast<uint8_t*>(signature); |
| sig.len = signature_len; |
| vfy_context_ = VFY_CreateContext( |
| public_key, &sig, ToNSSSignatureType(signature_algorithm), nullptr); |
| SECKEY_DestroyPublicKey(public_key); // Done with public_key. |
| if (!vfy_context_) { |
| // A corrupted RSA signature could be detected without the data, so |
| // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE |
| // (-8182). |
| return false; |
| } |
| |
| if (VFY_Begin(vfy_context_) != SECSuccess) { |
| NOTREACHED(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool SignatureVerifier::VerifyInitRSAPSS(HashAlgorithm hash_alg, |
| HashAlgorithm mask_hash_alg, |
| int salt_len, |
| const uint8_t* signature, |
| int signature_len, |
| const uint8_t* public_key_info, |
| int public_key_info_len) { |
| if (vfy_context_ || hash_context_) |
| return false; |
| |
| signature_.assign(signature, signature + signature_len); |
| |
| SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info, |
| public_key_info_len); |
| if (!public_key) |
| return false; |
| |
| public_key_ = public_key; |
| hash_alg_ = hash_alg; |
| mask_hash_alg_ = mask_hash_alg; |
| salt_len_ = salt_len; |
| hash_context_ = HASH_Create(ToNSSHashType(hash_alg_)); |
| if (!hash_context_) |
| return false; |
| HASH_Begin(hash_context_); |
| return true; |
| } |
| |
| void SignatureVerifier::VerifyUpdate(const uint8_t* data_part, |
| int data_part_len) { |
| if (vfy_context_) { |
| SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len); |
| DCHECK_EQ(SECSuccess, rv); |
| } else { |
| HASH_Update(hash_context_, data_part, data_part_len); |
| } |
| } |
| |
| bool SignatureVerifier::VerifyFinal() { |
| SECStatus rv; |
| if (vfy_context_) { |
| rv = VFY_End(vfy_context_); |
| } else { |
| rv = VerifyRSAPSS_End(public_key_, hash_context_, |
| ToNSSHashType(mask_hash_alg_), salt_len_, |
| signature_.data(), |
| signature_.size()); |
| } |
| Reset(); |
| |
| // If signature verification fails, the error code is |
| // SEC_ERROR_BAD_SIGNATURE (-8182). |
| return (rv == SECSuccess); |
| } |
| |
| // static |
| SECKEYPublicKey* SignatureVerifier::DecodePublicKeyInfo( |
| const uint8_t* public_key_info, |
| int public_key_info_len) { |
| CERTSubjectPublicKeyInfo* spki = NULL; |
| SECItem spki_der; |
| spki_der.type = siBuffer; |
| spki_der.data = const_cast<uint8_t*>(public_key_info); |
| spki_der.len = public_key_info_len; |
| spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der); |
| if (!spki) |
| return NULL; |
| SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); |
| SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki. |
| return public_key; |
| } |
| |
| void SignatureVerifier::Reset() { |
| if (vfy_context_) { |
| VFY_DestroyContext(vfy_context_, PR_TRUE); |
| vfy_context_ = NULL; |
| } |
| if (hash_context_) { |
| HASH_Destroy(hash_context_); |
| hash_context_ = NULL; |
| } |
| if (public_key_) { |
| SECKEY_DestroyPublicKey(public_key_); |
| public_key_ = NULL; |
| } |
| signature_.clear(); |
| } |
| |
| } // namespace crypto |