Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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 "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 #include <cryptohi.h> | |
| 13 #include <keyhi.h> | |
| 14 #include <pk11pub.h> | |
| 15 #include <secmod.h> | |
| 16 | |
| 17 #include "base/logging.h" | |
| 18 #include "base/memory/scoped_ptr.h" | |
| 19 #include "crypto/nss_util.h" | |
| 20 #include "crypto/nss_util_internal.h" | |
| 21 #include "crypto/scoped_nss_types.h" | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // Copied from rsa_private_key_nss.cc. | |
| 26 static bool ReadAttribute(SECKEYPrivateKey* key, | |
| 27 CK_ATTRIBUTE_TYPE type, | |
| 28 std::vector<uint8>* output) { | |
| 29 SECItem item; | |
| 30 SECStatus rv; | |
| 31 rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item); | |
| 32 if (rv != SECSuccess) { | |
| 33 NOTREACHED() << "error: " << PORT_GetError(); | |
| 34 return false; | |
| 35 } | |
| 36 | |
| 37 output->assign(item.data, item.data + item.len); | |
| 38 SECITEM_FreeItem(&item, PR_FALSE); | |
| 39 return true; | |
| 40 } | |
| 41 | |
| 42 // Like PK11_ImportEncryptedPrivateKeyInfo, but hardcoded for EC, and returns | |
|
wtc
2011/11/03 02:17:50
These two functions should be put in a file with a
mattm
2011/11/04 02:39:14
Done.
| |
| 43 // the SECKEYPrivateKey. | |
| 44 // TODO(mattm): make patch to add something like this upstream? | |
| 45 // See https://bugzilla.mozilla.org/show_bug.cgi?id=211546 | |
| 46 SECStatus ImportEncryptedPrivateKeyInfoAndReturnKey( | |
|
wtc
2011/11/03 02:17:50
Ideally this function should simply return SECKEYP
| |
| 47 PK11SlotInfo* slot, | |
| 48 SECKEYEncryptedPrivateKeyInfo* epki, | |
| 49 SECItem* password, | |
| 50 SECItem* nickname, | |
| 51 SECItem* public_value, | |
| 52 PRBool permanent, | |
| 53 PRBool sensitive, | |
| 54 SECKEYPrivateKey** private_key, | |
| 55 void* wincx) { | |
| 56 SECItem* crypto_param = NULL; | |
| 57 | |
| 58 CK_ATTRIBUTE_TYPE usage = CKA_SIGN; | |
| 59 | |
| 60 PK11SymKey* key = PK11_PBEKeyGen(slot, | |
| 61 &epki->algorithm, | |
| 62 password, | |
| 63 PR_FALSE, // faulty3DES | |
| 64 wincx); | |
| 65 if (key == NULL) { | |
| 66 NOTREACHED() << "PK11_PBEKeyGen error: " << PORT_GetError(); | |
| 67 return SECFailure; | |
| 68 } | |
| 69 | |
| 70 CK_MECHANISM_TYPE cryptoMechType = PK11_GetPBECryptoMechanism( | |
|
wtc
2011/11/03 02:17:50
Since you are following Google style, name this va
mattm
2011/11/04 02:39:14
Done.
| |
| 71 &epki->algorithm, &crypto_param, password); | |
| 72 if (cryptoMechType == CKM_INVALID_MECHANISM) { | |
| 73 NOTREACHED() << "PK11_GetPBECryptoMechanism error: " << PORT_GetError(); | |
| 74 PK11_FreeSymKey(key); | |
| 75 return SECFailure; | |
| 76 } | |
| 77 | |
| 78 cryptoMechType = PK11_GetPadMechanism(cryptoMechType); | |
| 79 | |
| 80 *private_key = PK11_UnwrapPrivKey(slot, key, cryptoMechType, crypto_param, | |
| 81 &epki->encryptedData, nickname, | |
| 82 public_value, permanent, sensitive, CKK_EC, | |
| 83 &usage, 1, wincx); | |
| 84 | |
| 85 if (crypto_param != NULL) | |
| 86 SECITEM_ZfreeItem(crypto_param, PR_TRUE); | |
| 87 | |
| 88 PK11_FreeSymKey(key); | |
| 89 | |
| 90 if (!*private_key) { | |
| 91 NOTREACHED() << "PK11_UnwrapPrivKey error: " << PORT_GetError(); | |
| 92 return SECFailure; | |
| 93 } | |
| 94 | |
| 95 return SECSuccess; | |
| 96 } | |
| 97 | |
| 98 // SECKEY_ConvertToPublicKey doesn't support EC. | |
| 99 // TODO(mattm): post patch for SECKEY_ConvertToPublicKey upstream? | |
| 100 SECKEYPublicKey * | |
| 101 ConvertToPublicKey(SECKEYPrivateKey *privk) | |
| 102 { | |
| 103 SECKEYPublicKey *pubk; | |
| 104 PLArenaPool *arena; | |
| 105 SECStatus rv; | |
| 106 | |
| 107 arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); | |
| 108 if (arena == NULL) { | |
| 109 return NULL; | |
| 110 } | |
| 111 pubk = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena, | |
| 112 sizeof (SECKEYPublicKey)); | |
| 113 if (pubk == NULL) { | |
| 114 PORT_FreeArena(arena,PR_FALSE); | |
| 115 return NULL; | |
| 116 } | |
| 117 pubk->keyType = privk->keyType; | |
| 118 pubk->pkcs11Slot = NULL; | |
| 119 pubk->pkcs11ID = CK_INVALID_HANDLE; | |
| 120 pubk->arena = arena; | |
| 121 | |
| 122 // We have to do a little dance of reading into a SECItem then copying to | |
| 123 // another SECItem allocated under the pubkey's arena because | |
| 124 // PK11_ReadAttribute isn't public and PK11_ReadRawAttribute doesn't take an | |
| 125 // arena arg. | |
| 126 SECItem item; | |
| 127 switch(privk->keyType) { | |
| 128 case ecKey: | |
| 129 // See nss/lib/pk11wrap/pk11obj.c#966 | |
| 130 rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privk, | |
| 131 CKA_NETSCAPE_DB, &item); | |
|
wtc
2011/11/03 02:17:50
The CKA_NETSCAPE_DB attribute is only used by NSS'
| |
| 132 if (rv != SECSuccess) { | |
| 133 NOTREACHED() << "error: " << PORT_GetError(); | |
| 134 break; | |
| 135 } | |
| 136 rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue, &item); | |
| 137 SECITEM_FreeItem(&item, PR_FALSE); | |
| 138 if (rv != SECSuccess) { | |
| 139 NOTREACHED() << "error: " << PORT_GetError(); | |
| 140 break; | |
| 141 } | |
| 142 | |
| 143 rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privk, | |
| 144 CKA_EC_PARAMS, &item); | |
| 145 if (rv != SECSuccess) { | |
| 146 NOTREACHED() << "error: " << PORT_GetError(); | |
| 147 break; | |
| 148 } | |
| 149 rv = SECITEM_CopyItem(arena, &pubk->u.ec.DEREncodedParams, &item); | |
| 150 SECITEM_FreeItem(&item, PR_FALSE); | |
| 151 if (rv != SECSuccess) { | |
| 152 NOTREACHED() << "error: " << PORT_GetError(); | |
| 153 break; | |
| 154 } | |
| 155 | |
| 156 // Seems to be okay to leave pubk->u.ec.size = 0. | |
|
wtc
2011/11/03 02:17:50
Based on code inspection, I believe you're right.
mattm
2011/11/04 02:39:14
Yes, that did set it to 256.
| |
| 157 return pubk; | |
| 158 default: | |
| 159 break; | |
| 160 } | |
| 161 | |
| 162 PORT_FreeArena (arena, PR_FALSE); | |
| 163 return NULL; | |
| 164 } | |
| 165 | |
| 166 } // namespace | |
| 167 | |
| 168 namespace crypto { | |
| 169 | |
| 170 ECPrivateKey::~ECPrivateKey() { | |
| 171 if (key_) | |
| 172 SECKEY_DestroyPrivateKey(key_); | |
| 173 if (public_key_) | |
| 174 SECKEY_DestroyPublicKey(public_key_); | |
| 175 } | |
| 176 | |
| 177 // static | |
| 178 ECPrivateKey* ECPrivateKey::Create() { | |
| 179 return CreateWithParams(PR_FALSE /* not permanent */, | |
| 180 PR_FALSE /* not sensitive */); | |
| 181 } | |
| 182 | |
| 183 // static | |
| 184 ECPrivateKey* ECPrivateKey::CreateSensitive() { | |
| 185 #if defined(USE_NSS) | |
| 186 return CreateWithParams(PR_TRUE /* permanent */, | |
| 187 PR_TRUE /* sensitive */); | |
| 188 #else | |
| 189 NOTIMPLEMENTED(); | |
|
wtc
2011/11/03 02:17:50
Please add a comment to explain why this is not im
mattm
2011/11/04 02:39:14
Done.
| |
| 190 return NULL; | |
| 191 #endif | |
| 192 } | |
| 193 | |
| 194 // static | |
| 195 ECPrivateKey* ECPrivateKey::CreateFromPrivateKeyInfo( | |
| 196 const std::vector<uint8>& input) { | |
| 197 return CreateFromPrivateKeyInfoWithParams(input, | |
| 198 PR_FALSE /* not permanent */, | |
| 199 PR_FALSE /* not sensitive */); | |
| 200 } | |
| 201 | |
| 202 // static | |
| 203 ECPrivateKey* ECPrivateKey::CreateSensitiveFromPrivateKeyInfo( | |
| 204 const std::vector<uint8>& input) { | |
| 205 #if defined(USE_NSS) | |
| 206 return CreateFromPrivateKeyInfoWithParams(input, | |
| 207 PR_TRUE /* permanent */, | |
| 208 PR_TRUE /* sensitive */); | |
| 209 #else | |
| 210 NOTIMPLEMENTED(); | |
| 211 return NULL; | |
| 212 #endif | |
| 213 } | |
| 214 | |
| 215 bool ECPrivateKey::ExportPrivateKey(std::vector<uint8>* output) { | |
| 216 // We export as an EncryptedPrivateKeyInfo bundle instead of a plain PKCS #8 | |
| 217 // PrivateKeyInfo because PK11_ImportDERPrivateKeyInfoAndReturnKey doesn't | |
| 218 // support EC keys. | |
| 219 // https://bugzilla.mozilla.org/show_bug.cgi?id=327773 | |
| 220 SECItem password = {siBuffer, NULL, 0}; | |
| 221 | |
| 222 SECKEYEncryptedPrivateKeyInfo* encrypted = PK11_ExportEncryptedPrivKeyInfo( | |
| 223 NULL, // Slot, optional. | |
| 224 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC, | |
| 225 &password, | |
| 226 key_, | |
| 227 1, // iterations. | |
| 228 NULL); // wincx. | |
| 229 | |
| 230 if (!encrypted) { | |
| 231 NOTREACHED() << "PK11_ExportEncryptedPrivKeyInfo error: " | |
| 232 << PORT_GetError(); | |
| 233 return false; | |
| 234 } | |
| 235 | |
| 236 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); | |
| 237 SECItem der_key = {siBuffer, NULL, 0}; | |
| 238 if (!SEC_ASN1EncodeItem( | |
| 239 arena.get(), | |
| 240 &der_key, | |
| 241 encrypted, | |
| 242 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate))) { | |
| 243 NOTREACHED() << "SEC_ASN1EncodeItem error: " << PORT_GetError(); | |
| 244 SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted, PR_TRUE); | |
| 245 return false; | |
| 246 } | |
| 247 SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted, PR_TRUE); | |
|
wtc
2011/11/03 02:17:50
Nit: save the return value of SEC_ASN1EncodeItem i
mattm
2011/11/04 02:39:14
Done.
| |
| 248 | |
| 249 | |
| 250 output->clear(); | |
| 251 // Do a really lame and simple encoding of the necessary information into the | |
| 252 // output buffer. Assumes publicValue is 255 bytes or less. | |
| 253 // XXX: do we care about cross build compatibility (like loading keys | |
|
wtc
2011/11/03 02:17:50
Replace XXX with TODO.
mattm
2011/11/04 02:39:14
Removed the comment, since I hope there shouldn't
| |
| 254 // generated in NSS with an OpenSSL build, or vice versa)? The key | |
| 255 // should be loadable by OpenSSL (it's a standard PKCS #8 | |
| 256 // EncryptedPrivateKeyInfo), and I don't think OpenSSL needs the publicValue, | |
| 257 // but what about the reverse? | |
| 258 DCHECK_LT(public_key_->u.ec.publicValue.len, 256U); | |
| 259 output->push_back(public_key_->u.ec.publicValue.len); | |
| 260 output->insert(output->end(), | |
| 261 public_key_->u.ec.publicValue.data, | |
| 262 public_key_->u.ec.publicValue.data + | |
| 263 public_key_->u.ec.publicValue.len); | |
| 264 output->insert(output->end(), | |
| 265 der_key.data, | |
| 266 der_key.data + | |
| 267 der_key.len); | |
| 268 | |
| 269 return true; | |
| 270 } | |
| 271 | |
| 272 bool ECPrivateKey::ExportPublicKey(std::vector<uint8>* output) { | |
| 273 ScopedSECItem der_pubkey(SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_)); | |
| 274 if (!der_pubkey.get()) { | |
| 275 NOTREACHED(); | |
| 276 return false; | |
| 277 } | |
| 278 | |
| 279 for (size_t i = 0; i < der_pubkey->len; ++i) | |
| 280 output->push_back(der_pubkey->data[i]); | |
|
wtc
2011/11/03 02:17:50
Can we use the assign or insert method to avoid by
mattm
2011/11/04 02:39:14
done (I'll do rsa_private_key_nss.cc and rsa_priva
| |
| 281 | |
| 282 return true; | |
| 283 } | |
| 284 | |
| 285 bool ECPrivateKey::ExportValue(std::vector<uint8>* output) { | |
| 286 return ReadAttribute(key_, CKA_VALUE, output); | |
| 287 } | |
| 288 | |
| 289 bool ECPrivateKey::ExportECParams(std::vector<uint8>* output) { | |
| 290 return ReadAttribute(key_, CKA_EC_PARAMS, output); | |
| 291 } | |
| 292 | |
| 293 ECPrivateKey::ECPrivateKey() : key_(NULL), public_key_(NULL) { | |
| 294 EnsureNSSInit(); | |
|
wtc
2011/11/03 02:17:50
This EnsureNSSInit() call is redundant.
mattm
2011/11/04 02:39:14
Done.
| |
| 295 } | |
| 296 | |
| 297 // static | |
| 298 ECPrivateKey* ECPrivateKey::CreateWithParams(bool permanent, | |
| 299 bool sensitive) { | |
| 300 EnsureNSSInit(); | |
| 301 | |
| 302 scoped_ptr<ECPrivateKey> result(new ECPrivateKey); | |
| 303 | |
| 304 ScopedPK11Slot slot(GetPrivateNSSKeySlot()); | |
| 305 if (!slot.get()) | |
| 306 return NULL; | |
| 307 | |
| 308 SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); | |
| 309 if (!oid_data) { | |
| 310 NOTREACHED(); | |
| 311 return NULL; | |
| 312 } | |
| 313 | |
| 314 SECKEYECParams ec_parameters = {siDEROID, NULL, 0}; | |
| 315 | |
| 316 if (!SECITEM_AllocItem(NULL, &ec_parameters, (2 + oid_data->oid.len))) { | |
| 317 return NULL; | |
| 318 } | |
| 319 | |
| 320 // SECKEYECParams is a SECItem containing the DER encoded ASN.1 ECParameters | |
| 321 // value. For a named curve, that is just the OBJECT IDENTIFIER of the curve. | |
| 322 ec_parameters.data[0] = SEC_ASN1_OBJECT_ID; | |
| 323 ec_parameters.data[1] = oid_data->oid.len; | |
| 324 memcpy(ec_parameters.data + 2, oid_data->oid.data, oid_data->oid.len); | |
| 325 | |
| 326 result->key_ = PK11_GenerateKeyPair(slot.get(), | |
| 327 CKM_EC_KEY_PAIR_GEN, | |
| 328 &ec_parameters, | |
| 329 &result->public_key_, | |
| 330 permanent, | |
| 331 sensitive, | |
| 332 NULL); | |
| 333 SECITEM_FreeItem(&ec_parameters, PR_FALSE); | |
| 334 if (!result->key_) { | |
| 335 NOTREACHED() << "PK11_GenerateKeyPair error: " << PORT_GetError(); | |
| 336 return NULL; | |
| 337 } | |
| 338 | |
| 339 return result.release(); | |
| 340 } | |
| 341 | |
| 342 // static | |
| 343 ECPrivateKey* ECPrivateKey::CreateFromPrivateKeyInfoWithParams( | |
| 344 const std::vector<uint8>& input, bool permanent, bool sensitive) { | |
| 345 EnsureNSSInit(); | |
| 346 | |
| 347 scoped_ptr<ECPrivateKey> result(new ECPrivateKey); | |
| 348 | |
| 349 ScopedPK11Slot slot(GetPrivateNSSKeySlot()); | |
| 350 if (!slot.get()) | |
| 351 return NULL; | |
| 352 | |
| 353 SECItem public_value = {siBuffer}; | |
|
wtc
2011/11/03 02:17:50
Nit: initialize this to {siBuffer, NULL, 0} for co
mattm
2011/11/04 02:39:14
Done.
| |
| 354 SECItem der_key = {siBuffer, NULL, 0}; | |
| 355 SECKEYEncryptedPrivateKeyInfo epki; | |
| 356 memset(&epki, 0, sizeof(epki)); | |
| 357 | |
| 358 if (input.size() < 2U) | |
| 359 return NULL; | |
| 360 unsigned char* buf = const_cast<unsigned char*>(&input[0]); | |
|
wtc
2011/11/03 02:17:50
Nit: I prefer moving the const_cast to lines 366 a
mattm
2011/11/04 02:39:14
done-ish (code is a bit different now)
| |
| 361 | |
| 362 public_value.len = *buf++; | |
| 363 if (public_value.len >= input.size()) | |
|
wtc
2011/11/03 02:17:50
I think this should be
if (1 + public_value.len
| |
| 364 return NULL; | |
| 365 | |
| 366 public_value.data = buf; | |
| 367 buf += public_value.len; | |
| 368 | |
| 369 der_key.data = buf; | |
| 370 der_key.len = &input.back() - buf + 1; | |
|
wtc
2011/11/03 02:17:50
input.size() - 1 - public_value.len is easier to u
| |
| 371 | |
| 372 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); | |
| 373 | |
| 374 SECStatus rv = SEC_ASN1DecodeItem( | |
| 375 arena.get(), | |
| 376 &epki, | |
| 377 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate), | |
| 378 &der_key); | |
| 379 if (rv != SECSuccess) { | |
| 380 NOTREACHED() << "SEC_ASN1DecodeItem error: " << PORT_GetError(); | |
| 381 return NULL; | |
| 382 } | |
| 383 | |
| 384 SECItem password = {siBuffer, NULL, 0}; | |
| 385 | |
| 386 rv = ImportEncryptedPrivateKeyInfoAndReturnKey( | |
| 387 slot.get(), | |
| 388 &epki, | |
| 389 &password, | |
| 390 NULL, // nickname | |
| 391 &public_value, | |
| 392 permanent, | |
| 393 sensitive, | |
| 394 &result->key_, | |
| 395 NULL); // wincx | |
| 396 if (rv != SECSuccess) { | |
| 397 NOTREACHED() << "ImportEncryptedPrivateKeyInfoAndReturnKey error: " | |
| 398 << PORT_GetError(); | |
| 399 return NULL; | |
| 400 } | |
| 401 | |
| 402 result->public_key_ = ConvertToPublicKey(result->key_); | |
|
wtc
2011/11/03 02:17:50
IMPORTANT: this ConvertToPublicKey call is not nec
mattm
2011/11/04 02:39:14
Done.
| |
| 403 if (!result->public_key_) { | |
| 404 NOTREACHED() << "SECKEY_ConvertToPublicKey error: " << PORT_GetError(); | |
| 405 return NULL; | |
| 406 } | |
| 407 | |
| 408 return result.release(); | |
| 409 } | |
| 410 | |
| 411 } // namespace crypto | |
| OLD | NEW |