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 |