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.
5 #include "crypto/ec_private_key.h"
8 // Work around NSS missing SEC_BEGIN_PROTOS in secmodt.h. This must come before
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"
28 PK11SlotInfo
* GetKeySlot() {
29 return crypto::GetPublicNSSKeySlot();
32 class EllipticCurveSupportChecker
{
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(GetKeySlot());
41 supported_
= PK11_DoesMechanism(slot
.get(), CKM_EC_KEY_PAIR_GEN
) &&
42 PK11_DoesMechanism(slot
.get(), CKM_ECDSA
);
53 static base::LazyInstance
<EllipticCurveSupportChecker
>::Leaky
54 g_elliptic_curve_supported
= LAZY_INSTANCE_INITIALIZER
;
56 // Copied from rsa_private_key_nss.cc.
57 static bool ReadAttribute(SECKEYPrivateKey
* key
,
58 CK_ATTRIBUTE_TYPE type
,
59 std::vector
<uint8
>* output
) {
62 rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, key
, type
, &item
);
63 if (rv
!= SECSuccess
) {
64 DLOG(ERROR
) << "PK11_ReadRawAttribute: " << PORT_GetError();
68 output
->assign(item
.data
, item
.data
+ item
.len
);
69 SECITEM_FreeItem(&item
, PR_FALSE
);
77 ECPrivateKey::~ECPrivateKey() {
79 SECKEY_DestroyPrivateKey(key_
);
81 SECKEY_DestroyPublicKey(public_key_
);
85 bool ECPrivateKey::IsSupported() {
86 return g_elliptic_curve_supported
.Get().Supported();
90 ECPrivateKey
* ECPrivateKey::Create() {
91 return CreateWithParams(PR_FALSE
/* not permanent */,
92 PR_FALSE
/* not sensitive */);
96 ECPrivateKey
* ECPrivateKey::CreateSensitive() {
98 return CreateWithParams(PR_TRUE
/* permanent */,
99 PR_TRUE
/* sensitive */);
101 // If USE_NSS is not defined, we initialize NSS with no databases, so we can't
102 // create permanent keys.
109 ECPrivateKey
* ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
110 const std::string
& password
,
111 const std::vector
<uint8
>& encrypted_private_key_info
,
112 const std::vector
<uint8
>& subject_public_key_info
) {
113 return CreateFromEncryptedPrivateKeyInfoWithParams(
115 encrypted_private_key_info
,
116 subject_public_key_info
,
117 PR_FALSE
/* not permanent */,
118 PR_FALSE
/* not sensitive */);
122 ECPrivateKey
* ECPrivateKey::CreateSensitiveFromEncryptedPrivateKeyInfo(
123 const std::string
& password
,
124 const std::vector
<uint8
>& encrypted_private_key_info
,
125 const std::vector
<uint8
>& subject_public_key_info
) {
127 return CreateFromEncryptedPrivateKeyInfoWithParams(
129 encrypted_private_key_info
,
130 subject_public_key_info
,
131 PR_TRUE
/* permanent */,
132 PR_TRUE
/* sensitive */);
134 // If USE_NSS is not defined, we initialize NSS with no databases, so we can't
135 // create permanent keys.
142 bool ECPrivateKey::ImportFromEncryptedPrivateKeyInfo(
143 const std::string
& password
,
144 const uint8
* encrypted_private_key_info
,
145 size_t encrypted_private_key_info_len
,
146 CERTSubjectPublicKeyInfo
* decoded_spki
,
149 SECKEYPrivateKey
** key
,
150 SECKEYPublicKey
** public_key
) {
151 ScopedPK11Slot
slot(GetKeySlot());
155 *public_key
= SECKEY_ExtractPublicKey(decoded_spki
);
158 DLOG(ERROR
) << "SECKEY_ExtractPublicKey: " << PORT_GetError();
162 SECItem encoded_epki
= {
164 const_cast<unsigned char*>(encrypted_private_key_info
),
165 static_cast<unsigned>(encrypted_private_key_info_len
)
167 SECKEYEncryptedPrivateKeyInfo epki
;
168 memset(&epki
, 0, sizeof(epki
));
170 ScopedPLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
172 SECStatus rv
= SEC_QuickDERDecodeItem(
175 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate
),
177 if (rv
!= SECSuccess
) {
178 DLOG(ERROR
) << "SEC_QuickDERDecodeItem: " << PORT_GetError();
179 SECKEY_DestroyPublicKey(*public_key
);
184 SECItem password_item
= {
186 reinterpret_cast<unsigned char*>(const_cast<char*>(password
.data())),
187 static_cast<unsigned>(password
.size())
190 rv
= ImportEncryptedECPrivateKeyInfoAndReturnKey(
195 &(*public_key
)->u
.ec
.publicValue
,
200 if (rv
!= SECSuccess
) {
201 DLOG(ERROR
) << "ImportEncryptedECPrivateKeyInfoAndReturnKey: "
203 SECKEY_DestroyPublicKey(*public_key
);
211 bool ECPrivateKey::ExportEncryptedPrivateKey(
212 const std::string
& password
,
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
218 // https://bugzilla.mozilla.org/show_bug.cgi?id=327773
219 SECItem password_item
= {
221 reinterpret_cast<unsigned char*>(const_cast<char*>(password
.data())),
222 static_cast<unsigned>(password
.size())
225 SECKEYEncryptedPrivateKeyInfo
* encrypted
= PK11_ExportEncryptedPrivKeyInfo(
226 NULL
, // Slot, optional.
227 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC
,
234 DLOG(ERROR
) << "PK11_ExportEncryptedPrivKeyInfo: " << PORT_GetError();
238 ScopedPLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
239 SECItem der_key
= {siBuffer
, NULL
, 0};
240 SECItem
* encoded_item
= SEC_ASN1EncodeItem(
244 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate
));
245 SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted
, PR_TRUE
);
247 DLOG(ERROR
) << "SEC_ASN1EncodeItem: " << PORT_GetError();
251 output
->assign(der_key
.data
, der_key
.data
+ der_key
.len
);
256 bool ECPrivateKey::ExportPublicKey(std::vector
<uint8
>* output
) {
257 ScopedSECItem
der_pubkey(
258 SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_
));
259 if (!der_pubkey
.get()) {
263 output
->assign(der_pubkey
->data
, der_pubkey
->data
+ der_pubkey
->len
);
267 bool ECPrivateKey::ExportValue(std::vector
<uint8
>* output
) {
268 return ReadAttribute(key_
, CKA_VALUE
, output
);
271 bool ECPrivateKey::ExportECParams(std::vector
<uint8
>* output
) {
272 return ReadAttribute(key_
, CKA_EC_PARAMS
, output
);
275 ECPrivateKey::ECPrivateKey() : key_(NULL
), public_key_(NULL
) {}
278 ECPrivateKey
* ECPrivateKey::CreateWithParams(bool permanent
,
282 scoped_ptr
<ECPrivateKey
> result(new ECPrivateKey
);
284 ScopedPK11Slot
slot(GetKeySlot());
288 SECOidData
* oid_data
= SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1
);
290 DLOG(ERROR
) << "SECOID_FindOIDByTag: " << PORT_GetError();
294 // SECKEYECParams is a SECItem containing the DER encoded ASN.1 ECParameters
295 // value. For a named curve, that is just the OBJECT IDENTIFIER of the curve.
296 // In addition to the oid data, the encoding requires one byte for the ASN.1
297 // tag and one byte for the length (assuming the length is <= 127).
298 DCHECK_LE(oid_data
->oid
.len
, 127U);
299 std::vector
<unsigned char> parameters_buf(2 + oid_data
->oid
.len
);
300 SECKEYECParams ec_parameters
= {
301 siDEROID
, ¶meters_buf
[0],
302 static_cast<unsigned>(parameters_buf
.size())
305 ec_parameters
.data
[0] = SEC_ASN1_OBJECT_ID
;
306 ec_parameters
.data
[1] = oid_data
->oid
.len
;
307 memcpy(ec_parameters
.data
+ 2, oid_data
->oid
.data
, oid_data
->oid
.len
);
309 result
->key_
= PK11_GenerateKeyPair(slot
.get(),
312 &result
->public_key_
,
317 DLOG(ERROR
) << "PK11_GenerateKeyPair: " << PORT_GetError();
321 return result
.release();
325 ECPrivateKey
* ECPrivateKey::CreateFromEncryptedPrivateKeyInfoWithParams(
326 const std::string
& password
,
327 const std::vector
<uint8
>& encrypted_private_key_info
,
328 const std::vector
<uint8
>& subject_public_key_info
,
333 scoped_ptr
<ECPrivateKey
> result(new ECPrivateKey
);
335 SECItem encoded_spki
= {
337 const_cast<unsigned char*>(&subject_public_key_info
[0]),
338 static_cast<unsigned>(subject_public_key_info
.size())
340 CERTSubjectPublicKeyInfo
* decoded_spki
= SECKEY_DecodeDERSubjectPublicKeyInfo(
343 DLOG(ERROR
) << "SECKEY_DecodeDERSubjectPublicKeyInfo: " << PORT_GetError();
347 bool success
= ECPrivateKey::ImportFromEncryptedPrivateKeyInfo(
349 &encrypted_private_key_info
[0],
350 encrypted_private_key_info
.size(),
355 &result
->public_key_
);
357 SECKEY_DestroySubjectPublicKeyInfo(decoded_spki
);
360 return result
.release();
365 } // namespace crypto