OLD | NEW |
(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/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) { |
| 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) |
| 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) { |
| 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) |
| 168 return false; |
| 169 crypto::ScopedEC_KEY pub_key(EVP_PKEY_get1_EC_KEY(pkey.get())); |
| 170 if (!pub_key) { |
| 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) { |
| 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); |
| 327 ASSERT_TRUE(wrapper_key); |
| 328 |
| 329 crypto::ScopedEVP_PKEY openssl_key(ImportPrivateKeyFile(kTestRsaKeyFile)); |
| 330 ASSERT_TRUE(openssl_key); |
| 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 = WrapJavaPrivateKey(ecdsa_key); |
| 359 ASSERT_TRUE(wrapper_key); |
| 360 |
| 361 crypto::ScopedEVP_PKEY openssl_key(ImportPrivateKeyFile(kTestEcdsaKeyFile)); |
| 362 ASSERT_TRUE(openssl_key); |
| 363 |
| 364 // Check that the wrapper key returns the correct length and type. |
| 365 EXPECT_EQ(SSLPrivateKey::Type::ECDSA, wrapper_key->GetType()); |
| 366 EXPECT_EQ(static_cast<size_t>(EVP_PKEY_size(openssl_key.get())), |
| 367 wrapper_key->GetMaxSignatureLengthInBytes()); |
| 368 |
| 369 // Test signing against each hash. |
| 370 for (const auto& hash : kHashes) { |
| 371 // ECDSA does not sign MD5-SHA1. |
| 372 if (hash.nid == NID_md5_sha1) |
| 373 continue; |
| 374 |
| 375 SCOPED_TRACE(hash.name); |
| 376 const EVP_MD* md = EVP_get_digestbynid(hash.nid); |
| 377 ASSERT_TRUE(md); |
| 378 std::string digest(EVP_MD_size(md), 'a'); |
| 379 |
| 380 std::string signature; |
| 381 DoKeySigningWithWrapper(wrapper_key.get(), hash.hash, digest, &signature); |
| 382 ASSERT_TRUE(VerifyTestECDSASignature(digest, signature)); |
| 383 } |
| 384 } |
| 385 |
| 386 } // namespace net |
OLD | NEW |