[email protected] | ab782c9 | 2012-07-09 23:12:14 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 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 "crypto/ec_private_key.h" |
| 6 | |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 7 | #include <openssl/bio.h> |
| 8 | #include <openssl/bn.h> |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 9 | #include <openssl/bytestring.h> |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 10 | #include <openssl/ec.h> |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 11 | #include <openssl/ec_key.h> |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 12 | #include <openssl/evp.h> |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 13 | #include <openssl/mem.h> |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 14 | #include <openssl/pkcs12.h> |
| 15 | #include <openssl/x509.h> |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 16 | #include <stddef.h> |
| 17 | #include <stdint.h> |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 18 | |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 19 | #include <utility> |
| 20 | |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 21 | #include "base/logging.h" |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 22 | #include "crypto/openssl_util.h" |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 23 | |
| 24 | namespace crypto { |
| 25 | |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 26 | namespace { |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 27 | |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 28 | // Function pointer definition, for injecting the required key export function |
| 29 | // into ExportKeyWithBio, below. |bio| is a temporary memory BIO object, and |
| 30 | // |key| is a handle to the input key object. Return 1 on success, 0 otherwise. |
| 31 | // NOTE: Used with OpenSSL functions, which do not comply with the Chromium |
| 32 | // style guide, hence the unusual parameter placement / types. |
| 33 | typedef int (*ExportBioFunction)(BIO* bio, const void* key); |
| 34 | |
| 35 | // Helper to export |key| into |output| via the specified ExportBioFunction. |
| 36 | bool ExportKeyWithBio(const void* key, |
| 37 | ExportBioFunction export_fn, |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 38 | std::vector<uint8_t>* output) { |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 39 | if (!key) |
| 40 | return false; |
| 41 | |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 42 | bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem())); |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 43 | if (!bio) |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 44 | return false; |
| 45 | |
| 46 | if (!export_fn(bio.get(), key)) |
| 47 | return false; |
| 48 | |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 49 | char* data = nullptr; |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 50 | long len = BIO_get_mem_data(bio.get(), &data); |
| 51 | if (!data || len < 0) |
| 52 | return false; |
| 53 | |
| 54 | output->assign(data, data + len); |
| 55 | return true; |
| 56 | } |
| 57 | |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 58 | } // namespace |
| 59 | |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 60 | ECPrivateKey::~ECPrivateKey() {} |
[email protected] | ab782c9 | 2012-07-09 23:12:14 | [diff] [blame] | 61 | |
| 62 | // static |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 63 | std::unique_ptr<ECPrivateKey> ECPrivateKey::Create() { |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 64 | OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 65 | |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 66 | bssl::UniquePtr<EC_KEY> ec_key( |
| 67 | EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 68 | if (!ec_key || !EC_KEY_generate_key(ec_key.get())) |
| 69 | return nullptr; |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 70 | |
thakis | d1a1847 | 2016-04-08 22:30:41 | [diff] [blame] | 71 | std::unique_ptr<ECPrivateKey> result(new ECPrivateKey()); |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 72 | result->key_.reset(EVP_PKEY_new()); |
| 73 | if (!result->key_ || !EVP_PKEY_set1_EC_KEY(result->key_.get(), ec_key.get())) |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 74 | return nullptr; |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 75 | |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 76 | CHECK_EQ(EVP_PKEY_EC, EVP_PKEY_id(result->key_.get())); |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 77 | return result; |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 78 | } |
| 79 | |
| 80 | // static |
davidben | 212cdf6 | 2016-06-07 17:11:09 | [diff] [blame] | 81 | std::unique_ptr<ECPrivateKey> ECPrivateKey::CreateFromPrivateKeyInfo( |
| 82 | const std::vector<uint8_t>& input) { |
| 83 | OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 84 | |
| 85 | CBS cbs; |
| 86 | CBS_init(&cbs, input.data(), input.size()); |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 87 | bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_private_key(&cbs)); |
davidben | 212cdf6 | 2016-06-07 17:11:09 | [diff] [blame] | 88 | if (!pkey || CBS_len(&cbs) != 0 || EVP_PKEY_id(pkey.get()) != EVP_PKEY_EC) |
| 89 | return nullptr; |
| 90 | |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 91 | std::unique_ptr<ECPrivateKey> result(new ECPrivateKey()); |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 92 | result->key_ = std::move(pkey); |
davidben | 212cdf6 | 2016-06-07 17:11:09 | [diff] [blame] | 93 | return result; |
| 94 | } |
| 95 | |
| 96 | // static |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 97 | std::unique_ptr<ECPrivateKey> ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 98 | const std::string& password, |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 99 | const std::vector<uint8_t>& encrypted_private_key_info, |
| 100 | const std::vector<uint8_t>& subject_public_key_info) { |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 101 | // NOTE: The |subject_public_key_info| can be ignored here, it is only |
| 102 | // useful for the NSS implementation (which uses the public key's SHA1 |
| 103 | // as a lookup key when storing the private one in its store). |
| 104 | if (encrypted_private_key_info.empty()) |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 105 | return nullptr; |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 106 | |
| 107 | OpenSSLErrStackTracer err_tracer(FROM_HERE); |
[email protected] | dc06f75 | 2014-08-06 23:11:09 | [diff] [blame] | 108 | |
| 109 | const uint8_t* data = &encrypted_private_key_info[0]; |
| 110 | const uint8_t* ptr = data; |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 111 | bssl::UniquePtr<X509_SIG> p8_encrypted( |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 112 | d2i_X509_SIG(nullptr, &ptr, encrypted_private_key_info.size())); |
[email protected] | dc06f75 | 2014-08-06 23:11:09 | [diff] [blame] | 113 | if (!p8_encrypted || ptr != data + encrypted_private_key_info.size()) |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 114 | return nullptr; |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 115 | |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 116 | bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> p8_decrypted; |
[email protected] | dc06f75 | 2014-08-06 23:11:09 | [diff] [blame] | 117 | if (password.empty()) { |
| 118 | // Hack for reading keys generated by an older version of the OpenSSL |
| 119 | // code. OpenSSL used to use "\0\0" rather than the empty string because it |
| 120 | // would treat the password as an ASCII string to be converted to UCS-2 |
| 121 | // while NSS used a byte string. |
| 122 | p8_decrypted.reset(PKCS8_decrypt_pbe( |
| 123 | p8_encrypted.get(), reinterpret_cast<const uint8_t*>("\0\0"), 2)); |
[email protected] | cdddc0f9 | 2014-03-22 02:42:47 | [diff] [blame] | 124 | } |
[email protected] | dc06f75 | 2014-08-06 23:11:09 | [diff] [blame] | 125 | if (!p8_decrypted) { |
| 126 | p8_decrypted.reset(PKCS8_decrypt_pbe( |
| 127 | p8_encrypted.get(), |
| 128 | reinterpret_cast<const uint8_t*>(password.data()), |
| 129 | password.size())); |
| 130 | } |
| 131 | |
| 132 | if (!p8_decrypted) |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 133 | return nullptr; |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 134 | |
| 135 | // Create a new EVP_PKEY for it. |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 136 | std::unique_ptr<ECPrivateKey> result(new ECPrivateKey()); |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 137 | result->key_.reset(EVP_PKCS82PKEY(p8_decrypted.get())); |
| 138 | if (!result->key_ || EVP_PKEY_id(result->key_.get()) != EVP_PKEY_EC) |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 139 | return nullptr; |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 140 | |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 141 | return result; |
| 142 | } |
| 143 | |
| 144 | std::unique_ptr<ECPrivateKey> ECPrivateKey::Copy() const { |
| 145 | std::unique_ptr<ECPrivateKey> copy(new ECPrivateKey()); |
agl | 5a7cadf | 2016-07-13 16:52:53 | [diff] [blame] | 146 | if (key_) { |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 147 | EVP_PKEY_up_ref(key_.get()); |
| 148 | copy->key_.reset(key_.get()); |
agl | 5a7cadf | 2016-07-13 16:52:53 | [diff] [blame] | 149 | } |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 150 | return copy; |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 151 | } |
| 152 | |
davidben | 212cdf6 | 2016-06-07 17:11:09 | [diff] [blame] | 153 | bool ECPrivateKey::ExportPrivateKey(std::vector<uint8_t>* output) const { |
| 154 | OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 155 | uint8_t* der; |
| 156 | size_t der_len; |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 157 | bssl::ScopedCBB cbb; |
| 158 | if (!CBB_init(cbb.get(), 0) || |
| 159 | !EVP_marshal_private_key(cbb.get(), key_.get()) || |
davidben | 212cdf6 | 2016-06-07 17:11:09 | [diff] [blame] | 160 | !CBB_finish(cbb.get(), &der, &der_len)) { |
| 161 | return false; |
| 162 | } |
| 163 | output->assign(der, der + der_len); |
| 164 | OPENSSL_free(der); |
| 165 | return true; |
| 166 | } |
| 167 | |
| 168 | bool ECPrivateKey::ExportEncryptedPrivateKey( |
| 169 | const std::string& password, |
| 170 | int iterations, |
| 171 | std::vector<uint8_t>* output) const { |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 172 | OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 173 | // Convert into a PKCS#8 object. |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 174 | bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> pkcs8(EVP_PKEY2PKCS8(key_.get())); |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 175 | if (!pkcs8) |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 176 | return false; |
| 177 | |
| 178 | // Encrypt the object. |
| 179 | // NOTE: NSS uses SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC |
| 180 | // so use NID_pbe_WithSHA1And3_Key_TripleDES_CBC which should be the OpenSSL |
| 181 | // equivalent. |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 182 | bssl::UniquePtr<X509_SIG> encrypted( |
| 183 | PKCS8_encrypt_pbe(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr, |
| 184 | reinterpret_cast<const uint8_t*>(password.data()), |
| 185 | password.size(), nullptr, 0, iterations, pkcs8.get())); |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 186 | if (!encrypted) |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 187 | return false; |
| 188 | |
| 189 | // Write it into |*output| |
| 190 | return ExportKeyWithBio(encrypted.get(), |
| 191 | reinterpret_cast<ExportBioFunction>(i2d_PKCS8_bio), |
| 192 | output); |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 193 | } |
| 194 | |
davidben | 212cdf6 | 2016-06-07 17:11:09 | [diff] [blame] | 195 | bool ECPrivateKey::ExportPublicKey(std::vector<uint8_t>* output) const { |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 196 | OpenSSLErrStackTracer err_tracer(FROM_HERE); |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 197 | uint8_t *der; |
| 198 | size_t der_len; |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 199 | bssl::ScopedCBB cbb; |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 200 | if (!CBB_init(cbb.get(), 0) || |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 201 | !EVP_marshal_public_key(cbb.get(), key_.get()) || |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 202 | !CBB_finish(cbb.get(), &der, &der_len)) { |
[email protected] | ac30ed0e | 2014-06-24 04:12:34 | [diff] [blame] | 203 | return false; |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 204 | } |
| 205 | output->assign(der, der + der_len); |
| 206 | OPENSSL_free(der); |
[email protected] | ac30ed0e | 2014-06-24 04:12:34 | [diff] [blame] | 207 | return true; |
| 208 | } |
| 209 | |
davidben | 212cdf6 | 2016-06-07 17:11:09 | [diff] [blame] | 210 | bool ECPrivateKey::ExportRawPublicKey(std::string* output) const { |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 211 | OpenSSLErrStackTracer err_tracer(FROM_HERE); |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 212 | |
| 213 | // Export the x and y field elements as 32-byte, big-endian numbers. (This is |
| 214 | // the same as X9.62 uncompressed form without the leading 0x04 byte.) |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 215 | EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(key_.get()); |
| 216 | bssl::UniquePtr<BIGNUM> x(BN_new()); |
| 217 | bssl::UniquePtr<BIGNUM> y(BN_new()); |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 218 | uint8_t buf[64]; |
| 219 | if (!x || !y || |
| 220 | !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), |
| 221 | EC_KEY_get0_public_key(ec_key), |
| 222 | x.get(), y.get(), nullptr) || |
| 223 | !BN_bn2bin_padded(buf, 32, x.get()) || |
| 224 | !BN_bn2bin_padded(buf + 32, 32, y.get())) { |
| 225 | return false; |
| 226 | } |
| 227 | |
| 228 | output->assign(reinterpret_cast<const char*>(buf), sizeof(buf)); |
| 229 | return true; |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 230 | } |
| 231 | |
davidben | 74f6744 | 2016-10-01 01:45:22 | [diff] [blame^] | 232 | ECPrivateKey::ECPrivateKey() {} |
[email protected] | d4db0b6 | 2013-10-17 16:09:24 | [diff] [blame] | 233 | |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 234 | } // namespace crypto |