Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(559)

Side by Side Diff: net/android/keystore_openssl.cc

Issue 11571059: Add net/android/keystore.h (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Appease the angry 'findbugs' gods. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013 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/android/keystore_openssl.h"
6
7 #include <jni.h>
8 #include <openssl/dsa.h>
9 #include <openssl/engine.h>
10 #include <openssl/evp.h>
11 #include <openssl/rsa.h>
12 #include <pthread.h>
13
14 #include "base/android/build_info.h"
15 #include "base/android/jni_android.h"
16 #include "base/android/scoped_java_ref.h"
17 #include "base/basictypes.h"
18 #include "base/logging.h"
19 #include "crypto/openssl_util.h"
20 #include "net/android/keystore.h"
21 #include "net/base/ssl_client_cert_type.h"
22
23 // This include is required to get the ECDSA_METHOD structure definition
24 // which isn't currently part of the OpenSSL official ABI. This should
25 // not be a concern for Chromium which always links against its own
26 // version of the library on Android.
27 #include <openssl/../../crypto/ecdsa/ecs_locl.h>
Ryan Sleevi 2013/01/31 03:09:53 This certainly seems the wrong way to include this
digit1 2013/01/31 17:44:30 I've added ../third_party/openssl/ to the 'include
28
29 // IMPORTANT NOTE: The following code will currently only work when used
30 // to implement client certificate support with OpenSSL. That's because
31 // only signing is implemented here.
32 //
agl 2013/01/30 14:28:53 I note that you have a number of "//" lines in her
digit1 2013/01/31 17:44:30 Done.
33
34 // The OpenSSL EVP_PKEY type is a generic wrapper around key pairs.
35 // Internally, it can hold a pointer to a RSA, DSA or ECDA structure,
agl 2013/01/30 14:28:53 ECDA -> ECDSA
digit1 2013/01/31 17:44:30 Done.
36 // which model keypair implementations of each respective crypto
37 // algorithm.
38 //
39 // The RSA type has a 'method' field pointer to a vtable-like structure
40 // called a RSA_METHOD. This contains several function pointers that
41 // correspond to operations on RSA keys (e.g. decode/encode with public
42 // key, decode/encode with private key, signing, validation), as well as
43 // a few flags.
44 //
45 // For example, the RSA_sign() function will call "method->rsa_sign()" if
46 // method->rsa_sign is not NULL, otherwise, it will perform a regular
47 // signing operation using the other fields in the RSA structure (which
48 // are used to hold the typical modulus / exponent / parameters for the
49 // key pair).
50 //
51 // This source file thus defines a custom RSA_METHOD structure, which
52 // fields points to static methods used to implement the corresponding
53 // RSA operation using platform Android APIs.
54 //
55 // However, the platform APIs require a jobject JNI reference to work.
56 // It must be stored in the RSA instance, or made accessible when the
57 // custom RSA methods are called. This is done by using RSA_set_app_data()
58 // and RSA_get_app_data().
59 //
60 // One can thus _directly_ create a new EVP_PKEY that uses a custom RSA
61 // object with the following:
62 //
63 // RSA* rsa = RSA_new()
64 // RSA_set_method(&custom_rsa_method);
65 // RSA_set_app_data(rsa, jni_private_key);
66 //
67 // EVP_PKEY* pkey = EVP_PKEY_new();
68 // EVP_PKEY_assign_RSA(pkey, rsa);
69 //
70 // Note that because EVP_PKEY_assign_RSA() is used, instead of
71 // EVP_PKEY_set1_RSA(), the new EVP_PKEY now owns the RSA object, and
72 // will destroy it when it is itself destroyed.
73 //
74 // Similarly, custom DSA_METHOD and ECDSA_METHOD are defined by this source
75 // file.
76 //
77 // Note that there is no need to define an OpenSSL ENGINE here. These
78 // are objects that can be used to expose custom methods (i.e. either
79 // RSA_METHOD, DSA_METHOD, ECDSA_METHOD, and a large number of other ones
80 // for types not related to this source file), and make them used by
81 // default for a lot of operations. Very fortunately, this is not needed
82 // here, which saves a lot of complexity.
83 //
84
85 namespace {
86
87 ///////////////////////////////////////////////////////////////////////////
88 //
89 // Custom RSA_METHOD that uses the platform APIs.
90 // Note that for now, only signing through RSA_sign() is really supported.
91 // all other method pointers are either stubs returning errors, or no-ops.
92 //
93 // See <openssl/rsa.h> for exact declaration of RSA_METHOD.
94 //
95
96 int RsaMethodPubEnc(int flen,
97 const unsigned char* from,
98 unsigned char* to,
99 RSA* rsa,
100 int padding) {
101 NOTIMPLEMENTED();
102 RSAerr(RSA_F_RSA_PUBLIC_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
103 return -1;
104 }
105
106 int RsaMethodPubDec(int flen,
107 const unsigned char* from,
108 unsigned char* to,
109 RSA* rsa,
110 int padding) {
111 NOTIMPLEMENTED();
112 RSAerr(RSA_F_RSA_PUBLIC_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
113 return -1;
114 }
115
116 int RsaMethodPrivEnc(int flen,
117 const unsigned char *from,
118 unsigned char *to,
119 RSA *rsa,
120 int padding) {
121 NOTIMPLEMENTED();
122 RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
123 return -1;
124 }
125
126 int RsaMethodPrivDec(int flen,
127 const unsigned char* from,
128 unsigned char* to,
129 RSA* rsa,
130 int padding) {
131 NOTIMPLEMENTED();
132 RSAerr(RSA_F_RSA_PRIVATE_DECRYPT, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED);
133 return -1;
134 }
135
136 int RsaMethodInit(RSA* rsa) {
137 // Required to ensure that RsaMethodSign will be called.
138 rsa->flags |= RSA_FLAG_SIGN_VER;
139 return 0;
140 }
141
142 int RsaMethodFinish(RSA* rsa) {
143 // Ensure the global JNI reference is destroyed with this key.
144 jobject key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
145 if (key != NULL) {
146 RSA_set_app_data(rsa, NULL);
147 JNIEnv* env = base::android::AttachCurrentThread();
148 env->DeleteGlobalRef(key);
149 }
150 // Actual return value is ignored by OpenSSL. There are no docs
151 // explaining what this is supposed to be.
152 return 0;
153 }
154
155 int RsaMethodSign(int type,
156 const unsigned char* message,
157 unsigned int message_len,
158 unsigned char* signature,
159 unsigned int* signature_len,
160 const RSA* rsa) {
161 // This is only used for client certificate support, which
162 // will always pass the NID_md5_sha1 |type| value.
agl 2013/01/30 14:28:53 This will stop being true when we add TLS 1.2 supp
digit1 2013/01/31 17:44:30 I know, that's why I've put the check here. I don'
163 DCHECK_EQ(NID_md5_sha1, type);
164 if (type != NID_md5_sha1) {
165 RSAerr(RSA_F_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE);
166 return 0;
167 }
168 // Retrieve private key JNI reference.
169 jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
170 if (!private_key) {
171 LOG(WARNING) << "Null JNI reference passed to RsaMethodSign!";
172 return 0;
173 }
174 // Sign message with it through JNI.
175 base::StringPiece message_piece(reinterpret_cast<const char*>(message),
176 static_cast<size_t>(message_len));
177 std::vector<uint8> result;
178
179 if (!net::android::SignWithPrivateKey(
180 private_key, message_piece, &result)) {
181 LOG(WARNING) << "Could not sign message in RsaMethodSign!";
182 return 0;
183 }
184 // Copy result to OpenSSL-provided buffer
185 memcpy(signature, &result[0], result.size());
Ryan Sleevi 2013/01/31 03:09:53 BUG: No check that result.size() == RSA_size(rsa)
digit1 2013/01/31 17:44:30 I have added code that setups r->n in the custom R
186 *signature_len = static_cast<unsigned int>(result.size());
187 return 1;
188 }
189
190 const RSA_METHOD android_rsa_method = {
191 /* .name = */ "Android signing-only RSA method",
192 /* .rsa_pub_enc = */ RsaMethodPubEnc,
193 /* .rsa_pub_dec = */ RsaMethodPubDec,
194 /* .rsa_priv_enc = */ RsaMethodPrivEnc,
195 /* .rsa_priv_dec = */ RsaMethodPrivDec,
196 /* .rsa_mod_exp = */ NULL,
197 /* .bn_mod_exp = */ NULL,
198 /* .init = */ RsaMethodInit,
199 /* .finish = */ RsaMethodFinish,
200 /* .flags = */ RSA_FLAG_SIGN_VER, // indicates that rsa_sign is usable.
201 /* .app_data = */ NULL,
202 /* .rsa_sign = */ RsaMethodSign,
203 /* .rsa_verify = */ NULL,
204 /* .rsa_keygen = */ NULL,
205 };
206
207 ///////////////////////////////////////////////////////////////////////////
208 //
209 // Custom DSA_METHOD that uses the platform APIs.
210 // Note that for now, only signing through DSA_sign() is really supported.
211 // all other method pointers are either stubs returning errors, or no-ops.
212 //
213 // See <openssl/dsa.h> for exact declaration of DSA_METHOD.
214 //
215 // Note: There is no DSA_set_app_data() and DSA_get_app_data() functions,
216 // but RSA_set_app_data() is defined as a simple macro that calls
217 // RSA_set_ex_data() with a hard-coded index of 0, so this code
218 // does the same thing here.
219 //
220
221 static DSA_SIG* DsaMethodDoSign(const unsigned char* dgst,
222 int dlen,
223 DSA* dsa) {
224 // Extract the JNI reference to the PrivateKey object.
225 jobject private_key = reinterpret_cast<jobject>(DSA_get_ex_data(dsa, 0));
226 if (private_key == NULL)
227 return NULL;
228
229 // Sign the message with it, calling platform APIs.
230 std::vector<uint8> signature;
231 if (!net::android::SignWithPrivateKey(
232 private_key,
233 base::StringPiece(
234 reinterpret_cast<const char*>(dgst),
235 static_cast<size_t>(dlen)),
236 &signature)) {
Ryan Sleevi 2013/01/31 03:09:53 I don't believe the indents are correct here for t
digit1 2013/01/31 17:44:30 I'm not sure, what would you propose instead?
237 return NULL;
238 }
239
digit1 2013/01/31 17:44:30 Note: I've added code to implement DSA_size() prop
240 // Convert the signature into a DSA_SIG object.
241 const unsigned char* sigbuf =
242 reinterpret_cast<const unsigned char*>(&signature[0]);
243 int siglen = static_cast<size_t>(signature.size());
244 DSA_SIG* dsa_sig = d2i_DSA_SIG(NULL, &sigbuf, siglen);
245 return dsa_sig;
246 }
247
248 static int DsaMethodSignSetup(DSA* dsa,
249 BN_CTX* ctx_in,
250 BIGNUM** kinvp,
251 BIGNUM** rp) {
252 NOTIMPLEMENTED();
253 DSAerr(DSA_F_DSA_SIGN_SETUP, DSA_R_INVALID_DIGEST_TYPE);
254 return -1;
255 }
256
257 static int DsaMethodDoVerify(const unsigned char* dgst,
258 int dgst_len,
259 DSA_SIG* sig,
260 DSA* dsa) {
261 NOTIMPLEMENTED();
262 DSAerr(DSA_F_DSA_DO_VERIFY, DSA_R_INVALID_DIGEST_TYPE);
263 return -1;
264 }
265
266 static int DsaMethodFinish(DSA* dsa) {
267 // Free the global JNI reference.
268 jobject key = reinterpret_cast<jobject>(DSA_get_ex_data(dsa,0));
269 if (key != NULL) {
270 DSA_set_ex_data(dsa, 0, NULL);
271 JNIEnv* env = base::android::AttachCurrentThread();
272 env->DeleteGlobalRef(key);
273 }
274 // Actual return value is ignored by OpenSSL. There are no docs
275 // explaining what this is supposed to be.
276 return 0;
277 }
278
279 const DSA_METHOD android_dsa_method = {
280 /* .name = */ "Android signing-only DSA method",
281 /* .dsa_do_sign = */ DsaMethodDoSign,
282 /* .dsa_sign_setup = */ DsaMethodSignSetup,
283 /* .dsa_do_verify = */ DsaMethodDoVerify,
284 /* .dsa_mod_exp = */ NULL,
285 /* .bn_mod_exp = */ NULL,
286 /* .init = */ NULL, // nothing to do here.
287 /* .finish = */ DsaMethodFinish,
288 /* .flags = */ 0,
289 /* .app_data = */ NULL,
290 /* .dsa_paramgem = */ NULL,
291 /* .dsa_keygen = */ NULL
292 };
293
294 ///////////////////////////////////////////////////////////////////////////
295 //
296 // Custom ECDSA_METHOD that uses the platform APIs.
297 // Note that for now, only signing through ECDSA_sign() is really supported.
298 // all other method pointers are either stubs returning errors, or no-ops.
299 //
300
301 // Note: The ECDSA_METHOD structure doesn't have init/finish
302 // methods. As such, the only way to to ensure the global
303 // JNI reference is properly released when the EVP_PKEY is
304 // destroyed is to use a custom EX_DATA type.
305
306 // Used to ensure that the global JNI reference associated with a
307 // custom EC_KEY + ECDSA_METHOD is released when the key is destroyed.
308 void ExDataFree(void* parent,
309 void* ptr,
310 CRYPTO_EX_DATA* ad,
311 int idx,
312 long argl,
313 void* argp) {
314 jobject private_key = reinterpret_cast<jobject>(ptr);
315 if (private_key == NULL)
316 return;
317
318 CRYPTO_set_ex_data(ad, idx, NULL);
319
320 JNIEnv* env = base::android::AttachCurrentThread();
321 env->DeleteGlobalRef(private_key);
322 }
323
324 int ExDataDup(CRYPTO_EX_DATA* to,
325 CRYPTO_EX_DATA* from,
326 void* from_d,
327 int idx,
328 long argl,
329 void* argp) {
330 // This callback shall never be called with the current OpenSSL
331 // implementation (the library only ever duplicates EX_DATA items
332 // for SSL and BIO objects). But provide this to catch regressions
333 // in the future.
334 NOTIMPLEMENTED();
335 LOG(WARNING) << "ExDataDup for was called for ECDSA custom key !?";
336 // Return value is currently ignored by OpenSSL.
337 return 0;
338 }
339
340 int s_ecdsa_ex_data_index;
341
342 void EcdsaInitExDataIndex(void) {
343 // Note that OpenSSL does not provide a way to unregister these
344 // indices, so don't worry about releasing them on program exit.
345 // There is also no need for an ExDataNew function.
346 s_ecdsa_ex_data_index = ECDSA_get_ex_new_index(0, // argl
347 NULL, // argp
348 NULL, // new_func
349 ExDataDup, // dup_func
350 ExDataFree); // free_func
351 }
352
353 // Returns the index of the custom EX_DATA used to store the JNI reference.
354 int EcdsaGetExDataIndex(void) {
355 // The index must be initialized once per processs, and this function
356 // can be called from distinct threads. Use a pthread_once_t to perform
357 // proper lazy initialization.
358 static pthread_once_t once = PTHREAD_ONCE_INIT;
359 pthread_once(&once, EcdsaInitExDataIndex);
360 return s_ecdsa_ex_data_index;
361 }
362
363 ECDSA_SIG* EcdsaMethodDoSign(const unsigned char* dgst,
364 int dgst_len,
365 const BIGNUM* inv,
366 const BIGNUM* rp,
367 EC_KEY* eckey) {
368 // Retrieve private key JNI reference.
369 jobject private_key = reinterpret_cast<jobject>(
370 ECDSA_get_ex_data(eckey, EcdsaGetExDataIndex()));
371 if (!private_key) {
372 LOG(WARNING) << "Null JNI reference passed to EcdsaMethodDoSign!";
373 return NULL;
374 }
375 // Sign message with it through JNI.
376 std::vector<uint8> signature;
377 if (!net::android::SignWithPrivateKey(
378 private_key,
379 base::StringPiece(
380 reinterpret_cast<const char*>(dgst),
381 static_cast<size_t>(dgst_len)),
382 &signature)) {
383 LOG(WARNING) << "Could not sign message in EcdsaMethodDoSign!";
384 return NULL;
385 }
386
digit1 2013/01/31 17:44:30 I'd like to add a check for signature.size() == EC
Ryan Sleevi 2013/02/01 00:51:29 So the ECParameterSpec ( http://docs.oracle.com/ja
digit1 2013/02/01 21:44:36 That's essentially what I ended up implementing. I
387 // Convert signature to ECDSA_SIG object
388 const unsigned char* sigbuf =
389 reinterpret_cast<const unsigned char*>(&signature[0]);
390 long siglen = static_cast<long>(signature.size());
391 return d2i_ECDSA_SIG(NULL, &sigbuf, siglen);
392 }
393
394 int EcdsaMethodSignSetup(EC_KEY* eckey,
395 BN_CTX* ctx,
396 BIGNUM** kinv,
397 BIGNUM** r) {
398 NOTIMPLEMENTED();
399 ECDSAerr(ECDSA_F_ECDSA_SIGN_SETUP, ECDSA_R_ERR_EC_LIB);
400 return -1;
401 }
402
403 int EcdsaMethodDoVerify(const unsigned char* dgst,
404 int dgst_len,
405 const ECDSA_SIG* sig,
406 EC_KEY* eckey) {
407 NOTIMPLEMENTED();
408 ECDSAerr(ECDSA_F_ECDSA_DO_VERIFY, ECDSA_R_ERR_EC_LIB);
409 return -1;
410 }
411
412 const ECDSA_METHOD android_ecdsa_method = {
413 /* .name = */ "Android signing-only ECDSA method",
414 /* .ecdsa_do_sign = */ EcdsaMethodDoSign,
415 /* .ecdsa_sign_setup = */ EcdsaMethodSignSetup,
416 /* .ecdsa_do_verify = */ EcdsaMethodDoVerify,
417 /* .flags = */ 0,
418 /* .app_data = */ NULL,
419 };
420
421 } // namespace
422
423
424 namespace net {
425 namespace android {
426
427 EVP_PKEY* GetOpenSSLPrivateKeyWrapper(jobject private_key) {
428 // Create scoped JNI global reference from the private key.
429 // This ensure that it will be usable from any thread, not only
430 // from the caller's.
431 //
432 // Note: "ScopedJavaGlobalRef<jobject> ref(a_jobject)" doesn't
433 // compile. Route around this by creating an empty object first,
434 // then resetting it.
435 base::android::ScopedJavaGlobalRef<jobject> global_key;
436 global_key.Reset(NULL, private_key);
437 if (global_key.is_null())
438 return NULL;
439
440 // Create new empty EVP_PKEY instance.
441 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
442 if (!pkey.get())
443 return NULL;
444
445 // Create sub key type, depending on private key's algorithm type.
446 PrivateKeyType key_type = GetPrivateKeyType(global_key.obj());
447 switch (key_type) {
448 case PRIVATE_KEY_TYPE_RSA:
449 {
450 // Route around platform bug: if Android < 4.2, then
451 // base::android::SignWithPrivateKey() cannot work, so
452 // instead, obtain a raw EVP_PKEY* to the system object
453 // backing this PrivateKey object.
454 const int kAndroid42ApiLevel = 17;
455 if (base::android::BuildInfo::GetInstance()->sdk_int() <
456 kAndroid42ApiLevel) {
457 EVP_PKEY* sys_pkey =
458 GetOpenSSLSystemHandleForPrivateKey(global_key.obj());
459 if (sys_pkey == NULL) {
460 LOG(ERROR) << "Can't get system OpenSSL key!";
461 return NULL;
462 }
463 CRYPTO_add(&sys_pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
464 pkey.reset(sys_pkey);
465 } else {
466 RSA* rsa = RSA_new();
467 RSA_set_method(rsa, &android_rsa_method);
468 RSA_set_app_data(rsa, global_key.Release());
469 EVP_PKEY_assign_RSA(pkey.get(), rsa);
470 }
471 }
472 break;
473 case PRIVATE_KEY_TYPE_DSA:
474 {
475 DSA* dsa = DSA_new();
476 DSA_set_method(dsa, &android_dsa_method);
477 DSA_set_ex_data(dsa, 0, global_key.Release());
478 EVP_PKEY_assign_DSA(pkey.get(), dsa);
479 }
480 break;
481 case PRIVATE_KEY_TYPE_ECDSA:
482 {
483 EC_KEY* ec_key = EC_KEY_new();
484 ECDSA_set_method(ec_key, &android_ecdsa_method);
485 ECDSA_set_ex_data(ec_key, EcdsaGetExDataIndex(), global_key.Release());
486 EVP_PKEY_assign_EC_KEY(pkey.get(), ec_key);
487 }
488 break;
489 default:
490 LOG(WARNING)
491 << "GetOpenSSLPrivateKeyWrapper() called with invalid key type";
492 return NULL;
493 }
494
495 return pkey.release();
496 }
497
498 } // namespace android
499 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698