Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1153)

Side by Side Diff: crypto/ec_private_key_nss.cc

Issue 8413024: Add ECPrivateKey for Elliptic Curve keypair generation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix win build for real Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698