OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
davidben
2016/08/30 20:08:32
This file is actually copied over from keystore_un
| |
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/digest.h> | |
6 #include <openssl/ecdsa.h> | |
7 #include <openssl/err.h> | |
8 #include <openssl/evp.h> | |
9 #include <openssl/pem.h> | |
10 #include <openssl/rsa.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/bind.h" | |
17 #include "base/callback.h" | |
18 #include "base/compiler_specific.h" | |
19 #include "base/files/file_path.h" | |
20 #include "base/files/file_util.h" | |
21 #include "base/files/scoped_file.h" | |
22 #include "base/run_loop.h" | |
23 #include "base/strings/string_number_conversions.h" | |
24 #include "base/strings/string_util.h" | |
25 #include "crypto/auto_cbb.h" | |
26 #include "crypto/openssl_util.h" | |
27 #include "crypto/scoped_openssl_types.h" | |
28 #include "net/android/keystore.h" | |
29 #include "net/ssl/scoped_openssl_types.h" | |
30 #include "net/ssl/ssl_platform_key_android.h" | |
31 #include "net/ssl/ssl_private_key.h" | |
32 #include "net/test/jni/AndroidKeyStoreTestUtil_jni.h" | |
33 #include "net/test/test_data_directory.h" | |
34 #include "testing/gtest/include/gtest/gtest.h" | |
35 | |
36 namespace net { | |
37 | |
38 namespace { | |
39 | |
40 typedef base::android::ScopedJavaLocalRef<jobject> ScopedJava; | |
41 | |
42 // Resize a string to |size| bytes of data, then return its data buffer | |
43 // address cast as an 'unsigned char*', as expected by OpenSSL functions. | |
44 // |str| the target string. | |
45 // |size| the number of bytes to write into the string. | |
46 // Return the string's new buffer in memory, as an 'unsigned char*' | |
47 // pointer. | |
48 unsigned char* OpenSSLWriteInto(std::string* str, size_t size) { | |
49 return reinterpret_cast<unsigned char*>(base::WriteInto(str, size + 1)); | |
50 } | |
51 | |
52 // Load a given private key file into an EVP_PKEY. | |
53 // |filename| is the key file path. | |
54 // Returns a new EVP_PKEY on success, NULL on failure. | |
55 EVP_PKEY* ImportPrivateKeyFile(const char* filename) { | |
56 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
57 | |
58 // Load file in memory. | |
59 base::FilePath certs_dir = GetTestCertsDirectory(); | |
60 base::FilePath file_path = certs_dir.AppendASCII(filename); | |
61 base::ScopedFILE handle(base::OpenFile(file_path, "rb")); | |
62 if (!handle.get()) { | |
63 LOG(ERROR) << "Could not open private key file: " << filename; | |
64 return NULL; | |
65 } | |
66 // Assume it is PEM_encoded. Load it as an EVP_PKEY. | |
67 EVP_PKEY* pkey = PEM_read_PrivateKey(handle.get(), NULL, NULL, NULL); | |
68 if (!pkey) { | |
69 LOG(ERROR) << "Could not load public key file: " << filename; | |
70 return NULL; | |
71 } | |
72 return pkey; | |
73 } | |
74 | |
75 // Convert a private key into its PKCS#8 encoded representation. | |
76 // |pkey| is the EVP_PKEY handle for the private key. | |
77 // |pkcs8| will receive the PKCS#8 bytes. | |
78 // Returns true on success, false otherwise. | |
79 bool GetPrivateKeyPkcs8Bytes(const crypto::ScopedEVP_PKEY& pkey, | |
80 std::string* pkcs8) { | |
81 uint8_t* der; | |
82 size_t der_len; | |
83 crypto::AutoCBB cbb; | |
84 if (!CBB_init(cbb.get(), 0) || | |
85 !EVP_marshal_private_key(cbb.get(), pkey.get()) || | |
86 !CBB_finish(cbb.get(), &der, &der_len)) { | |
87 return false; | |
88 } | |
89 pkcs8->assign(reinterpret_cast<const char*>(der), der_len); | |
90 OPENSSL_free(der); | |
91 return true; | |
92 } | |
93 | |
94 bool ImportPrivateKeyFileAsPkcs8(const char* filename, std::string* pkcs8) { | |
95 crypto::ScopedEVP_PKEY pkey(ImportPrivateKeyFile(filename)); | |
96 if (!pkey.get()) | |
97 return false; | |
98 return GetPrivateKeyPkcs8Bytes(pkey, pkcs8); | |
99 } | |
100 | |
101 // Same as ImportPrivateKey, but for public ones. | |
102 EVP_PKEY* ImportPublicKeyFile(const char* filename) { | |
103 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
104 | |
105 // Load file as PEM data. | |
106 base::FilePath certs_dir = GetTestCertsDirectory(); | |
107 base::FilePath file_path = certs_dir.AppendASCII(filename); | |
108 base::ScopedFILE handle(base::OpenFile(file_path, "rb")); | |
109 if (!handle.get()) { | |
110 LOG(ERROR) << "Could not open public key file: " << filename; | |
111 return NULL; | |
112 } | |
113 EVP_PKEY* pkey = PEM_read_PUBKEY(handle.get(), NULL, NULL, NULL); | |
114 if (!pkey) { | |
115 LOG(ERROR) << "Could not load public key file: " << filename; | |
116 return NULL; | |
117 } | |
118 return pkey; | |
119 } | |
120 | |
121 // Retrieve a JNI local ref from encoded PKCS#8 data. | |
122 ScopedJava GetPKCS8PrivateKeyJava(android::PrivateKeyType key_type, | |
123 const std::string& pkcs8_key) { | |
124 JNIEnv* env = base::android::AttachCurrentThread(); | |
125 base::android::ScopedJavaLocalRef<jbyteArray> bytes( | |
126 base::android::ToJavaByteArray( | |
127 env, reinterpret_cast<const uint8_t*>(pkcs8_key.data()), | |
128 pkcs8_key.size())); | |
129 | |
130 ScopedJava key(Java_AndroidKeyStoreTestUtil_createPrivateKeyFromPKCS8( | |
131 env, key_type, bytes)); | |
132 | |
133 return key; | |
134 } | |
135 | |
136 const char kTestRsaKeyFile[] = "android-test-key-rsa.pem"; | |
137 | |
138 // Retrieve a JNI local ref for our test RSA key. | |
139 ScopedJava GetRSATestKeyJava() { | |
140 std::string key; | |
141 if (!ImportPrivateKeyFileAsPkcs8(kTestRsaKeyFile, &key)) | |
142 return ScopedJava(); | |
143 return GetPKCS8PrivateKeyJava(android::PRIVATE_KEY_TYPE_RSA, key); | |
144 } | |
145 | |
146 const char kTestEcdsaKeyFile[] = "android-test-key-ecdsa.pem"; | |
147 const char kTestEcdsaPublicKeyFile[] = "android-test-key-ecdsa-public.pem"; | |
148 | |
149 // Retrieve a JNI local ref for our test ECDSA key. | |
150 ScopedJava GetECDSATestKeyJava() { | |
151 std::string key; | |
152 if (!ImportPrivateKeyFileAsPkcs8(kTestEcdsaKeyFile, &key)) | |
153 return ScopedJava(); | |
154 return GetPKCS8PrivateKeyJava(android::PRIVATE_KEY_TYPE_ECDSA, key); | |
155 } | |
156 | |
157 // Call this function to verify that one message signed with our | |
158 // test ECDSA private key is correct. Since ECDSA signing introduces | |
159 // random elements in the signature, it is not possible to compare | |
160 // signature bits directly. However, one can use the public key | |
161 // to do the check. | |
162 bool VerifyTestECDSASignature(const base::StringPiece& message, | |
163 const base::StringPiece& signature) { | |
164 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
165 | |
166 crypto::ScopedEVP_PKEY pkey(ImportPublicKeyFile(kTestEcdsaPublicKeyFile)); | |
167 if (!pkey.get()) | |
168 return false; | |
169 crypto::ScopedEC_KEY pub_key(EVP_PKEY_get1_EC_KEY(pkey.get())); | |
170 if (!pub_key.get()) { | |
171 LOG(ERROR) << "Could not get ECDSA public key"; | |
172 return false; | |
173 } | |
174 | |
175 const unsigned char* digest = | |
176 reinterpret_cast<const unsigned char*>(message.data()); | |
177 int digest_len = static_cast<int>(message.size()); | |
178 const unsigned char* sigbuf = | |
179 reinterpret_cast<const unsigned char*>(signature.data()); | |
180 int siglen = static_cast<int>(signature.size()); | |
181 | |
182 int ret = ECDSA_verify(0, digest, digest_len, sigbuf, siglen, pub_key.get()); | |
183 if (ret != 1) { | |
184 LOG(ERROR) << "ECDSA_verify() failed"; | |
185 return false; | |
186 } | |
187 return true; | |
188 } | |
189 | |
190 // Sign a message with OpenSSL, return the result as a string. | |
191 // |message| is the message to be signed. | |
192 // |openssl_key| is an OpenSSL EVP_PKEY to use. | |
193 // |result| receives the result. | |
194 // Returns true on success, false otherwise. | |
195 bool SignWithOpenSSL(int hash_nid, | |
196 const base::StringPiece& message, | |
197 EVP_PKEY* openssl_key, | |
198 std::string* result) { | |
199 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
200 const unsigned char* digest = | |
201 reinterpret_cast<const unsigned char*>(message.data()); | |
202 unsigned int digest_len = static_cast<unsigned int>(message.size()); | |
203 std::string signature; | |
204 size_t signature_size; | |
205 size_t max_signature_size; | |
206 int key_type = EVP_PKEY_id(openssl_key); | |
207 switch (key_type) { | |
208 case EVP_PKEY_RSA: { | |
209 crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(openssl_key)); | |
210 if (!rsa.get()) { | |
211 LOG(ERROR) << "Could not get RSA from EVP_PKEY"; | |
212 return false; | |
213 } | |
214 // With RSA, the signature will always be RSA_size() bytes. | |
215 max_signature_size = static_cast<size_t>(RSA_size(rsa.get())); | |
216 unsigned char* p = OpenSSLWriteInto(&signature, max_signature_size); | |
217 unsigned int p_len = 0; | |
218 int ret = RSA_sign(hash_nid, digest, digest_len, p, &p_len, rsa.get()); | |
219 if (ret != 1) { | |
220 LOG(ERROR) << "RSA_sign() failed"; | |
221 return false; | |
222 } | |
223 signature_size = static_cast<size_t>(p_len); | |
224 break; | |
225 } | |
226 default: | |
227 LOG(WARNING) << "Invalid OpenSSL key type: " << key_type; | |
228 return false; | |
229 } | |
230 | |
231 if (signature_size == 0) { | |
232 LOG(ERROR) << "Signature is empty!"; | |
233 return false; | |
234 } | |
235 if (signature_size > max_signature_size) { | |
236 LOG(ERROR) << "Signature size mismatch, actual " << signature_size | |
237 << ", expected <= " << max_signature_size; | |
238 return false; | |
239 } | |
240 signature.resize(signature_size); | |
241 result->swap(signature); | |
242 return true; | |
243 } | |
244 | |
245 // Check that a generated signature for a given message matches | |
246 // OpenSSL output byte-by-byte. | |
247 // |message| is the input message. | |
248 // |signature| is the generated signature for the message. | |
249 // |openssl_key| is a raw EVP_PKEY for the same private key than the | |
250 // one which was used to generate the signature. | |
251 // Returns true on success, false otherwise. | |
252 bool CompareSignatureWithOpenSSL(int hash_nid, | |
253 const base::StringPiece& message, | |
254 const base::StringPiece& signature, | |
255 EVP_PKEY* openssl_key) { | |
256 std::string openssl_signature; | |
257 if (!SignWithOpenSSL(hash_nid, message, openssl_key, &openssl_signature)) | |
258 return false; | |
259 | |
260 if (signature.size() != openssl_signature.size()) { | |
261 LOG(ERROR) << "Signature size mismatch, actual " << signature.size() | |
262 << ", expected " << openssl_signature.size(); | |
263 return false; | |
264 } | |
265 for (size_t n = 0; n < signature.size(); ++n) { | |
266 if (openssl_signature[n] != signature[n]) { | |
267 LOG(ERROR) << "Signature byte mismatch at index " << n << "actual " | |
268 << signature[n] << ", expected " << openssl_signature[n]; | |
269 LOG(ERROR) << "Actual signature : " | |
270 << base::HexEncode(signature.data(), signature.size()); | |
271 LOG(ERROR) << "Expected signature: " | |
272 << base::HexEncode(openssl_signature.data(), | |
273 openssl_signature.size()); | |
274 return false; | |
275 } | |
276 } | |
277 return true; | |
278 } | |
279 | |
280 void OnSignComplete(base::RunLoop* loop, | |
281 Error* out_error, | |
282 std::string* out_signature, | |
283 Error error, | |
284 const std::vector<uint8_t>& signature) { | |
285 *out_error = error; | |
286 out_signature->assign(signature.begin(), signature.end()); | |
287 loop->Quit(); | |
288 } | |
289 | |
290 void DoKeySigningWithWrapper(SSLPrivateKey* key, | |
291 SSLPrivateKey::Hash hash, | |
292 const base::StringPiece& message, | |
293 std::string* result) { | |
294 Error error; | |
295 base::RunLoop loop; | |
296 | |
297 key->SignDigest( | |
298 hash, message, | |
299 base::Bind(OnSignComplete, base::Unretained(&loop), | |
300 base::Unretained(&error), base::Unretained(result))); | |
301 loop.Run(); | |
302 | |
303 ASSERT_EQ(OK, error); | |
304 } | |
305 | |
306 static const struct { | |
307 const char* name; | |
308 int nid; | |
309 SSLPrivateKey::Hash hash; | |
310 } kHashes[] = { | |
311 {"MD5-SHA1", NID_md5_sha1, SSLPrivateKey::Hash::MD5_SHA1}, | |
312 {"SHA-1", NID_sha1, SSLPrivateKey::Hash::SHA1}, | |
313 {"SHA-256", NID_sha256, SSLPrivateKey::Hash::SHA256}, | |
314 {"SHA-384", NID_sha384, SSLPrivateKey::Hash::SHA384}, | |
315 {"SHA-512", NID_sha512, SSLPrivateKey::Hash::SHA512}, | |
316 }; | |
317 | |
318 } // namespace | |
319 | |
320 TEST(SSLPlatformKeyAndroid, RSA) { | |
321 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
322 | |
323 ScopedJava rsa_key = GetRSATestKeyJava(); | |
324 ASSERT_FALSE(rsa_key.is_null()); | |
325 | |
326 scoped_refptr<SSLPrivateKey> wrapper_key = WrapJavaPrivateKey(rsa_key.obj()); | |
327 ASSERT_TRUE(wrapper_key.get()); | |
328 | |
329 crypto::ScopedEVP_PKEY openssl_key(ImportPrivateKeyFile(kTestRsaKeyFile)); | |
330 ASSERT_TRUE(openssl_key.get()); | |
331 | |
332 // Check that the wrapper key returns the correct length and type. | |
333 EXPECT_EQ(SSLPrivateKey::Type::RSA, wrapper_key->GetType()); | |
334 EXPECT_EQ(static_cast<size_t>(EVP_PKEY_size(openssl_key.get())), | |
335 wrapper_key->GetMaxSignatureLengthInBytes()); | |
336 | |
337 // Test signing against each hash. | |
338 for (const auto& hash : kHashes) { | |
339 SCOPED_TRACE(hash.name); | |
340 | |
341 const EVP_MD* md = EVP_get_digestbynid(hash.nid); | |
342 ASSERT_TRUE(md); | |
343 std::string digest(EVP_MD_size(md), 'a'); | |
344 | |
345 std::string signature; | |
346 DoKeySigningWithWrapper(wrapper_key.get(), hash.hash, digest, &signature); | |
347 ASSERT_TRUE(CompareSignatureWithOpenSSL(hash.nid, digest, signature, | |
348 openssl_key.get())); | |
349 } | |
350 } | |
351 | |
352 TEST(SSLPlatformKeyAndroid, ECDSA) { | |
353 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
354 | |
355 ScopedJava ecdsa_key = GetECDSATestKeyJava(); | |
356 ASSERT_FALSE(ecdsa_key.is_null()); | |
357 | |
358 scoped_refptr<SSLPrivateKey> wrapper_key = | |
359 WrapJavaPrivateKey(ecdsa_key.obj()); | |
360 ASSERT_TRUE(wrapper_key.get()); | |
361 | |
362 crypto::ScopedEVP_PKEY openssl_key(ImportPrivateKeyFile(kTestEcdsaKeyFile)); | |
363 ASSERT_TRUE(openssl_key.get()); | |
364 | |
365 // Check that the wrapper key returns the correct length and type. | |
366 EXPECT_EQ(SSLPrivateKey::Type::ECDSA, wrapper_key->GetType()); | |
367 EXPECT_EQ(static_cast<size_t>(EVP_PKEY_size(openssl_key.get())), | |
368 wrapper_key->GetMaxSignatureLengthInBytes()); | |
369 | |
370 // Test signing against each hash. | |
371 for (const auto& hash : kHashes) { | |
372 // ECDSA does not sign MD5-SHA1. | |
373 if (hash.nid == NID_md5_sha1) | |
374 continue; | |
375 | |
376 SCOPED_TRACE(hash.name); | |
377 const EVP_MD* md = EVP_get_digestbynid(hash.nid); | |
378 ASSERT_TRUE(md); | |
379 std::string digest(EVP_MD_size(md), 'a'); | |
380 | |
381 std::string signature; | |
382 DoKeySigningWithWrapper(wrapper_key.get(), hash.hash, digest, &signature); | |
383 ASSERT_TRUE(VerifyTestECDSASignature(digest, signature)); | |
384 } | |
385 } | |
386 | |
387 } // namespace net | |
OLD | NEW |