[email protected] | 3fcbd4b | 2012-06-05 01:54:46 | [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 | |
| 7 | extern "C" { |
| 8 | // Work around NSS missing SEC_BEGIN_PROTOS in secmodt.h. This must come before |
| 9 | // other NSS headers. |
| 10 | #include <secmodt.h> |
| 11 | } |
| 12 | |
| 13 | #include <cryptohi.h> |
| 14 | #include <keyhi.h> |
| 15 | #include <pk11pub.h> |
| 16 | #include <secmod.h> |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 17 | #include <stddef.h> |
| 18 | #include <stdint.h> |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 19 | |
thakis | d1a1847 | 2016-04-08 22:30:41 | [diff] [blame] | 20 | #include <memory> |
| 21 | |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 22 | #include "base/logging.h" |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 23 | #include "crypto/nss_util.h" |
| 24 | #include "crypto/nss_util_internal.h" |
| 25 | #include "crypto/scoped_nss_types.h" |
| 26 | #include "crypto/third_party/nss/chromium-nss.h" |
| 27 | |
| 28 | namespace { |
| 29 | |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 30 | static bool AppendAttribute(SECKEYPrivateKey* key, |
| 31 | CK_ATTRIBUTE_TYPE type, |
| 32 | std::vector<uint8_t>* output) { |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 33 | SECItem item; |
| 34 | SECStatus rv; |
| 35 | rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item); |
| 36 | if (rv != SECSuccess) { |
| 37 | DLOG(ERROR) << "PK11_ReadRawAttribute: " << PORT_GetError(); |
| 38 | return false; |
| 39 | } |
| 40 | |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 41 | output->insert(output->end(), item.data, item.data + item.len); |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 42 | SECITEM_FreeItem(&item, PR_FALSE); |
| 43 | return true; |
| 44 | } |
| 45 | |
| 46 | } // namespace |
| 47 | |
| 48 | namespace crypto { |
| 49 | |
| 50 | ECPrivateKey::~ECPrivateKey() { |
| 51 | if (key_) |
| 52 | SECKEY_DestroyPrivateKey(key_); |
| 53 | if (public_key_) |
| 54 | SECKEY_DestroyPublicKey(public_key_); |
| 55 | } |
| 56 | |
| 57 | // static |
| 58 | ECPrivateKey* ECPrivateKey::Create() { |
[email protected] | 6e7da44d | 2013-11-13 03:29:22 | [diff] [blame] | 59 | EnsureNSSInit(); |
| 60 | |
davidben | 24bb5a4 | 2015-10-15 22:13:22 | [diff] [blame] | 61 | ScopedPK11Slot slot(PK11_GetInternalSlot()); |
davidben | f2331cd | 2015-02-25 21:17:29 | [diff] [blame] | 62 | if (!slot) |
| 63 | return nullptr; |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 64 | |
thakis | d1a1847 | 2016-04-08 22:30:41 | [diff] [blame] | 65 | std::unique_ptr<ECPrivateKey> result(new ECPrivateKey); |
davidben | f2331cd | 2015-02-25 21:17:29 | [diff] [blame] | 66 | |
| 67 | SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); |
| 68 | if (!oid_data) { |
| 69 | DLOG(ERROR) << "SECOID_FindOIDByTag: " << PORT_GetError(); |
| 70 | return nullptr; |
| 71 | } |
| 72 | |
| 73 | // SECKEYECParams is a SECItem containing the DER encoded ASN.1 ECParameters |
| 74 | // value. For a named curve, that is just the OBJECT IDENTIFIER of the curve. |
| 75 | // In addition to the oid data, the encoding requires one byte for the ASN.1 |
| 76 | // tag and one byte for the length (assuming the length is <= 127). |
| 77 | CHECK_LE(oid_data->oid.len, 127U); |
| 78 | std::vector<unsigned char> parameters_buf(2 + oid_data->oid.len); |
| 79 | SECKEYECParams ec_parameters = { |
| 80 | siDEROID, ¶meters_buf[0], |
| 81 | static_cast<unsigned>(parameters_buf.size()) |
| 82 | }; |
| 83 | |
| 84 | ec_parameters.data[0] = SEC_ASN1_OBJECT_ID; |
| 85 | ec_parameters.data[1] = static_cast<unsigned char>(oid_data->oid.len); |
| 86 | memcpy(ec_parameters.data + 2, oid_data->oid.data, oid_data->oid.len); |
| 87 | |
| 88 | result->key_ = PK11_GenerateKeyPair(slot.get(), |
| 89 | CKM_EC_KEY_PAIR_GEN, |
| 90 | &ec_parameters, |
| 91 | &result->public_key_, |
| 92 | PR_FALSE /* not permanent */, |
| 93 | PR_FALSE /* not sensitive */, |
| 94 | NULL); |
| 95 | if (!result->key_) { |
| 96 | DLOG(ERROR) << "PK11_GenerateKeyPair: " << PORT_GetError(); |
| 97 | return nullptr; |
| 98 | } |
| 99 | CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(result->public_key_)); |
| 100 | |
| 101 | return result.release(); |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 102 | } |
| 103 | |
| 104 | // static |
| 105 | ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( |
| 106 | const std::string& password, |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 107 | const std::vector<uint8_t>& encrypted_private_key_info, |
| 108 | const std::vector<uint8_t>& subject_public_key_info) { |
[email protected] | 6e7da44d | 2013-11-13 03:29:22 | [diff] [blame] | 109 | EnsureNSSInit(); |
| 110 | |
davidben | 24bb5a4 | 2015-10-15 22:13:22 | [diff] [blame] | 111 | ScopedPK11Slot slot(PK11_GetInternalSlot()); |
davidben | f2331cd | 2015-02-25 21:17:29 | [diff] [blame] | 112 | if (!slot) |
| 113 | return nullptr; |
| 114 | |
thakis | d1a1847 | 2016-04-08 22:30:41 | [diff] [blame] | 115 | std::unique_ptr<ECPrivateKey> result(new ECPrivateKey); |
davidben | f2331cd | 2015-02-25 21:17:29 | [diff] [blame] | 116 | |
| 117 | SECItem encoded_spki = { |
| 118 | siBuffer, |
| 119 | const_cast<unsigned char*>(&subject_public_key_info[0]), |
| 120 | static_cast<unsigned>(subject_public_key_info.size()) |
| 121 | }; |
| 122 | CERTSubjectPublicKeyInfo* decoded_spki = SECKEY_DecodeDERSubjectPublicKeyInfo( |
| 123 | &encoded_spki); |
| 124 | if (!decoded_spki) { |
| 125 | DLOG(ERROR) << "SECKEY_DecodeDERSubjectPublicKeyInfo: " << PORT_GetError(); |
| 126 | return nullptr; |
| 127 | } |
| 128 | |
| 129 | bool success = ImportFromEncryptedPrivateKeyInfo( |
[email protected] | 6e7da44d | 2013-11-13 03:29:22 | [diff] [blame] | 130 | slot.get(), |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 131 | password, |
davidben | f2331cd | 2015-02-25 21:17:29 | [diff] [blame] | 132 | &encrypted_private_key_info[0], |
| 133 | encrypted_private_key_info.size(), |
| 134 | decoded_spki, |
[email protected] | 6e7da44d | 2013-11-13 03:29:22 | [diff] [blame] | 135 | false /* not permanent */, |
davidben | f2331cd | 2015-02-25 21:17:29 | [diff] [blame] | 136 | false /* not sensitive */, |
| 137 | &result->key_, |
| 138 | &result->public_key_); |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 139 | |
davidben | f2331cd | 2015-02-25 21:17:29 | [diff] [blame] | 140 | SECKEY_DestroySubjectPublicKeyInfo(decoded_spki); |
| 141 | |
| 142 | if (success) { |
| 143 | CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(result->public_key_)); |
| 144 | return result.release(); |
| 145 | } |
| 146 | |
| 147 | return nullptr; |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 148 | } |
| 149 | |
[email protected] | 550cee9 | 2011-12-06 04:21:09 | [diff] [blame] | 150 | // static |
| 151 | bool ECPrivateKey::ImportFromEncryptedPrivateKeyInfo( |
[email protected] | 6e7da44d | 2013-11-13 03:29:22 | [diff] [blame] | 152 | PK11SlotInfo* slot, |
[email protected] | 550cee9 | 2011-12-06 04:21:09 | [diff] [blame] | 153 | const std::string& password, |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 154 | const uint8_t* encrypted_private_key_info, |
[email protected] | 550cee9 | 2011-12-06 04:21:09 | [diff] [blame] | 155 | size_t encrypted_private_key_info_len, |
| 156 | CERTSubjectPublicKeyInfo* decoded_spki, |
| 157 | bool permanent, |
| 158 | bool sensitive, |
| 159 | SECKEYPrivateKey** key, |
| 160 | SECKEYPublicKey** public_key) { |
[email protected] | 6e7da44d | 2013-11-13 03:29:22 | [diff] [blame] | 161 | if (!slot) |
[email protected] | 550cee9 | 2011-12-06 04:21:09 | [diff] [blame] | 162 | return false; |
| 163 | |
| 164 | *public_key = SECKEY_ExtractPublicKey(decoded_spki); |
| 165 | |
| 166 | if (!*public_key) { |
| 167 | DLOG(ERROR) << "SECKEY_ExtractPublicKey: " << PORT_GetError(); |
| 168 | return false; |
| 169 | } |
| 170 | |
[email protected] | ac30ed0e | 2014-06-24 04:12:34 | [diff] [blame] | 171 | if (SECKEY_GetPublicKeyType(*public_key) != ecKey) { |
| 172 | DLOG(ERROR) << "The public key is not an EC key"; |
| 173 | SECKEY_DestroyPublicKey(*public_key); |
| 174 | *public_key = NULL; |
| 175 | return false; |
| 176 | } |
| 177 | |
[email protected] | 550cee9 | 2011-12-06 04:21:09 | [diff] [blame] | 178 | SECItem encoded_epki = { |
| 179 | siBuffer, |
| 180 | const_cast<unsigned char*>(encrypted_private_key_info), |
[email protected] | 3fcbd4b | 2012-06-05 01:54:46 | [diff] [blame] | 181 | static_cast<unsigned>(encrypted_private_key_info_len) |
[email protected] | 550cee9 | 2011-12-06 04:21:09 | [diff] [blame] | 182 | }; |
| 183 | SECKEYEncryptedPrivateKeyInfo epki; |
| 184 | memset(&epki, 0, sizeof(epki)); |
| 185 | |
| 186 | ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
| 187 | |
| 188 | SECStatus rv = SEC_QuickDERDecodeItem( |
| 189 | arena.get(), |
| 190 | &epki, |
| 191 | SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate), |
| 192 | &encoded_epki); |
| 193 | if (rv != SECSuccess) { |
| 194 | DLOG(ERROR) << "SEC_QuickDERDecodeItem: " << PORT_GetError(); |
| 195 | SECKEY_DestroyPublicKey(*public_key); |
| 196 | *public_key = NULL; |
| 197 | return false; |
| 198 | } |
| 199 | |
| 200 | SECItem password_item = { |
| 201 | siBuffer, |
| 202 | reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())), |
[email protected] | 3fcbd4b | 2012-06-05 01:54:46 | [diff] [blame] | 203 | static_cast<unsigned>(password.size()) |
[email protected] | 550cee9 | 2011-12-06 04:21:09 | [diff] [blame] | 204 | }; |
| 205 | |
| 206 | rv = ImportEncryptedECPrivateKeyInfoAndReturnKey( |
[email protected] | 6e7da44d | 2013-11-13 03:29:22 | [diff] [blame] | 207 | slot, |
[email protected] | 550cee9 | 2011-12-06 04:21:09 | [diff] [blame] | 208 | &epki, |
| 209 | &password_item, |
| 210 | NULL, // nickname |
| 211 | &(*public_key)->u.ec.publicValue, |
| 212 | permanent, |
| 213 | sensitive, |
| 214 | key, |
| 215 | NULL); // wincx |
| 216 | if (rv != SECSuccess) { |
| 217 | DLOG(ERROR) << "ImportEncryptedECPrivateKeyInfoAndReturnKey: " |
| 218 | << PORT_GetError(); |
| 219 | SECKEY_DestroyPublicKey(*public_key); |
| 220 | *public_key = NULL; |
| 221 | return false; |
| 222 | } |
| 223 | |
| 224 | return true; |
| 225 | } |
| 226 | |
[email protected] | ac30ed0e | 2014-06-24 04:12:34 | [diff] [blame] | 227 | ECPrivateKey* ECPrivateKey::Copy() const { |
thakis | d1a1847 | 2016-04-08 22:30:41 | [diff] [blame] | 228 | std::unique_ptr<ECPrivateKey> copy(new ECPrivateKey); |
[email protected] | ac30ed0e | 2014-06-24 04:12:34 | [diff] [blame] | 229 | if (key_) { |
| 230 | copy->key_ = SECKEY_CopyPrivateKey(key_); |
| 231 | if (!copy->key_) |
| 232 | return NULL; |
| 233 | } |
| 234 | if (public_key_) { |
| 235 | copy->public_key_ = SECKEY_CopyPublicKey(public_key_); |
| 236 | if (!copy->public_key_) |
| 237 | return NULL; |
| 238 | } |
| 239 | return copy.release(); |
| 240 | } |
| 241 | |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 242 | bool ECPrivateKey::ExportEncryptedPrivateKey(const std::string& password, |
| 243 | int iterations, |
| 244 | std::vector<uint8_t>* output) { |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 245 | // We export as an EncryptedPrivateKeyInfo bundle instead of a plain PKCS #8 |
| 246 | // PrivateKeyInfo because PK11_ImportDERPrivateKeyInfoAndReturnKey doesn't |
| 247 | // support EC keys. |
| 248 | // https://bugzilla.mozilla.org/show_bug.cgi?id=327773 |
| 249 | SECItem password_item = { |
| 250 | siBuffer, |
| 251 | reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())), |
[email protected] | 3fcbd4b | 2012-06-05 01:54:46 | [diff] [blame] | 252 | static_cast<unsigned>(password.size()) |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 253 | }; |
| 254 | |
| 255 | SECKEYEncryptedPrivateKeyInfo* encrypted = PK11_ExportEncryptedPrivKeyInfo( |
[email protected] | c9c251d | 2014-07-22 00:09:25 | [diff] [blame] | 256 | NULL, // Slot, optional. |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 257 | SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC, |
| 258 | &password_item, |
| 259 | key_, |
| 260 | iterations, |
[email protected] | c9c251d | 2014-07-22 00:09:25 | [diff] [blame] | 261 | NULL); // wincx. |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 262 | |
| 263 | if (!encrypted) { |
| 264 | DLOG(ERROR) << "PK11_ExportEncryptedPrivKeyInfo: " << PORT_GetError(); |
| 265 | return false; |
| 266 | } |
| 267 | |
| 268 | ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
| 269 | SECItem der_key = {siBuffer, NULL, 0}; |
| 270 | SECItem* encoded_item = SEC_ASN1EncodeItem( |
| 271 | arena.get(), |
| 272 | &der_key, |
| 273 | encrypted, |
| 274 | SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate)); |
| 275 | SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted, PR_TRUE); |
| 276 | if (!encoded_item) { |
| 277 | DLOG(ERROR) << "SEC_ASN1EncodeItem: " << PORT_GetError(); |
| 278 | return false; |
| 279 | } |
| 280 | |
| 281 | output->assign(der_key.data, der_key.data + der_key.len); |
| 282 | |
| 283 | return true; |
| 284 | } |
| 285 | |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 286 | bool ECPrivateKey::ExportPublicKey(std::vector<uint8_t>* output) { |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 287 | ScopedSECItem der_pubkey( |
| 288 | SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_)); |
| 289 | if (!der_pubkey.get()) { |
| 290 | return false; |
| 291 | } |
| 292 | |
| 293 | output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len); |
| 294 | return true; |
| 295 | } |
| 296 | |
[email protected] | ac30ed0e | 2014-06-24 04:12:34 | [diff] [blame] | 297 | bool ECPrivateKey::ExportRawPublicKey(std::string* output) { |
| 298 | // public_key_->u.ec.publicValue is an ANSI X9.62 public key which, for |
| 299 | // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field |
| 300 | // elements as 32-byte, big-endian numbers. |
| 301 | static const unsigned int kExpectedKeyLength = 65; |
| 302 | |
| 303 | CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(public_key_)); |
| 304 | const unsigned char* const data = public_key_->u.ec.publicValue.data; |
| 305 | const unsigned int len = public_key_->u.ec.publicValue.len; |
| 306 | if (len != kExpectedKeyLength || data[0] != 0x04) |
| 307 | return false; |
| 308 | |
| 309 | output->assign(reinterpret_cast<const char*>(data + 1), |
| 310 | kExpectedKeyLength - 1); |
| 311 | return true; |
| 312 | } |
| 313 | |
davidben | 7dad2a3 | 2016-03-01 23:47:47 | [diff] [blame] | 314 | bool ECPrivateKey::ExportValueForTesting(std::vector<uint8_t>* output) { |
| 315 | // This serialization format is purely for testing equality, so just |
| 316 | // concatenate the raw private key (always 32 bytes for P-256) with the |
| 317 | // parameters. |
| 318 | output->clear(); |
| 319 | return AppendAttribute(key_, CKA_VALUE, output) && |
| 320 | output->size() == 32 && |
| 321 | AppendAttribute(key_, CKA_EC_PARAMS, output); |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 322 | } |
| 323 | |
| 324 | ECPrivateKey::ECPrivateKey() : key_(NULL), public_key_(NULL) {} |
| 325 | |
[email protected] | eaa6048 | 2011-11-09 05:08:51 | [diff] [blame] | 326 | } // namespace crypto |