ec_private_key_nss.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright (c) 2012 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
7extern "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>
17
18#include "base/lazy_instance.h"
19#include "base/logging.h"
20#include "base/memory/scoped_ptr.h"
21#include "crypto/nss_util.h"
22#include "crypto/nss_util_internal.h"
23#include "crypto/scoped_nss_types.h"
24#include "crypto/third_party/nss/chromium-nss.h"
25
26namespace {
27
28PK11SlotInfo* GetTempKeySlot() {
29  return PK11_GetInternalSlot();
30}
31
32class EllipticCurveSupportChecker {
33 public:
34  EllipticCurveSupportChecker() {
35    // NOTE: we can do this check here only because we use the NSS internal
36    // slot.  If we support other slots in the future, checking whether they
37    // support ECDSA may block NSS, and the value may also change as devices are
38    // inserted/removed, so we would need to re-check on every use.
39    crypto::EnsureNSSInit();
40    crypto::ScopedPK11Slot slot(GetTempKeySlot());
41    supported_ = PK11_DoesMechanism(slot.get(), CKM_EC_KEY_PAIR_GEN) &&
42        PK11_DoesMechanism(slot.get(), CKM_ECDSA);
43  }
44
45  bool Supported() {
46    return supported_;
47  }
48
49 private:
50  bool supported_;
51};
52
53static base::LazyInstance<EllipticCurveSupportChecker>::Leaky
54    g_elliptic_curve_supported = LAZY_INSTANCE_INITIALIZER;
55
56// Copied from rsa_private_key_nss.cc.
57static bool ReadAttribute(SECKEYPrivateKey* key,
58                          CK_ATTRIBUTE_TYPE type,
59                          std::vector<uint8>* output) {
60  SECItem item;
61  SECStatus rv;
62  rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item);
63  if (rv != SECSuccess) {
64    DLOG(ERROR) << "PK11_ReadRawAttribute: " << PORT_GetError();
65    return false;
66  }
67
68  output->assign(item.data, item.data + item.len);
69  SECITEM_FreeItem(&item, PR_FALSE);
70  return true;
71}
72
73}  // namespace
74
75namespace crypto {
76
77ECPrivateKey::~ECPrivateKey() {
78  if (key_)
79    SECKEY_DestroyPrivateKey(key_);
80  if (public_key_)
81    SECKEY_DestroyPublicKey(public_key_);
82}
83
84// static
85bool ECPrivateKey::IsSupported() {
86  return g_elliptic_curve_supported.Get().Supported();
87}
88
89// static
90ECPrivateKey* ECPrivateKey::Create() {
91  EnsureNSSInit();
92
93  ScopedPK11Slot slot(GetTempKeySlot());
94  return CreateWithParams(slot.get(),
95                          false /* not permanent */,
96                          false /* not sensitive */);
97}
98
99#if defined(USE_NSS)
100// static
101ECPrivateKey* ECPrivateKey::CreateSensitive(PK11SlotInfo* slot) {
102  return CreateWithParams(
103      slot, true /* permanent */, true /* sensitive */);
104}
105#endif
106
107// static
108ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
109    const std::string& password,
110    const std::vector<uint8>& encrypted_private_key_info,
111    const std::vector<uint8>& subject_public_key_info) {
112  EnsureNSSInit();
113
114  ScopedPK11Slot slot(GetTempKeySlot());
115  return CreateFromEncryptedPrivateKeyInfoWithParams(
116      slot.get(),
117      password,
118      encrypted_private_key_info,
119      subject_public_key_info,
120      false /* not permanent */,
121      false /* not sensitive */);
122}
123
124#if defined(USE_NSS)
125// static
126ECPrivateKey* ECPrivateKey::CreateSensitiveFromEncryptedPrivateKeyInfo(
127    PK11SlotInfo* slot,
128    const std::string& password,
129    const std::vector<uint8>& encrypted_private_key_info,
130    const std::vector<uint8>& subject_public_key_info) {
131  return CreateFromEncryptedPrivateKeyInfoWithParams(
132      slot,
133      password,
134      encrypted_private_key_info,
135      subject_public_key_info,
136      true /* permanent */,
137      true /* sensitive */);
138}
139#endif
140
141// static
142bool ECPrivateKey::ImportFromEncryptedPrivateKeyInfo(
143    PK11SlotInfo* slot,
144    const std::string& password,
145    const uint8* encrypted_private_key_info,
146    size_t encrypted_private_key_info_len,
147    CERTSubjectPublicKeyInfo* decoded_spki,
148    bool permanent,
149    bool sensitive,
150    SECKEYPrivateKey** key,
151    SECKEYPublicKey** public_key) {
152  if (!slot)
153    return false;
154
155  *public_key = SECKEY_ExtractPublicKey(decoded_spki);
156
157  if (!*public_key) {
158    DLOG(ERROR) << "SECKEY_ExtractPublicKey: " << PORT_GetError();
159    return false;
160  }
161
162  SECItem encoded_epki = {
163    siBuffer,
164    const_cast<unsigned char*>(encrypted_private_key_info),
165    static_cast<unsigned>(encrypted_private_key_info_len)
166  };
167  SECKEYEncryptedPrivateKeyInfo epki;
168  memset(&epki, 0, sizeof(epki));
169
170  ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
171
172  SECStatus rv = SEC_QuickDERDecodeItem(
173      arena.get(),
174      &epki,
175      SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate),
176      &encoded_epki);
177  if (rv != SECSuccess) {
178    DLOG(ERROR) << "SEC_QuickDERDecodeItem: " << PORT_GetError();
179    SECKEY_DestroyPublicKey(*public_key);
180    *public_key = NULL;
181    return false;
182  }
183
184  SECItem password_item = {
185    siBuffer,
186    reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())),
187    static_cast<unsigned>(password.size())
188  };
189
190  rv = ImportEncryptedECPrivateKeyInfoAndReturnKey(
191      slot,
192      &epki,
193      &password_item,
194      NULL,  // nickname
195      &(*public_key)->u.ec.publicValue,
196      permanent,
197      sensitive,
198      key,
199      NULL);  // wincx
200  if (rv != SECSuccess) {
201    DLOG(ERROR) << "ImportEncryptedECPrivateKeyInfoAndReturnKey: "
202                << PORT_GetError();
203    SECKEY_DestroyPublicKey(*public_key);
204    *public_key = NULL;
205    return false;
206  }
207
208  return true;
209}
210
211bool ECPrivateKey::ExportEncryptedPrivateKey(
212    const std::string& password,
213    int iterations,
214    std::vector<uint8>* output) {
215  // We export as an EncryptedPrivateKeyInfo bundle instead of a plain PKCS #8
216  // PrivateKeyInfo because PK11_ImportDERPrivateKeyInfoAndReturnKey doesn't
217  // support EC keys.
218  // https://bugzilla.mozilla.org/show_bug.cgi?id=327773
219  SECItem password_item = {
220    siBuffer,
221    reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())),
222    static_cast<unsigned>(password.size())
223  };
224
225  SECKEYEncryptedPrivateKeyInfo* encrypted = PK11_ExportEncryptedPrivKeyInfo(
226      NULL, // Slot, optional.
227      SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC,
228      &password_item,
229      key_,
230      iterations,
231      NULL); // wincx.
232
233  if (!encrypted) {
234    DLOG(ERROR) << "PK11_ExportEncryptedPrivKeyInfo: " << PORT_GetError();
235    return false;
236  }
237
238  ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
239  SECItem der_key = {siBuffer, NULL, 0};
240  SECItem* encoded_item = SEC_ASN1EncodeItem(
241      arena.get(),
242      &der_key,
243      encrypted,
244      SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate));
245  SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted, PR_TRUE);
246  if (!encoded_item) {
247    DLOG(ERROR) << "SEC_ASN1EncodeItem: " << PORT_GetError();
248    return false;
249  }
250
251  output->assign(der_key.data, der_key.data + der_key.len);
252
253  return true;
254}
255
256bool ECPrivateKey::ExportPublicKey(std::vector<uint8>* output) {
257  ScopedSECItem der_pubkey(
258      SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_));
259  if (!der_pubkey.get()) {
260    return false;
261  }
262
263  output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len);
264  return true;
265}
266
267bool ECPrivateKey::ExportValue(std::vector<uint8>* output) {
268  return ReadAttribute(key_, CKA_VALUE, output);
269}
270
271bool ECPrivateKey::ExportECParams(std::vector<uint8>* output) {
272  return ReadAttribute(key_, CKA_EC_PARAMS, output);
273}
274
275ECPrivateKey::ECPrivateKey() : key_(NULL), public_key_(NULL) {}
276
277// static
278ECPrivateKey* ECPrivateKey::CreateWithParams(PK11SlotInfo* slot,
279                                             bool permanent,
280                                             bool sensitive) {
281  if (!slot)
282    return NULL;
283
284  scoped_ptr<ECPrivateKey> result(new ECPrivateKey);
285
286  SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
287  if (!oid_data) {
288    DLOG(ERROR) << "SECOID_FindOIDByTag: " << PORT_GetError();
289    return NULL;
290  }
291
292  // SECKEYECParams is a SECItem containing the DER encoded ASN.1 ECParameters
293  // value.  For a named curve, that is just the OBJECT IDENTIFIER of the curve.
294  // In addition to the oid data, the encoding requires one byte for the ASN.1
295  // tag and one byte for the length (assuming the length is <= 127).
296  DCHECK_LE(oid_data->oid.len, 127U);
297  std::vector<unsigned char> parameters_buf(2 + oid_data->oid.len);
298  SECKEYECParams ec_parameters = {
299    siDEROID, &parameters_buf[0],
300    static_cast<unsigned>(parameters_buf.size())
301  };
302
303  ec_parameters.data[0] = SEC_ASN1_OBJECT_ID;
304  ec_parameters.data[1] = oid_data->oid.len;
305  memcpy(ec_parameters.data + 2, oid_data->oid.data, oid_data->oid.len);
306
307  result->key_ = PK11_GenerateKeyPair(slot,
308                                      CKM_EC_KEY_PAIR_GEN,
309                                      &ec_parameters,
310                                      &result->public_key_,
311                                      permanent,
312                                      sensitive,
313                                      NULL);
314  if (!result->key_) {
315    DLOG(ERROR) << "PK11_GenerateKeyPair: " << PORT_GetError();
316    return NULL;
317  }
318
319  return result.release();
320}
321
322// static
323ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfoWithParams(
324    PK11SlotInfo* slot,
325    const std::string& password,
326    const std::vector<uint8>& encrypted_private_key_info,
327    const std::vector<uint8>& subject_public_key_info,
328    bool permanent,
329    bool sensitive) {
330  scoped_ptr<ECPrivateKey> result(new ECPrivateKey);
331
332  SECItem encoded_spki = {
333    siBuffer,
334    const_cast<unsigned char*>(&subject_public_key_info[0]),
335    static_cast<unsigned>(subject_public_key_info.size())
336  };
337  CERTSubjectPublicKeyInfo* decoded_spki = SECKEY_DecodeDERSubjectPublicKeyInfo(
338      &encoded_spki);
339  if (!decoded_spki) {
340    DLOG(ERROR) << "SECKEY_DecodeDERSubjectPublicKeyInfo: " << PORT_GetError();
341    return NULL;
342  }
343
344  bool success = ImportFromEncryptedPrivateKeyInfo(
345      slot,
346      password,
347      &encrypted_private_key_info[0],
348      encrypted_private_key_info.size(),
349      decoded_spki,
350      permanent,
351      sensitive,
352      &result->key_,
353      &result->public_key_);
354
355  SECKEY_DestroySubjectPublicKeyInfo(decoded_spki);
356
357  if (success)
358    return result.release();
359
360  return NULL;
361}
362
363}  // namespace crypto
364