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

Side by Side Diff: net/android/keystore_unittest.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 <openssl/dsa.h>
6 #include <openssl/ecdsa.h>
7 #include <openssl/err.h>
8 #include <openssl/evp.h>
9 #include <openssl/rsa.h>
10 #include <openssl/x509.h>
11
12 #include "base/android/build_info.h"
13 #include "base/android/jni_android.h"
14 #include "base/android/jni_array.h"
15 #include "base/android/scoped_java_ref.h"
16 #include "base/basictypes.h"
17 #include "base/bind.h"
18 #include "base/callback.h"
19 #include "base/compiler_specific.h"
20 #include "base/file_path.h"
21 #include "base/file_util.h"
22 #include "crypto/openssl_util.h"
23 #include "jni/AndroidKeyStoreTestUtil_jni.h"
24 #include "net/android/keystore.h"
25 #include "net/android/keystore_openssl.h"
26 #include "net/base/test_data_directory.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 using base::android::AttachCurrentThread;
30 using base::android::ScopedJavaLocalRef;
31 using base::android::ToJavaByteArray;
32
33 // Technical note:
34 //
35 // This source file not only checks that signing with SignWithPrivateKey()
36 // works correctly, it also verifies that the generated signature matches
37 // 100% of what OpenSSL generates when calling RSA_sign(NID_md5_sha1,...),
38 // DSA_sign(0, ...) or ECDSA_sign(0, ...).
39 //
40 // That's crucial to ensure that this function can later be used to
41 // implement client certificate support. More specifically, that it is
42 // possible to create a custom EVP_PKEY that uses SignWithPrivateKey()
43 // internally to perform RSA/DSA/ECDSA signing, as invoked by the
44 // OpenSSL code at ssl/s3_clnt.c:ssl3_send_client_verify().
45 //
46 // Finally, it also checks that using the EVP_PKEY generated with
47 // GetOpenSSLPrivateKeyWrapper() works correctly.
48 //
49
50 namespace net {
51 namespace android {
52
53 namespace {
54
55 JNIEnv* InitEnv() {
56 JNIEnv* env = AttachCurrentThread();
57 static bool inited = false;
58 if (!inited) {
59 RegisterNativesImpl(env);
60 inited = true;
61 }
62 return env;
63 }
64
65 // Returns true if running on an Android version older than 4.2
66 bool IsOnAndroidOlderThan_4_2(void) {
67 const int kAndroid42ApiLevel = 17;
68 int level = base::android::BuildInfo::GetInstance()->sdk_int();
69 return level < kAndroid42ApiLevel;
70 }
71
72 // Implements the callback expected by ERR_print_errors_cb().
73 // used by GetOpenSSLErrorString below.
74 int openssl_print_error_callback(const char* msg, size_t msglen, void* u) {
75 std::string* result = reinterpret_cast<std::string*>(u);
76 result->append(msg, msglen);
77 return 1;
78 }
79
80 // Retrieves the OpenSSL error as a string
81 std::string GetOpenSSLErrorString(void) {
82 std::string result;
83 ERR_print_errors_cb(openssl_print_error_callback, &result);
84 return result;
85 }
86
87 // Retrieve a JNI local ref from encoded PKCS#8 data.
88 ScopedJavaLocalRef<jobject> GetPKCS8PrivateKey(
89 PrivateKeyType key_type,
90 const std::string& pkcs8_key) {
91 JNIEnv* env = InitEnv();
92 ScopedJavaLocalRef<jbyteArray> bytes(
93 ToJavaByteArray(env,
94 reinterpret_cast<const uint8*>(pkcs8_key.data()),
95 pkcs8_key.size()));
96
97 ScopedJavaLocalRef<jobject> key(
98 Java_AndroidKeyStoreTestUtil_createPrivateKeyFromPKCS8(
99 env, key_type, bytes.obj()));
100
101 return key;
102 }
103
104 // Create an OpenSSL EVP_PKEY object from a PKCS#8 in-memory content.
105 EVP_PKEY* GetOpenSSLPKCS8PrivateKey(int type, const std::string& key) {
106 const unsigned char* p =
107 reinterpret_cast<const unsigned char*>(key.data());
108 long p_length = static_cast<long>(key.size());
109 return d2i_PrivateKey(type, NULL, &p, p_length);
110 }
111
112 // Load a given key file into a string. Assert on error.
113 // |filename| is the key file name.
114 // |bytes| will receive the raw key data.
115 void ImportKeyFile(const char* filename,
116 std::string& bytes) {
117 FilePath certs_dir = GetTestCertsDirectory();
118 FilePath file_path = certs_dir.AppendASCII(filename);
119 ASSERT_TRUE(file_util::ReadFileToString(file_path, &bytes)) <<
120 "Could not load key file: " << filename;
121 }
122
123 const char kTestRsaKeyFile[] = "android-test-key-rsa.pkcs8";
124
125 // Retrieve a JNI local ref for our test RSA key.
126 ScopedJavaLocalRef<jobject> GetRSATestKey() {
127 std::string key;
128 ImportKeyFile(kTestRsaKeyFile, key);
129 return GetPKCS8PrivateKey(PRIVATE_KEY_TYPE_RSA, key);
130 }
131
132 // Retrieve the OpenSSL object for our test RSA key.
133 EVP_PKEY* GetRSATestKeyOpenSSL() {
134 std::string key;
135 ImportKeyFile(kTestRsaKeyFile, key);
136 return GetOpenSSLPKCS8PrivateKey(EVP_PKEY_RSA, key);
137 }
138
139 const char kTestDsaKeyFile[] = "android-test-key-dsa.pkcs8";
140 const char kTestDsaPublicKeyFile[] = "android-test-key-dsa-public.der";
141
142 // Retrieve a JNI local ref for our test DSA key.
143 ScopedJavaLocalRef<jobject> GetDSATestKey() {
144 std::string key;
145 ImportKeyFile(kTestDsaKeyFile, key);
146 return GetPKCS8PrivateKey(PRIVATE_KEY_TYPE_DSA, key);
147 }
148
149 // Retrieve the OpenSSL object for our test DSA key.
150 EVP_PKEY* GetDSATestKeyOpenSSL() {
151 std::string key;
152 ImportKeyFile(kTestDsaKeyFile, key);
153 return GetOpenSSLPKCS8PrivateKey(EVP_PKEY_DSA, key);
154 }
155
156 // Call this function to verify that one message signed with our
157 // test DSA private key is correct. Since DSA signing introduces
158 // random elements in the signature, it is not possible to compare
159 // signature bits directly. However, one can use the public key
160 // to do the check.
161 void VerifyTestDSASignature(const base::StringPiece& message,
162 const base::StringPiece& signature) {
163 std::string public_key;
164 ImportKeyFile(kTestDsaPublicKeyFile, public_key);
165 const unsigned char* p =
166 reinterpret_cast<const unsigned char*>(public_key.data());
167 long length = static_cast<long>(public_key.size());
168 DSA* pub_key = d2i_DSA_PUBKEY(NULL, &p, length);
169 ASSERT_TRUE(pub_key != NULL);
170
171 const unsigned char* digest =
172 reinterpret_cast<const unsigned char*>(message.data());
173 int digest_len = static_cast<int>(message.size());
174 const unsigned char* sigbuf =
175 reinterpret_cast<const unsigned char*>(signature.data());
176 int siglen = static_cast<int>(signature.size());
177
178 ASSERT_EQ(1, DSA_verify(0, digest, digest_len, sigbuf, siglen, pub_key));
179 DSA_free(pub_key);
180 }
181
182 const char kTestEcdsaKeyFile[] = "android-test-key-ecdsa.pkcs8";
183 const char kTestEcdsaPublicKeyFile[] = "android-test-key-ecdsa-public.der";
184
185 // Retrieve a JNI local ref for our test ECDSA key.
186 ScopedJavaLocalRef<jobject> GetECDSATestKey() {
187 std::string key;
188 ImportKeyFile(kTestEcdsaKeyFile, key);
189 return GetPKCS8PrivateKey(PRIVATE_KEY_TYPE_ECDSA, key);
190 }
191
192 // Retrieve the OpenSSL object for our test ECDSA key.
193 EVP_PKEY* GetECDSATestKeyOpenSSL() {
194 std::string key;
195 ImportKeyFile(kTestEcdsaKeyFile, key);
196 return GetOpenSSLPKCS8PrivateKey(EVP_PKEY_EC, key);
197 }
198
199 // Call this function to verify that one message signed with our
200 // test DSA private key is correct. Since DSA signing introduces
201 // random elements in the signature, it is not possible to compare
202 // signature bits directly. However, one can use the public key
203 // to do the check.
204 void VerifyTestECDSASignature(const base::StringPiece& message,
205 const base::StringPiece& signature) {
206 std::string public_key;
207 ImportKeyFile(kTestEcdsaPublicKeyFile, public_key);
208 const unsigned char* p =
209 reinterpret_cast<const unsigned char*>(public_key.data());
210 long length = static_cast<long>(public_key.size());
211 EC_KEY* pub_key = d2i_EC_PUBKEY(NULL, &p, length);
212 ASSERT_TRUE(pub_key != NULL);
213
214 const unsigned char* digest =
215 reinterpret_cast<const unsigned char*>(message.data());
216 int digest_len = static_cast<int>(message.size());
217 const unsigned char* sigbuf =
218 reinterpret_cast<const unsigned char*>(signature.data());
219 int siglen = static_cast<int>(signature.size());
220
221 ASSERT_EQ(
222 1, ECDSA_verify(0, digest, digest_len, sigbuf, siglen, pub_key));
Ryan Sleevi 2013/01/31 03:09:53 BUG: If this ASSERT fails, you end up leaking pub_
digit1 2013/01/31 17:44:30 Ah, I didn't realize we didn't want memory leaks,
223 EC_KEY_free(pub_key);
224 }
225
226 // Sign a message with OpenSSL, return the result as a string.
227 // |message| is the message to be signed.
228 // |openssl_key| is an OpenSSL EVP_PKEY to use.
229 // |result| receives the result.
230 void SignWithOpenSSL(const base::StringPiece& message,
231 EVP_PKEY* openssl_key,
232 std::string* result) {
233 const unsigned char* digest =
234 reinterpret_cast<const unsigned char*>(message.data());
235 unsigned int digest_len = static_cast<unsigned int>(message.size());
236 // Calling size functions like "RSA_size()" doesn't work at all
237 // with custom RSA_METHOD implementations. That's probably a bug
238 // in OpenSSL, so instead just use a very large buffer.
239 // Note that the code in ssl/s3_clnt.c does something similar.
Ryan Sleevi 2013/01/31 03:09:53 This does not seem like something we should rely o
digit1 2013/01/31 17:44:30 I've update the code appropriately.
240 unsigned char openssl_signature[8192];
241 unsigned int openssl_signature_len = 0;
242 int key_type = EVP_PKEY_id(openssl_key);
243 switch (key_type) {
244 case EVP_PKEY_RSA:
245 {
246 RSA* rsa = EVP_PKEY_get1_RSA(openssl_key);
247 ASSERT_TRUE(rsa != NULL);
Ryan Sleevi 2013/01/31 03:09:53 nit: ASSERT_TRUE(rsa) (and same throughout this fi
248 int ret = RSA_sign(NID_md5_sha1,
249 digest,
250 digest_len,
251 openssl_signature,
252 &openssl_signature_len,
253 rsa);
254 ASSERT_EQ(1, ret) << GetOpenSSLErrorString();
255 RSA_free(rsa);
256 }
257 break;
258 case EVP_PKEY_DSA:
259 {
260 DSA* dsa = EVP_PKEY_get1_DSA(openssl_key);
261 ASSERT_TRUE(dsa != NULL);
262 int ret = DSA_sign(0, // ignored by the function
263 digest,
264 digest_len,
265 openssl_signature,
266 &openssl_signature_len,
267 dsa);
268 ASSERT_EQ(1, ret) << GetOpenSSLErrorString();
269 DSA_free(dsa);
270 }
271 break;
272 case EVP_PKEY_EC:
273 {
274 EC_KEY* ecdsa = EVP_PKEY_get1_EC_KEY(openssl_key);
275 ASSERT_TRUE(ecdsa != NULL);
276 int ret = ECDSA_sign(0, // ignored by the function
277 digest,
278 digest_len,
279 openssl_signature,
280 &openssl_signature_len,
281 ecdsa);
282 ASSERT_EQ(1, ret) << GetOpenSSLErrorString();
283 EC_KEY_free(ecdsa);
284 }
285 break;
286 default:
287 LOG(WARNING) << "Invalid OpenSSL key type: " + key_type;
288 return;
289 }
290 result->assign(reinterpret_cast<const char*>(openssl_signature),
291 static_cast<size_t>(openssl_signature_len));
292 }
293
294 // Check that a generated signature for a given message matches
295 // OpenSSL output byte-by-byte.
296 // |message| is the input message.
297 // |signature| is the generated signature for the message.
298 // |openssl_key| is a raw EVP_PKEY for the same private key than the
299 // one which was used to generate the signature.
300 void CompareSignatureWithOpenSSL(const base::StringPiece& message,
301 const base::StringPiece& signature,
302 EVP_PKEY* openssl_key) {
303 std::string openssl_signature;
304 SignWithOpenSSL(message, openssl_key, &openssl_signature);
305
306 ASSERT_EQ(signature.size(), openssl_signature.size());
307 for (size_t n = 0; n < signature.size(); ++n)
308 ASSERT_EQ(openssl_signature[n], signature[n]);
309
310 // All good, just exit.
311 }
312
313 // Sign a message with our platform API.
314 //
315 // |android_key| is a JNI reference to the platform PrivateKey object.
316 // |openssl_key| is a pointer to an OpenSSL key object for the exact
317 // same key content.
318 // |message| is a message.
319 // |result| will receive the result.
320 void DoKeySigning(jobject android_key,
321 EVP_PKEY* openssl_key,
322 const base::StringPiece& message,
323 std::string* result) {
324 // First, get the platform signature.
325 std::vector<uint8> android_signature;
326 ASSERT_TRUE(
327 SignWithPrivateKey(android_key,
328 message,
329 &android_signature));
330
331 result->assign(
332 reinterpret_cast<const char*>(&android_signature[0]),
333 android_signature.size());
334 }
335
336 // Sign a message with our OpenSSL EVP_PKEY wrapper around platform
337 // APIS.
338 //
339 // |android_key| is a JNI reference to the platform PrivateKey object.
340 // |openssl_key| is a pointer to an OpenSSL key object for the exact
341 // same key content.
342 // |message| is a message.
343 // |result| will receive the result.
344 void DoKeySigningWithWrapper(EVP_PKEY* wrapper_key,
345 EVP_PKEY* openssl_key,
346 const base::StringPiece& message,
347 std::string* result) {
348 // First, get the platform signature.
349 std::string wrapper_signature;
350 SignWithOpenSSL(message, wrapper_key, &wrapper_signature);
351 ASSERT_NE(0U, wrapper_signature.size());
352
353 result->assign(
354 reinterpret_cast<const char*>(&wrapper_signature[0]),
355 wrapper_signature.size());
356 }
357
358 } // namespace
359
360 TEST(AndroidKeyStore,GetPrivateKeyTypeRSA) {
361 crypto::OpenSSLErrStackTracer err_trace(FROM_HERE);
362
363 ScopedJavaLocalRef<jobject> rsa_key = GetRSATestKey();
364 ASSERT_FALSE(rsa_key.is_null());
365 EXPECT_EQ(PRIVATE_KEY_TYPE_RSA,
366 GetPrivateKeyType(rsa_key.obj()));
367 }
368
369 TEST(AndroidKeyStore,SignWithPrivateKeyRSA) {
370 ScopedJavaLocalRef<jobject> rsa_key = GetRSATestKey();
371 ASSERT_FALSE(rsa_key.is_null());
372
373 if (IsOnAndroidOlderThan_4_2()) {
374 LOG(INFO) << "This test can't run on Android < 4.2";
375 return;
376 }
377
378 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> openssl_key(
379 GetRSATestKeyOpenSSL());
380 ASSERT_TRUE(openssl_key.get() != NULL);
Ryan Sleevi 2013/01/31 03:09:53 Drop the != NULL (same as before and same througho
digit1 2013/01/31 17:44:30 I was just using the recommendations from the GTes
381
382 // Message size must be 36 for RSA_sign(NID_md5_sha1,...) to return
383 // without an error.
384 // See third_party/openssl/openssl/crypto/rsa/rsa_sign.c
Ryan Sleevi 2013/01/31 03:09:53 You don't need to cross-reference this. The 36 byt
385 std::string message = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
386 ASSERT_EQ(36U, message.size());
387
388 std::string signature;
389 DoKeySigning(rsa_key.obj(), openssl_key.get(), message, &signature);
390 CompareSignatureWithOpenSSL(message, signature, openssl_key.get());
391 // All good.
392 }
393
394 TEST(AndroidKeyStore,SignWithWrapperKeyRSA) {
395 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
396
397 ScopedJavaLocalRef<jobject> rsa_key = GetRSATestKey();
398 ASSERT_FALSE(rsa_key.is_null());
399
400 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> wrapper_key(
401 GetOpenSSLPrivateKeyWrapper(rsa_key.obj()));
402 ASSERT_TRUE(wrapper_key.get() != NULL);
403
404 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> openssl_key(
405 GetRSATestKeyOpenSSL());
406 ASSERT_TRUE(openssl_key.get() != NULL);
407
408 // Message size must be 36 for RSA_sign(NID_md5_sha1,...) to return
409 // without an error.
410 std::string message = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
411 ASSERT_EQ(36U, message.size());
412
413 std::string signature;
414 DoKeySigningWithWrapper(wrapper_key.get(),
415 openssl_key.get(),
416 message,
417 &signature);
418 CompareSignatureWithOpenSSL(message, signature, openssl_key.get());
419 // All good.
420 }
421
422 TEST(AndroidKeyStore,GetPrivateKeyTypeDSA) {
423 crypto::OpenSSLErrStackTracer err_trace(FROM_HERE);
424
425 ScopedJavaLocalRef<jobject> dsa_key = GetDSATestKey();
426 ASSERT_FALSE(dsa_key.is_null());
427 EXPECT_EQ(PRIVATE_KEY_TYPE_DSA,
428 GetPrivateKeyType(dsa_key.obj()));
429 }
430
431 TEST(AndroidKeyStore,SignWithPrivateKeyDSA) {
432 ScopedJavaLocalRef<jobject> dsa_key = GetDSATestKey();
433 ASSERT_FALSE(dsa_key.is_null());
434
435 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> openssl_key(
436 GetDSATestKeyOpenSSL());
437 ASSERT_TRUE(openssl_key.get() != NULL);
438
439 std::string message = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
440 ASSERT_EQ(36U, message.size());
441
442 std::string signature;
443 DoKeySigning(dsa_key.obj(), openssl_key.get(), message, &signature);
444 VerifyTestDSASignature(message, signature);
445 // All good.
446 }
447
448 TEST(AndroidKeyStore,SignWithWrapperKeyDSA) {
449 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
450
451 ScopedJavaLocalRef<jobject> dsa_key = GetDSATestKey();
452 ASSERT_FALSE(dsa_key.is_null());
453
454 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> wrapper_key(
455 GetOpenSSLPrivateKeyWrapper(dsa_key.obj()));
456 ASSERT_TRUE(wrapper_key.get() != NULL);
457
458 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> openssl_key(
459 GetDSATestKeyOpenSSL());
460 ASSERT_TRUE(openssl_key.get() != NULL);
461
462 std::string message = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
463 std::string signature;
464 DoKeySigningWithWrapper(wrapper_key.get(),
465 openssl_key.get(),
466 message,
467 &signature);
468 VerifyTestDSASignature(message, signature);
469 // All good.
470 }
471
472 TEST(AndroidKeyStore,GetPrivateKeyTypeECDSA) {
473 crypto::OpenSSLErrStackTracer err_trace(FROM_HERE);
474
475 ScopedJavaLocalRef<jobject> ecdsa_key = GetECDSATestKey();
476 ASSERT_FALSE(ecdsa_key.is_null());
477 EXPECT_EQ(PRIVATE_KEY_TYPE_ECDSA,
478 GetPrivateKeyType(ecdsa_key.obj()));
479 }
480
481 TEST(AndroidKeyStore,SignWithPrivateKeyECDSA) {
482 ScopedJavaLocalRef<jobject> ecdsa_key = GetECDSATestKey();
483 ASSERT_FALSE(ecdsa_key.is_null());
484
485 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> openssl_key(
486 GetECDSATestKeyOpenSSL());
487 ASSERT_TRUE(openssl_key.get() != NULL);
488
489 std::string message = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
490 std::string signature;
491 DoKeySigning(ecdsa_key.obj(), openssl_key.get(), message, &signature);
492 VerifyTestECDSASignature(message, signature);
493 // All good.
494 }
495
496 TEST(AndroidKeyStore, SignWithWrapperKeyECDSA) {
497 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
498
499 ScopedJavaLocalRef<jobject> ecdsa_key = GetECDSATestKey();
500 ASSERT_FALSE(ecdsa_key.is_null());
501
502 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> wrapper_key(
503 GetOpenSSLPrivateKeyWrapper(ecdsa_key.obj()));
504 ASSERT_TRUE(wrapper_key.get() != NULL);
505
506 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> openssl_key(
507 GetECDSATestKeyOpenSSL());
508 ASSERT_TRUE(openssl_key.get() != NULL);
509
510 std::string message = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
511 std::string signature;
512 DoKeySigningWithWrapper(wrapper_key.get(),
513 openssl_key.get(),
514 message,
515 &signature);
516 VerifyTestECDSASignature(message, signature);
517 // All good.
518 }
519
520 } // namespace android
521 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698