Chromium Code Reviews| 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 |