Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "crypto/signature_verifier.h" |
| 6 | |
| 7 | #include <cryptohi.h> |
| 8 | #include <keyhi.h> |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 9 | #include <pk11pub.h> |
| 10 | #include <secerr.h> |
| 11 | #include <sechash.h> |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 12 | #include <stdlib.h> |
| 13 | |
| 14 | #include "base/logging.h" |
| 15 | #include "crypto/nss_util.h" |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 16 | #include "crypto/third_party/nss/chromium-nss.h" |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 17 | |
| 18 | namespace crypto { |
| 19 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 20 | namespace { |
| 21 | |
| 22 | HASH_HashType ToNSSHashType(SignatureVerifier::HashAlgorithm hash_alg) { |
| 23 | switch (hash_alg) { |
| 24 | case SignatureVerifier::SHA1: |
| 25 | return HASH_AlgSHA1; |
| 26 | case SignatureVerifier::SHA256: |
| 27 | return HASH_AlgSHA256; |
| 28 | } |
| 29 | return HASH_AlgNULL; |
| 30 | } |
| 31 | |
| 32 | SECStatus VerifyRSAPSS_End(SECKEYPublicKey* public_key, |
| 33 | HASHContext* hash_context, |
| 34 | HASH_HashType mask_hash_alg, |
| 35 | unsigned int salt_len, |
| 36 | const unsigned char* signature, |
| 37 | unsigned int signature_len) { |
| 38 | unsigned int hash_len = HASH_ResultLenContext(hash_context); |
| 39 | std::vector<unsigned char> hash(hash_len); |
| 40 | HASH_End(hash_context, &hash[0], &hash_len, hash.size()); |
| 41 | |
| 42 | unsigned int modulus_len = SECKEY_PublicKeyStrength(public_key); |
| 43 | if (signature_len != modulus_len) { |
| 44 | PORT_SetError(SEC_ERROR_BAD_SIGNATURE); |
| 45 | return SECFailure; |
| 46 | } |
| 47 | std::vector<unsigned char> enc(signature_len); |
| 48 | SECStatus rv = PK11_PubEncryptRaw(public_key, &enc[0], |
| 49 | const_cast<unsigned char*>(signature), |
| 50 | signature_len, NULL); |
| 51 | if (rv != SECSuccess) { |
| 52 | LOG(WARNING) << "PK11_PubEncryptRaw failed"; |
| 53 | return rv; |
| 54 | } |
| 55 | return emsa_pss_verify(&hash[0], &enc[0], enc.size(), |
| 56 | HASH_GetType(hash_context), mask_hash_alg, |
| 57 | salt_len); |
| 58 | } |
| 59 | |
| 60 | } // namespace |
| 61 | |
| 62 | SignatureVerifier::SignatureVerifier() |
| 63 | : vfy_context_(NULL), |
| 64 | hash_alg_(SHA1), |
| 65 | mask_hash_alg_(SHA1), |
| 66 | salt_len_(0), |
| 67 | public_key_(NULL), |
| 68 | hash_context_(NULL) { |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 69 | EnsureNSSInit(); |
| 70 | } |
| 71 | |
| 72 | SignatureVerifier::~SignatureVerifier() { |
| 73 | Reset(); |
| 74 | } |
| 75 | |
| 76 | bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, |
| 77 | int signature_algorithm_len, |
| 78 | const uint8* signature, |
| 79 | int signature_len, |
| 80 | const uint8* public_key_info, |
| 81 | int public_key_info_len) { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 82 | if (vfy_context_ || hash_context_) |
| 83 | return false; |
| 84 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 85 | signature_.assign(signature, signature + signature_len); |
| 86 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 87 | SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info, |
| 88 | public_key_info_len); |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 89 | if (!public_key) |
| 90 | return false; |
| 91 | |
| 92 | PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| 93 | if (!arena) { |
| 94 | SECKEY_DestroyPublicKey(public_key); |
| 95 | return false; |
| 96 | } |
| 97 | |
| 98 | SECItem sig_alg_der; |
| 99 | sig_alg_der.type = siBuffer; |
| 100 | sig_alg_der.data = const_cast<uint8*>(signature_algorithm); |
| 101 | sig_alg_der.len = signature_algorithm_len; |
| 102 | SECAlgorithmID sig_alg_id; |
| 103 | SECStatus rv; |
| 104 | rv = SEC_QuickDERDecodeItem(arena, &sig_alg_id, |
| 105 | SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), |
| 106 | &sig_alg_der); |
| 107 | if (rv != SECSuccess) { |
| 108 | SECKEY_DestroyPublicKey(public_key); |
| 109 | PORT_FreeArena(arena, PR_TRUE); |
| 110 | return false; |
| 111 | } |
| 112 | |
| 113 | SECItem sig; |
| 114 | sig.type = siBuffer; |
| 115 | sig.data = const_cast<uint8*>(signature); |
| 116 | sig.len = signature_len; |
| 117 | SECOidTag hash_alg_tag; |
| 118 | vfy_context_ = VFY_CreateContextWithAlgorithmID(public_key, &sig, |
| 119 | &sig_alg_id, &hash_alg_tag, |
| 120 | NULL); |
| 121 | SECKEY_DestroyPublicKey(public_key); // Done with public_key. |
| 122 | PORT_FreeArena(arena, PR_TRUE); // Done with sig_alg_id. |
| 123 | if (!vfy_context_) { |
| 124 | // A corrupted RSA signature could be detected without the data, so |
| 125 | // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE |
| 126 | // (-8182). |
| 127 | return false; |
| 128 | } |
| 129 | |
| 130 | rv = VFY_Begin(vfy_context_); |
| 131 | if (rv != SECSuccess) { |
| 132 | NOTREACHED(); |
| 133 | return false; |
| 134 | } |
| 135 | return true; |
| 136 | } |
| 137 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 138 | bool SignatureVerifier::VerifyInitRSAPSS(HashAlgorithm hash_alg, |
| 139 | HashAlgorithm mask_hash_alg, |
| 140 | int salt_len, |
| 141 | const uint8* signature, |
| 142 | int signature_len, |
| 143 | const uint8* public_key_info, |
| 144 | int public_key_info_len) { |
| 145 | if (vfy_context_ || hash_context_) |
| 146 | return false; |
| 147 | |
| 148 | signature_.assign(signature, signature + signature_len); |
| 149 | |
| 150 | SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info, |
| 151 | public_key_info_len); |
| 152 | if (!public_key) |
| 153 | return false; |
| 154 | |
| 155 | public_key_ = public_key; |
| 156 | hash_alg_ = hash_alg; |
| 157 | mask_hash_alg_ = mask_hash_alg; |
| 158 | salt_len_ = salt_len; |
| 159 | hash_context_ = HASH_Create(ToNSSHashType(hash_alg_)); |
| 160 | if (!hash_context_) |
| 161 | return false; |
| 162 | HASH_Begin(hash_context_); |
| 163 | return true; |
| 164 | } |
| 165 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 166 | void SignatureVerifier::VerifyUpdate(const uint8* data_part, |
| 167 | int data_part_len) { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 168 | if (vfy_context_) { |
| 169 | SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len); |
| 170 | DCHECK_EQ(SECSuccess, rv); |
| 171 | } else { |
| 172 | HASH_Update(hash_context_, data_part, data_part_len); |
| 173 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 174 | } |
| 175 | |
| 176 | bool SignatureVerifier::VerifyFinal() { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 177 | SECStatus rv; |
| 178 | if (vfy_context_) { |
| 179 | rv = VFY_End(vfy_context_); |
| 180 | } else { |
| 181 | rv = VerifyRSAPSS_End(public_key_, hash_context_, |
| 182 | ToNSSHashType(mask_hash_alg_), salt_len_, |
| 183 | signature_.data(), |
| 184 | signature_.size()); |
| 185 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 186 | Reset(); |
| 187 | |
| 188 | // If signature verification fails, the error code is |
| 189 | // SEC_ERROR_BAD_SIGNATURE (-8182). |
| 190 | return (rv == SECSuccess); |
| 191 | } |
| 192 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 193 | // static |
| 194 | SECKEYPublicKey* SignatureVerifier::DecodePublicKeyInfo( |
| 195 | const uint8* public_key_info, |
| 196 | int public_key_info_len) { |
| 197 | CERTSubjectPublicKeyInfo* spki = NULL; |
| 198 | SECItem spki_der; |
| 199 | spki_der.type = siBuffer; |
| 200 | spki_der.data = const_cast<uint8*>(public_key_info); |
| 201 | spki_der.len = public_key_info_len; |
| 202 | spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der); |
| 203 | if (!spki) |
| 204 | return NULL; |
| 205 | SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); |
| 206 | SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki. |
| 207 | return public_key; |
| 208 | } |
| 209 | |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 210 | void SignatureVerifier::Reset() { |
| 211 | if (vfy_context_) { |
| 212 | VFY_DestroyContext(vfy_context_, PR_TRUE); |
| 213 | vfy_context_ = NULL; |
| 214 | } |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 215 | if (hash_context_) { |
| 216 | HASH_Destroy(hash_context_); |
| 217 | hash_context_ = NULL; |
| 218 | } |
| 219 | if (public_key_) { |
| 220 | SECKEY_DestroyPublicKey(public_key_); |
| 221 | public_key_ = NULL; |
| 222 | } |
Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 223 | signature_.clear(); |
| 224 | } |
| 225 | |
| 226 | } // namespace crypto |