Refactor WebsiteSettings to operate on a SecurityInfo
[chromium-blink-merge.git] / crypto / ec_private_key_nss.cc
blob5092010c939b5f734f7c608e1e2ec3c0a35e17c3
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"
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>
13 #include <cryptohi.h>
14 #include <keyhi.h>
15 #include <pk11pub.h>
16 #include <secmod.h>
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"
26 namespace {
28 PK11SlotInfo* GetTempKeySlot() {
29 return PK11_GetInternalSlot();
32 class 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);
45 bool Supported() {
46 return supported_;
49 private:
50 bool supported_;
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) {
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;
68 output->assign(item.data, item.data + item.len);
69 SECITEM_FreeItem(&item, PR_FALSE);
70 return true;
73 } // namespace
75 namespace crypto {
77 ECPrivateKey::~ECPrivateKey() {
78 if (key_)
79 SECKEY_DestroyPrivateKey(key_);
80 if (public_key_)
81 SECKEY_DestroyPublicKey(public_key_);
84 // static
85 bool ECPrivateKey::IsSupported() {
86 return g_elliptic_curve_supported.Get().Supported();
89 // static
90 ECPrivateKey* ECPrivateKey::Create() {
91 EnsureNSSInit();
93 ScopedPK11Slot slot(GetTempKeySlot());
94 if (!slot)
95 return nullptr;
97 scoped_ptr<ECPrivateKey> result(new ECPrivateKey);
99 SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
100 if (!oid_data) {
101 DLOG(ERROR) << "SECOID_FindOIDByTag: " << PORT_GetError();
102 return nullptr;
105 // SECKEYECParams is a SECItem containing the DER encoded ASN.1 ECParameters
106 // value. For a named curve, that is just the OBJECT IDENTIFIER of the curve.
107 // In addition to the oid data, the encoding requires one byte for the ASN.1
108 // tag and one byte for the length (assuming the length is <= 127).
109 CHECK_LE(oid_data->oid.len, 127U);
110 std::vector<unsigned char> parameters_buf(2 + oid_data->oid.len);
111 SECKEYECParams ec_parameters = {
112 siDEROID, &parameters_buf[0],
113 static_cast<unsigned>(parameters_buf.size())
116 ec_parameters.data[0] = SEC_ASN1_OBJECT_ID;
117 ec_parameters.data[1] = static_cast<unsigned char>(oid_data->oid.len);
118 memcpy(ec_parameters.data + 2, oid_data->oid.data, oid_data->oid.len);
120 result->key_ = PK11_GenerateKeyPair(slot.get(),
121 CKM_EC_KEY_PAIR_GEN,
122 &ec_parameters,
123 &result->public_key_,
124 PR_FALSE /* not permanent */,
125 PR_FALSE /* not sensitive */,
126 NULL);
127 if (!result->key_) {
128 DLOG(ERROR) << "PK11_GenerateKeyPair: " << PORT_GetError();
129 return nullptr;
131 CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(result->public_key_));
133 return result.release();
136 // static
137 ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
138 const std::string& password,
139 const std::vector<uint8>& encrypted_private_key_info,
140 const std::vector<uint8>& subject_public_key_info) {
141 EnsureNSSInit();
143 ScopedPK11Slot slot(GetTempKeySlot());
144 if (!slot)
145 return nullptr;
147 scoped_ptr<ECPrivateKey> result(new ECPrivateKey);
149 SECItem encoded_spki = {
150 siBuffer,
151 const_cast<unsigned char*>(&subject_public_key_info[0]),
152 static_cast<unsigned>(subject_public_key_info.size())
154 CERTSubjectPublicKeyInfo* decoded_spki = SECKEY_DecodeDERSubjectPublicKeyInfo(
155 &encoded_spki);
156 if (!decoded_spki) {
157 DLOG(ERROR) << "SECKEY_DecodeDERSubjectPublicKeyInfo: " << PORT_GetError();
158 return nullptr;
161 bool success = ImportFromEncryptedPrivateKeyInfo(
162 slot.get(),
163 password,
164 &encrypted_private_key_info[0],
165 encrypted_private_key_info.size(),
166 decoded_spki,
167 false /* not permanent */,
168 false /* not sensitive */,
169 &result->key_,
170 &result->public_key_);
172 SECKEY_DestroySubjectPublicKeyInfo(decoded_spki);
174 if (success) {
175 CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(result->public_key_));
176 return result.release();
179 return nullptr;
182 // static
183 bool ECPrivateKey::ImportFromEncryptedPrivateKeyInfo(
184 PK11SlotInfo* slot,
185 const std::string& password,
186 const uint8* encrypted_private_key_info,
187 size_t encrypted_private_key_info_len,
188 CERTSubjectPublicKeyInfo* decoded_spki,
189 bool permanent,
190 bool sensitive,
191 SECKEYPrivateKey** key,
192 SECKEYPublicKey** public_key) {
193 if (!slot)
194 return false;
196 *public_key = SECKEY_ExtractPublicKey(decoded_spki);
198 if (!*public_key) {
199 DLOG(ERROR) << "SECKEY_ExtractPublicKey: " << PORT_GetError();
200 return false;
203 if (SECKEY_GetPublicKeyType(*public_key) != ecKey) {
204 DLOG(ERROR) << "The public key is not an EC key";
205 SECKEY_DestroyPublicKey(*public_key);
206 *public_key = NULL;
207 return false;
210 SECItem encoded_epki = {
211 siBuffer,
212 const_cast<unsigned char*>(encrypted_private_key_info),
213 static_cast<unsigned>(encrypted_private_key_info_len)
215 SECKEYEncryptedPrivateKeyInfo epki;
216 memset(&epki, 0, sizeof(epki));
218 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
220 SECStatus rv = SEC_QuickDERDecodeItem(
221 arena.get(),
222 &epki,
223 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate),
224 &encoded_epki);
225 if (rv != SECSuccess) {
226 DLOG(ERROR) << "SEC_QuickDERDecodeItem: " << PORT_GetError();
227 SECKEY_DestroyPublicKey(*public_key);
228 *public_key = NULL;
229 return false;
232 SECItem password_item = {
233 siBuffer,
234 reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())),
235 static_cast<unsigned>(password.size())
238 rv = ImportEncryptedECPrivateKeyInfoAndReturnKey(
239 slot,
240 &epki,
241 &password_item,
242 NULL, // nickname
243 &(*public_key)->u.ec.publicValue,
244 permanent,
245 sensitive,
246 key,
247 NULL); // wincx
248 if (rv != SECSuccess) {
249 DLOG(ERROR) << "ImportEncryptedECPrivateKeyInfoAndReturnKey: "
250 << PORT_GetError();
251 SECKEY_DestroyPublicKey(*public_key);
252 *public_key = NULL;
253 return false;
256 return true;
259 ECPrivateKey* ECPrivateKey::Copy() const {
260 scoped_ptr<ECPrivateKey> copy(new ECPrivateKey);
261 if (key_) {
262 copy->key_ = SECKEY_CopyPrivateKey(key_);
263 if (!copy->key_)
264 return NULL;
266 if (public_key_) {
267 copy->public_key_ = SECKEY_CopyPublicKey(public_key_);
268 if (!copy->public_key_)
269 return NULL;
271 return copy.release();
274 bool ECPrivateKey::ExportEncryptedPrivateKey(
275 const std::string& password,
276 int iterations,
277 std::vector<uint8>* output) {
278 // We export as an EncryptedPrivateKeyInfo bundle instead of a plain PKCS #8
279 // PrivateKeyInfo because PK11_ImportDERPrivateKeyInfoAndReturnKey doesn't
280 // support EC keys.
281 // https://bugzilla.mozilla.org/show_bug.cgi?id=327773
282 SECItem password_item = {
283 siBuffer,
284 reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())),
285 static_cast<unsigned>(password.size())
288 SECKEYEncryptedPrivateKeyInfo* encrypted = PK11_ExportEncryptedPrivKeyInfo(
289 NULL, // Slot, optional.
290 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC,
291 &password_item,
292 key_,
293 iterations,
294 NULL); // wincx.
296 if (!encrypted) {
297 DLOG(ERROR) << "PK11_ExportEncryptedPrivKeyInfo: " << PORT_GetError();
298 return false;
301 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
302 SECItem der_key = {siBuffer, NULL, 0};
303 SECItem* encoded_item = SEC_ASN1EncodeItem(
304 arena.get(),
305 &der_key,
306 encrypted,
307 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate));
308 SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted, PR_TRUE);
309 if (!encoded_item) {
310 DLOG(ERROR) << "SEC_ASN1EncodeItem: " << PORT_GetError();
311 return false;
314 output->assign(der_key.data, der_key.data + der_key.len);
316 return true;
319 bool ECPrivateKey::ExportPublicKey(std::vector<uint8>* output) {
320 ScopedSECItem der_pubkey(
321 SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_));
322 if (!der_pubkey.get()) {
323 return false;
326 output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len);
327 return true;
330 bool ECPrivateKey::ExportRawPublicKey(std::string* output) {
331 // public_key_->u.ec.publicValue is an ANSI X9.62 public key which, for
332 // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field
333 // elements as 32-byte, big-endian numbers.
334 static const unsigned int kExpectedKeyLength = 65;
336 CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(public_key_));
337 const unsigned char* const data = public_key_->u.ec.publicValue.data;
338 const unsigned int len = public_key_->u.ec.publicValue.len;
339 if (len != kExpectedKeyLength || data[0] != 0x04)
340 return false;
342 output->assign(reinterpret_cast<const char*>(data + 1),
343 kExpectedKeyLength - 1);
344 return true;
347 bool ECPrivateKey::ExportValue(std::vector<uint8>* output) {
348 return ReadAttribute(key_, CKA_VALUE, output);
351 bool ECPrivateKey::ExportECParams(std::vector<uint8>* output) {
352 return ReadAttribute(key_, CKA_EC_PARAMS, output);
355 ECPrivateKey::ECPrivateKey() : key_(NULL), public_key_(NULL) {}
357 } // namespace crypto