|  | // Copyright 2013 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/cert/ct_log_verifier.h" | 
|  |  | 
|  | #include <cryptohi.h> | 
|  | #include <keyhi.h> | 
|  | #include <nss.h> | 
|  | #include <pk11pub.h> | 
|  | #include <secitem.h> | 
|  | #include <secoid.h> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "crypto/nss_util.h" | 
|  | #include "crypto/sha2.h" | 
|  | #include "net/cert/signed_tree_head.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | SECOidTag GetNSSSigAlg(ct::DigitallySigned::SignatureAlgorithm alg) { | 
|  | switch (alg) { | 
|  | case ct::DigitallySigned::SIG_ALGO_RSA: | 
|  | return SEC_OID_PKCS1_RSA_ENCRYPTION; | 
|  | case ct::DigitallySigned::SIG_ALGO_DSA: | 
|  | return SEC_OID_ANSIX9_DSA_SIGNATURE; | 
|  | case ct::DigitallySigned::SIG_ALGO_ECDSA: | 
|  | return SEC_OID_ANSIX962_EC_PUBLIC_KEY; | 
|  | case ct::DigitallySigned::SIG_ALGO_ANONYMOUS: | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return SEC_OID_UNKNOWN; | 
|  | } | 
|  | } | 
|  |  | 
|  | SECOidTag GetNSSHashAlg(ct::DigitallySigned::HashAlgorithm alg) { | 
|  | switch (alg) { | 
|  | case ct::DigitallySigned::HASH_ALGO_MD5: | 
|  | return SEC_OID_MD5; | 
|  | case ct::DigitallySigned::HASH_ALGO_SHA1: | 
|  | return SEC_OID_SHA1; | 
|  | case ct::DigitallySigned::HASH_ALGO_SHA224: | 
|  | return SEC_OID_SHA224; | 
|  | case ct::DigitallySigned::HASH_ALGO_SHA256: | 
|  | return SEC_OID_SHA256; | 
|  | case ct::DigitallySigned::HASH_ALGO_SHA384: | 
|  | return SEC_OID_SHA384; | 
|  | case ct::DigitallySigned::HASH_ALGO_SHA512: | 
|  | return SEC_OID_SHA512; | 
|  | case ct::DigitallySigned::HASH_ALGO_NONE: | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return SEC_OID_UNKNOWN; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | CTLogVerifier::~CTLogVerifier() { | 
|  | if (public_key_) | 
|  | SECKEY_DestroyPublicKey(public_key_); | 
|  | } | 
|  |  | 
|  | CTLogVerifier::CTLogVerifier() | 
|  | : hash_algorithm_(ct::DigitallySigned::HASH_ALGO_NONE), | 
|  | signature_algorithm_(ct::DigitallySigned::SIG_ALGO_ANONYMOUS), | 
|  | public_key_(NULL) {} | 
|  |  | 
|  | bool CTLogVerifier::Init(const base::StringPiece& public_key, | 
|  | const base::StringPiece& description) { | 
|  | SECItem key_data; | 
|  |  | 
|  | crypto::EnsureNSSInit(); | 
|  |  | 
|  | key_data.data = reinterpret_cast<unsigned char*>( | 
|  | const_cast<char*>(public_key.data())); | 
|  | key_data.len = public_key.size(); | 
|  |  | 
|  | CERTSubjectPublicKeyInfo* public_key_info = | 
|  | SECKEY_DecodeDERSubjectPublicKeyInfo(&key_data); | 
|  | if (!public_key_info) { | 
|  | DVLOG(1) << "Failed decoding public key."; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public_key_ = SECKEY_ExtractPublicKey(public_key_info); | 
|  | SECKEY_DestroySubjectPublicKeyInfo(public_key_info); | 
|  |  | 
|  | if (!public_key_) { | 
|  | DVLOG(1) << "Failed extracting public key."; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | key_id_ = crypto::SHA256HashString(public_key); | 
|  | description_ = description.as_string(); | 
|  |  | 
|  | // Right now, only RSASSA-PKCS1v15 with SHA-256 and ECDSA with SHA-256 are | 
|  | // supported. | 
|  | switch (SECKEY_GetPublicKeyType(public_key_)) { | 
|  | case rsaKey: | 
|  | hash_algorithm_ = ct::DigitallySigned::HASH_ALGO_SHA256; | 
|  | signature_algorithm_ = ct::DigitallySigned::SIG_ALGO_RSA; | 
|  | break; | 
|  | case ecKey: | 
|  | hash_algorithm_ = ct::DigitallySigned::HASH_ALGO_SHA256; | 
|  | signature_algorithm_ = ct::DigitallySigned::SIG_ALGO_ECDSA; | 
|  | break; | 
|  | default: | 
|  | DVLOG(1) << "Unsupported key type: " | 
|  | << SECKEY_GetPublicKeyType(public_key_); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Extra sanity check: Require RSA keys of at least 2048 bits. | 
|  | if (signature_algorithm_ == ct::DigitallySigned::SIG_ALGO_RSA && | 
|  | SECKEY_PublicKeyStrengthInBits(public_key_) < 2048) { | 
|  | DVLOG(1) << "Too small a public key."; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CTLogVerifier::VerifySignature(const base::StringPiece& data_to_sign, | 
|  | const base::StringPiece& signature) { | 
|  | SECItem sig_data; | 
|  | sig_data.data = reinterpret_cast<unsigned char*>(const_cast<char*>( | 
|  | signature.data())); | 
|  | sig_data.len = signature.size(); | 
|  |  | 
|  | SECStatus rv = VFY_VerifyDataDirect( | 
|  | reinterpret_cast<const unsigned char*>(data_to_sign.data()), | 
|  | data_to_sign.size(), public_key_, &sig_data, | 
|  | GetNSSSigAlg(signature_algorithm_), GetNSSHashAlg(hash_algorithm_), | 
|  | NULL, NULL); | 
|  | DVLOG(1) << "Signature verification result: " << (rv == SECSuccess); | 
|  | return rv == SECSuccess; | 
|  | } | 
|  |  | 
|  | }  // namespace net |