OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "net/cert/internal/verify_signed_data.h" | |
6 | |
7 #include "net/cert/internal/signature_algorithm.h" | |
8 | |
9 // TODO(eroman): There is no intention to implement this for non-OpenSSL. Remove | |
10 // this branch once the migration is complete. This could have been done as a | |
11 // conditional file (_openssl.cc) in the build file instead, but that is likely | |
12 // not worth the effort at this point. | |
13 | |
14 #if !defined(USE_OPENSSL) | |
15 | |
16 namespace net { | |
17 | |
18 bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, | |
19 const der::Input& signed_data, | |
20 const der::Input& signature_value, | |
21 const der::Input& public_key) { | |
22 // Not implemented | |
davidben
2015/07/21 16:26:27
Nit: NOTIMPLEMENTED()
eroman
2015/07/21 19:24:28
Done.
| |
23 return false; | |
24 } | |
25 | |
26 } // namespace net | |
27 | |
28 #else | |
29 | |
30 #include <openssl/digest.h> | |
31 #include <openssl/ec.h> | |
32 #include <openssl/ec_key.h> | |
33 #include <openssl/evp.h> | |
34 #include <openssl/rsa.h> | |
35 #include <openssl/x509.h> | |
36 | |
37 #include "crypto/openssl_util.h" | |
38 #include "crypto/scoped_openssl_types.h" | |
39 #include "net/der/input.h" | |
40 #include "net/der/parser.h" | |
41 | |
42 namespace net { | |
43 | |
44 namespace { | |
45 | |
46 // Converts a DigestAlgorithm to an equivalent EVP_MD*. | |
47 WARN_UNUSED_RESULT bool GetDigest(DigestAlgorithm digest, const EVP_MD** out) { | |
davidben
2015/07/21 16:26:27
Optional: Could make this return const EVP_MD* and
davidben
2015/07/21 16:26:27
#include "base/compiler_specific.h"
eroman
2015/07/21 19:24:28
My thinking is to consistently return "bool" since
eroman
2015/07/21 19:24:28
Done.
| |
48 *out = nullptr; | |
49 | |
50 switch (digest) { | |
51 case DigestAlgorithm::Sha1: | |
52 *out = EVP_sha1(); | |
53 break; | |
54 case DigestAlgorithm::Sha256: | |
55 *out = EVP_sha256(); | |
56 break; | |
57 case DigestAlgorithm::Sha384: | |
58 *out = EVP_sha384(); | |
59 break; | |
60 case DigestAlgorithm::Sha512: | |
61 *out = EVP_sha512(); | |
62 break; | |
63 } | |
64 | |
65 return *out != nullptr; | |
66 } | |
67 | |
68 // Sets the RSASSA-PSS parameters on |pctx|. Returns true on success. | |
69 WARN_UNUSED_RESULT bool ApplyRsaPssOptions(const RsaPssParameters* params, | |
70 EVP_PKEY_CTX* pctx) { | |
71 // BoringSSL takes a signed int for the salt length, and interprets | |
72 // negative values in a special manner. Make sure not to silently underflow. | |
73 base::CheckedNumeric<int> salt_length_bytes_int(params->salt_length()); | |
74 if (!salt_length_bytes_int.IsValid()) | |
75 return false; | |
76 | |
77 const EVP_MD* mgf1_hash; | |
78 if (!GetDigest(params->mgf1_hash(), &mgf1_hash)) | |
79 return false; | |
80 | |
81 return (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) && | |
davidben
2015/07/21 16:26:27
Nit: Unnecessary parentheses. (Chromium's not very
eroman
2015/07/21 19:24:28
Done.
| |
82 EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_hash) && | |
83 EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, | |
84 salt_length_bytes_int.ValueOrDie())); | |
85 } | |
86 | |
87 WARN_UNUSED_RESULT bool ImportPkeyFromSpki(const der::Input& spki, | |
88 int expected_pkey_id, | |
89 crypto::ScopedEVP_PKEY* pkey) { | |
davidben
2015/07/21 16:26:27
Optional: Could return crypto::ScopedEVP_PKEY rath
eroman
2015/07/21 19:24:28
My preference is for a bool, as it will likely be
| |
90 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
91 | |
92 const uint8_t* ptr = spki.UnsafeData(); | |
93 pkey->reset(d2i_PUBKEY(nullptr, &ptr, spki.Length())); | |
davidben
2015/07/21 16:26:27
This probably needs a TODO to fix the strictness (
eroman
2015/07/21 19:24:28
Done.
| |
94 if (!pkey->get() || ptr != spki.UnsafeData() + spki.Length() || | |
95 EVP_PKEY_id(pkey->get()) != expected_pkey_id) { | |
96 pkey->reset(); | |
97 return false; | |
98 } | |
99 | |
100 return true; | |
101 } | |
102 | |
103 // Parses an RSA public key from SPKI to an EVP_PKEY. | |
104 // | |
105 // Returns true on success. | |
106 // | |
107 // There are two flavors of RSA public key that this function should recognize | |
108 // from RFC 5912 (however note that pk-rsaSSA-PSS is not supported in the | |
109 // current implementation). | |
110 // TODO(eroman): Support id-RSASSA-PSS and its associated parameters. | |
111 // | |
112 // pk-rsa PUBLIC-KEY ::= { | |
113 // IDENTIFIER rsaEncryption | |
114 // KEY RSAPublicKey | |
115 // PARAMS TYPE NULL ARE absent | |
116 // -- Private key format not in this module -- | |
117 // CERT-KEY-USAGE {digitalSignature, nonRepudiation, | |
118 // keyEncipherment, dataEncipherment, keyCertSign, cRLSign} | |
119 // } | |
120 // | |
121 // ... | |
122 // | |
123 // pk-rsaSSA-PSS PUBLIC-KEY ::= { | |
124 // IDENTIFIER id-RSASSA-PSS | |
125 // KEY RSAPublicKey | |
126 // PARAMS TYPE RSASSA-PSS-params ARE optional | |
127 // -- Private key format not in this module -- | |
128 // CERT-KEY-USAGE { nonRepudiation, digitalSignature, | |
129 // keyCertSign, cRLSign } | |
130 // } | |
131 // | |
132 // Any RSA signature algorithm can accept a "pk-rsa" (rsaEncryption). However a | |
133 // "pk-rsaSSA-PSS" key is only accepted if the signature algorithm was for PSS | |
134 // mode: | |
135 // | |
136 // sa-rsaSSA-PSS SIGNATURE-ALGORITHM ::= { | |
137 // IDENTIFIER id-RSASSA-PSS | |
138 // PARAMS TYPE RSASSA-PSS-params ARE required | |
139 // HASHES { mda-sha1 | mda-sha224 | mda-sha256 | mda-sha384 | |
140 // | mda-sha512 } | |
141 // PUBLIC-KEYS { pk-rsa | pk-rsaSSA-PSS } | |
142 // SMIME-CAPS { IDENTIFIED BY id-RSASSA-PSS } | |
143 // } | |
144 // | |
145 // Moreover, if a "pk-rsaSSA-PSS" key was used and it optionally provided | |
146 // parameters for the algorithm, they must match those of the signature | |
147 // algorithm. | |
148 // | |
149 // COMPATIBILITY NOTE: RFC 5912 specifies that the algorithm parameters for | |
150 // pk-rsa (rsaEncryption) are absent. However this is in contradiction with | |
151 // RFC 3279 section 2.3.1 which says it should be NULL: | |
davidben
2015/07/21 16:26:27
Nit: should -> MUST
(I.e. it's not that RFC 3279
eroman
2015/07/21 19:24:28
Done.
| |
152 // | |
153 // The rsaEncryption OID is intended to be used in the algorithm field | |
154 // of a value of type AlgorithmIdentifier. The parameters field MUST | |
155 // have ASN.1 type NULL for this algorithm identifier. | |
156 // | |
157 // In practice implementations follow the rules from RFC 3279 and not RFC 5912. | |
158 WARN_UNUSED_RESULT bool ParseRsaKeyFromSpki(const der::Input& public_key_spki, | |
159 crypto::ScopedEVP_PKEY* pkey) { | |
davidben
2015/07/21 16:26:27
Optional: Could return crypto::ScopedEVP_PKEY rath
eroman
2015/07/21 19:24:28
Same response as earlier.
| |
160 return ImportPkeyFromSpki(public_key_spki, EVP_PKEY_RSA, pkey); | |
161 } | |
162 | |
163 // Does signature verification using either RSA or ECDSA. | |
164 WARN_UNUSED_RESULT bool DoVerify(const SignatureAlgorithm& algorithm, | |
165 const der::Input& signed_data, | |
166 const der::Input& signature_value, | |
167 EVP_PKEY* public_key) { | |
168 DCHECK(algorithm.algorithm() == SignatureAlgorithmId::RsaPkcs1 || | |
davidben
2015/07/21 16:26:27
#include "base/logging.h"
eroman
2015/07/21 19:24:28
Done.
| |
169 algorithm.algorithm() == SignatureAlgorithmId::RsaPss || | |
170 algorithm.algorithm() == SignatureAlgorithmId::Ecdsa); | |
171 | |
172 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
173 | |
174 crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); | |
175 EVP_PKEY_CTX* pctx = nullptr; // Owned by |ctx|. | |
176 | |
177 const EVP_MD* digest; | |
178 if (!GetDigest(algorithm.digest(), &digest)) | |
179 return false; | |
180 | |
181 if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, nullptr, public_key)) | |
182 return false; | |
183 | |
184 // Set the RSASSA-PSS specific options. | |
185 if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && | |
186 !ApplyRsaPssOptions(algorithm.ParamsForRsaPss(), pctx)) { | |
187 return false; | |
188 } | |
189 | |
190 if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), | |
191 signed_data.Length())) { | |
192 return false; | |
193 } | |
194 | |
195 return EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), | |
196 signature_value.Length()); | |
197 } | |
198 | |
199 // Returns true if the given curve is allowed for ECDSA. The input is a | |
200 // BoringSSL NID. | |
201 // | |
202 // TODO(eroman): Extract policy decisions such as allowed curves, hashes, RSA | |
203 // modulus size, to somewhere more central. | |
204 WARN_UNUSED_RESULT bool IsAllowedCurveName(int curve_nid) { | |
205 switch (curve_nid) { | |
206 case NID_X9_62_prime256v1: | |
207 case NID_secp384r1: | |
208 case NID_secp521r1: | |
209 return true; | |
210 } | |
211 return false; | |
212 } | |
213 | |
214 // Parses an EC public key from SPKI to an EVP_PKEY. | |
215 // | |
216 // Returns true on success. | |
217 // | |
218 // RFC 5912 describes all the ECDSA signature algorithms as requiring a public | |
219 // key of type "pk-ec": | |
220 // | |
221 // pk-ec PUBLIC-KEY ::= { | |
222 // IDENTIFIER id-ecPublicKey | |
223 // KEY ECPoint | |
224 // PARAMS TYPE ECParameters ARE required | |
225 // -- Private key format not in this module -- | |
226 // CERT-KEY-USAGE { digitalSignature, nonRepudiation, keyAgreement, | |
227 // keyCertSign, cRLSign } | |
228 // } | |
229 // | |
230 // Moreover RFC 5912 stipulates what curves are allowed. The ECParameters | |
231 // MUST NOT use an implicitCurve or specificCurve for PKIX: | |
232 // | |
233 // ECParameters ::= CHOICE { | |
234 // namedCurve CURVE.&id({NamedCurve}) | |
235 // -- implicitCurve NULL | |
236 // -- implicitCurve MUST NOT be used in PKIX | |
237 // -- specifiedCurve SpecifiedCurve | |
238 // -- specifiedCurve MUST NOT be used in PKIX | |
239 // -- Details for specifiedCurve can be found in [X9.62] | |
240 // -- Any future additions to this CHOICE should be coordinated | |
241 // -- with ANSI X.9. | |
242 // } | |
243 // -- If you need to be able to decode ANSI X.9 parameter structures, | |
244 // -- uncomment the implicitCurve and specifiedCurve above, and also | |
245 // -- uncomment the following: | |
246 // --(WITH COMPONENTS {namedCurve PRESENT}) | |
247 // | |
248 // The namedCurves are extensible. The ones described by RFC 5912 are: | |
249 // | |
250 // NamedCurve CURVE ::= { | |
251 // { ID secp192r1 } | { ID sect163k1 } | { ID sect163r2 } | | |
252 // { ID secp224r1 } | { ID sect233k1 } | { ID sect233r1 } | | |
253 // { ID secp256r1 } | { ID sect283k1 } | { ID sect283r1 } | | |
254 // { ID secp384r1 } | { ID sect409k1 } | { ID sect409r1 } | | |
255 // { ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 }, | |
256 // ... -- Extensible | |
257 // } | |
258 WARN_UNUSED_RESULT bool ParseEcKeyFromSpki(const der::Input& public_key_spki, | |
259 crypto::ScopedEVP_PKEY* pkey) { | |
davidben
2015/07/21 16:26:27
Optional: Could return crypto::ScopedEVP_PKEY rath
eroman
2015/07/21 19:24:28
Same response as earlier.
| |
260 if (!ImportPkeyFromSpki(public_key_spki, EVP_PKEY_EC, pkey)) | |
261 return false; | |
262 | |
263 // Enforce policy on allowed curves in case ImportPkeyFromSpki() were to | |
264 // recognize and allow use of a weak curve. | |
265 crypto::ScopedEC_KEY ec(EVP_PKEY_get1_EC_KEY(pkey->get())); | |
266 if (!ec.get()) | |
267 return false; // Unexpected. | |
268 | |
269 int curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec.get())); | |
270 return IsAllowedCurveName(curve_nid); | |
271 } | |
272 | |
273 } // namespace | |
274 | |
275 bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, | |
276 const der::Input& signed_data, | |
277 const der::Input& signature_value, | |
278 const der::Input& public_key_spki) { | |
279 crypto::ScopedEVP_PKEY public_key; | |
280 | |
281 // Parse the SPKI to an EVP_PKEY appropriate for the signature algorithm. | |
282 switch (signature_algorithm.algorithm()) { | |
283 case SignatureAlgorithmId::RsaPkcs1: | |
284 case SignatureAlgorithmId::RsaPss: | |
285 if (!ParseRsaKeyFromSpki(public_key_spki, &public_key)) | |
286 return false; | |
287 break; | |
288 case SignatureAlgorithmId::Ecdsa: | |
289 if (!ParseEcKeyFromSpki(public_key_spki, &public_key)) | |
290 return false; | |
291 break; | |
292 } | |
293 | |
294 return DoVerify(signature_algorithm, signed_data, signature_value, | |
295 public_key.get()); | |
296 } | |
297 | |
298 } // namespace net | |
299 | |
300 #endif | |
OLD | NEW |