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. | |
Ryan Sleevi
2015/07/16 04:05:23
iOS is still affected by this at present.
eroman
2015/07/16 17:34:07
Would you prefer I designate this file _openssl an
| |
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 | |
23 return false; | |
24 } | |
25 | |
26 } // namespace net | |
27 | |
28 #else | |
29 | |
30 #include <openssl/evp.h> | |
31 #include <openssl/x509.h> | |
32 | |
33 #include "crypto/openssl_util.h" | |
34 #include "crypto/scoped_openssl_types.h" | |
35 #include "net/der/input.h" | |
36 #include "net/der/parser.h" | |
37 | |
38 namespace net { | |
39 | |
40 namespace { | |
41 | |
42 // Converts a DigestAlgorithm to an equivalent EVP_MD*. | |
43 WARN_UNUSED_RESULT bool GetDigest(DigestAlgorithm digest, const EVP_MD** out) { | |
44 switch (digest) { | |
45 case DigestAlgorithm::Sha1: | |
46 *out = EVP_sha1(); | |
47 break; | |
48 case DigestAlgorithm::Sha256: | |
49 *out = EVP_sha256(); | |
50 break; | |
51 case DigestAlgorithm::Sha384: | |
52 *out = EVP_sha384(); | |
53 break; | |
54 case DigestAlgorithm::Sha512: | |
55 *out = EVP_sha512(); | |
56 break; | |
57 } | |
58 | |
59 return *out != nullptr; | |
60 } | |
61 | |
62 // Sets the RSASSA-PSS parameters on |pctx|. Returns true on success. | |
63 WARN_UNUSED_RESULT bool ApplyRsaPssOptions(const RsaPssParameters* params, | |
64 EVP_PKEY_CTX* pctx) { | |
65 // BoringSSL takes a signed int for the salt length, and interprets | |
66 // negative values in a special manner. Make sure not to silently underflow. | |
67 base::CheckedNumeric<int> salt_length_bytes_int(params->salt_length()); | |
68 if (!salt_length_bytes_int.IsValid()) | |
69 return false; | |
70 | |
71 const EVP_MD* mgf1_hash; | |
72 if (!GetDigest(params->mgf1_hash(), &mgf1_hash)) | |
73 return false; | |
74 | |
75 return (1 == EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) && | |
76 1 == EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1_hash) && | |
77 1 == EVP_PKEY_CTX_set_rsa_pss_saltlen( | |
78 pctx, salt_length_bytes_int.ValueOrDie())); | |
79 } | |
80 | |
81 // Parses an RSA public key from SPKI to an EVP_PKEY. | |
82 // | |
83 // Returns true on success. | |
84 // | |
85 // There are two flavors of RSA public key that this function recognizes from | |
86 // RFC 5912: | |
87 // | |
88 // pk-rsa PUBLIC-KEY ::= { | |
89 // IDENTIFIER rsaEncryption | |
90 // KEY RSAPublicKey | |
91 // PARAMS TYPE NULL ARE absent | |
92 // -- Private key format not in this module -- | |
93 // CERT-KEY-USAGE {digitalSignature, nonRepudiation, | |
94 // keyEncipherment, dataEncipherment, keyCertSign, cRLSign} | |
95 // } | |
96 // | |
97 // ... | |
98 // | |
99 // pk-rsaSSA-PSS PUBLIC-KEY ::= { | |
100 // IDENTIFIER id-RSASSA-PSS | |
101 // KEY RSAPublicKey | |
102 // PARAMS TYPE RSASSA-PSS-params ARE optional | |
103 // -- Private key format not in this module -- | |
104 // CERT-KEY-USAGE { nonRepudiation, digitalSignature, | |
105 // keyCertSign, cRLSign } | |
106 // } | |
107 // | |
108 // Any RSA signature algorithm can accept a "pk-rsa" (rsaEncryption). However a | |
109 // "pk-rsaSSA-PSS" key is only accepted if the signature algorithm was for PSS | |
110 // mode: | |
111 // | |
112 // sa-rsaSSA-PSS SIGNATURE-ALGORITHM ::= { | |
113 // IDENTIFIER id-RSASSA-PSS | |
114 // PARAMS TYPE RSASSA-PSS-params ARE required | |
115 // HASHES { mda-sha1 | mda-sha224 | mda-sha256 | mda-sha384 | |
116 // | mda-sha512 } | |
117 // PUBLIC-KEYS { pk-rsa | pk-rsaSSA-PSS } | |
118 // SMIME-CAPS { IDENTIFIED BY id-RSASSA-PSS } | |
119 // } | |
120 // | |
121 // Moreover, if a "pk-rsaSSA-PSS" key was used and it optionally provided | |
122 // parameters for the algorithm, they must match those of the signature | |
123 // algorithm. | |
124 // | |
125 // COMPATIBILITY NOTE: RFC 5912 specifies that the algorithm parameters for | |
126 // pk-rsa (rsaEncryption) are absent. However in practice though it is common | |
127 // for these to be present and NULL. | |
128 WARN_UNUSED_RESULT bool ParseRsaKeyFromSpki(const der::Input& public_key_spki, | |
129 crypto::ScopedEVP_PKEY* pkey) { | |
130 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
131 | |
132 const uint8_t* ptr = public_key_spki.UnsafeData(); | |
133 crypto::ScopedRSA rsa( | |
134 d2i_RSA_PUBKEY(nullptr, &ptr, public_key_spki.Length())); | |
135 | |
136 if (!rsa || ptr != public_key_spki.UnsafeData() + public_key_spki.Length()) | |
137 return false; | |
138 | |
139 // Create a corresponding EVP_PKEY. | |
140 pkey->reset(EVP_PKEY_new()); | |
141 if (!pkey || !EVP_PKEY_set1_RSA(pkey->get(), rsa.get())) | |
142 return false; | |
143 | |
144 return true; | |
145 } | |
146 | |
147 WARN_UNUSED_RESULT bool RsaVerify(const SignatureAlgorithm& algorithm, | |
148 const der::Input& signed_data, | |
149 const der::Input& signature_value, | |
150 const der::Input& public_key_spki) { | |
151 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
152 | |
153 // TODO(eroman): This does not enforce that the SPKI's algorithm matches the | |
154 // signature algorithm. For instance it would not | |
155 // forbid a key with PSS parameters being used | |
156 // with PKCS1 padding. | |
157 crypto::ScopedEVP_PKEY public_key; | |
158 if (!ParseRsaKeyFromSpki(public_key_spki, &public_key)) | |
159 return false; | |
160 | |
161 crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); | |
162 EVP_PKEY_CTX* pctx = NULL; // Owned by |ctx|. | |
Ryan Sleevi
2015/07/16 04:05:23
nullptr?
| |
163 | |
164 const EVP_MD* digest; | |
165 if (!GetDigest(algorithm.digest(), &digest)) | |
166 return false; | |
167 | |
168 if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, NULL, public_key.get())) | |
Ryan Sleevi
2015/07/16 04:05:24
nullptr
| |
169 return false; | |
170 | |
171 // Set the RSASSA-PSS specific options. | |
172 if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && | |
173 !ApplyRsaPssOptions(algorithm.ParamsForRsaPss(), pctx)) { | |
174 return false; | |
175 } | |
176 | |
177 if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), | |
178 signed_data.Length())) { | |
179 return false; | |
180 } | |
181 | |
182 return 1 == EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), | |
183 signature_value.Length()); | |
184 } | |
185 | |
186 // Returns true if the given curve is allowed for ECDSA. The input is a | |
187 // BoringSSL NID. | |
188 // | |
189 // TODO(eroman): Extract policy decisions such as allowed curves, hashes, RSA | |
190 // modulus size, to somewhere more central. | |
191 WARN_UNUSED_RESULT bool IsAllowedCurveName(int curve_nid) { | |
192 switch (curve_nid) { | |
193 case NID_X9_62_prime256v1: | |
194 case NID_secp384r1: | |
195 case NID_secp521r1: | |
196 return true; | |
197 } | |
198 return false; | |
199 } | |
200 | |
201 // Parses an EC public key from SPKI to an EVP_PKEY. | |
202 // | |
203 // Returns true on success. | |
204 // | |
205 // RFC 5912 describes all the ECDSA signature algorithms as requiring a public | |
206 // key of type "pk-ec": | |
207 // | |
208 // pk-ec PUBLIC-KEY ::= { | |
209 // IDENTIFIER id-ecPublicKey | |
210 // KEY ECPoint | |
211 // PARAMS TYPE ECParameters ARE required | |
212 // -- Private key format not in this module -- | |
213 // CERT-KEY-USAGE { digitalSignature, nonRepudiation, keyAgreement, | |
214 // keyCertSign, cRLSign } | |
215 // } | |
216 // | |
217 // Moreover RFC 5912 stipulates what curves are allowed. The ECParameters | |
218 // MUST NOT use an implicitCurve or specificCurve for PKIX: | |
219 // | |
220 // ECParameters ::= CHOICE { | |
221 // namedCurve CURVE.&id({NamedCurve}) | |
222 // -- implicitCurve NULL | |
223 // -- implicitCurve MUST NOT be used in PKIX | |
224 // -- specifiedCurve SpecifiedCurve | |
225 // -- specifiedCurve MUST NOT be used in PKIX | |
226 // -- Details for specifiedCurve can be found in [X9.62] | |
227 // -- Any future additions to this CHOICE should be coordinated | |
228 // -- with ANSI X.9. | |
229 // } | |
230 // -- If you need to be able to decode ANSI X.9 parameter structures, | |
231 // -- uncomment the implicitCurve and specifiedCurve above, and also | |
232 // -- uncomment the following: | |
233 // --(WITH COMPONENTS {namedCurve PRESENT}) | |
234 // | |
235 // The namedCurves are extensible. The ones described by RFC 5912 are: | |
236 // | |
237 // NamedCurve CURVE ::= { | |
238 // { ID secp192r1 } | { ID sect163k1 } | { ID sect163r2 } | | |
239 // { ID secp224r1 } | { ID sect233k1 } | { ID sect233r1 } | | |
240 // { ID secp256r1 } | { ID sect283k1 } | { ID sect283r1 } | | |
241 // { ID secp384r1 } | { ID sect409k1 } | { ID sect409r1 } | | |
242 // { ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 }, | |
243 // ... -- Extensible | |
244 // } | |
245 WARN_UNUSED_RESULT bool ParseEcKeyFromSpki(const der::Input& public_key_spki, | |
246 crypto::ScopedEVP_PKEY* pkey) { | |
247 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
248 | |
249 const uint8_t* ptr = public_key_spki.UnsafeData(); | |
250 crypto::ScopedEC_KEY ec( | |
251 d2i_EC_PUBKEY(nullptr, &ptr, public_key_spki.Length())); | |
252 | |
253 if (!ec || ptr != public_key_spki.UnsafeData() + public_key_spki.Length()) | |
254 return false; | |
255 | |
256 // Enforce policy on allowed curves in case d2i_EC_PUBKEY() were to recognize | |
257 // and allow use of a weak curve. | |
258 if (!IsAllowedCurveName(EC_GROUP_get_curve_name(EC_KEY_get0_group(ec.get())))) | |
259 return false; | |
260 | |
261 // Create a corresponding EVP_PKEY. | |
262 pkey->reset(EVP_PKEY_new()); | |
263 if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey->get(), ec.get())) | |
264 return false; | |
265 | |
266 return true; | |
267 } | |
268 | |
269 WARN_UNUSED_RESULT bool EcdsaVerify(const SignatureAlgorithm& algorithm, | |
270 const der::Input& signed_data, | |
271 const der::Input& signature_value, | |
272 const der::Input& public_key_spki) { | |
273 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
274 | |
275 crypto::ScopedEVP_PKEY public_key; | |
276 if (!ParseEcKeyFromSpki(public_key_spki, &public_key)) | |
277 return false; | |
278 | |
279 crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); | |
Ryan Sleevi
2015/07/16 04:05:24
From here through 292, it seems the same as RsaVer
eroman
2015/07/16 17:34:07
The RSA version is somewhat different, in that it
eroman
2015/07/18 22:44:23
Done.
| |
280 | |
281 const EVP_MD* digest; | |
282 if (!GetDigest(algorithm.digest(), &digest)) | |
283 return false; | |
284 | |
285 if (!EVP_DigestVerifyInit(ctx.get(), NULL, digest, NULL, public_key.get()) || | |
Ryan Sleevi
2015/07/16 04:05:24
nullptr
eroman
2015/07/18 22:44:23
Done (throughout)
| |
286 !EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), | |
287 signed_data.Length())) { | |
288 return false; | |
289 } | |
290 | |
291 return 1 == EVP_DigestVerifyFinal(ctx.get(), signature_value.UnsafeData(), | |
292 signature_value.Length()); | |
293 } | |
294 | |
295 } // namespace | |
296 | |
297 bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, | |
298 const der::Input& signed_data, | |
299 const der::Input& signature_value, | |
300 const der::Input& public_key_spki) { | |
301 switch (signature_algorithm.algorithm()) { | |
302 case SignatureAlgorithmId::RsaPkcs1: | |
303 case SignatureAlgorithmId::RsaPss: | |
304 return RsaVerify(signature_algorithm, signed_data, signature_value, | |
305 public_key_spki); | |
306 case SignatureAlgorithmId::Ecdsa: | |
307 return EcdsaVerify(signature_algorithm, signed_data, signature_value, | |
308 public_key_spki); | |
309 } | |
310 | |
311 return false; // Unsupported algorithm. | |
312 } | |
313 | |
314 } // namespace net | |
315 | |
316 #endif | |
OLD | NEW |