OLD | NEW |
| (Empty) |
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 | |
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 | |
13 #include <cryptohi.h> | |
14 #include <keyhi.h> | |
15 #include <pk11pub.h> | |
16 #include <secmod.h> | |
17 #include <stddef.h> | |
18 #include <stdint.h> | |
19 | |
20 #include <memory> | |
21 | |
22 #include "base/logging.h" | |
23 #include "crypto/nss_util.h" | |
24 #include "crypto/nss_util_internal.h" | |
25 #include "crypto/scoped_nss_types.h" | |
26 #include "crypto/third_party/nss/chromium-nss.h" | |
27 | |
28 namespace { | |
29 | |
30 static bool AppendAttribute(SECKEYPrivateKey* key, | |
31 CK_ATTRIBUTE_TYPE type, | |
32 std::vector<uint8_t>* output) { | |
33 SECItem item; | |
34 SECStatus rv; | |
35 rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item); | |
36 if (rv != SECSuccess) { | |
37 DLOG(ERROR) << "PK11_ReadRawAttribute: " << PORT_GetError(); | |
38 return false; | |
39 } | |
40 | |
41 output->insert(output->end(), item.data, item.data + item.len); | |
42 SECITEM_FreeItem(&item, PR_FALSE); | |
43 return true; | |
44 } | |
45 | |
46 } // namespace | |
47 | |
48 namespace crypto { | |
49 | |
50 ECPrivateKey::~ECPrivateKey() { | |
51 if (key_) | |
52 SECKEY_DestroyPrivateKey(key_); | |
53 if (public_key_) | |
54 SECKEY_DestroyPublicKey(public_key_); | |
55 } | |
56 | |
57 // static | |
58 ECPrivateKey* ECPrivateKey::Create() { | |
59 EnsureNSSInit(); | |
60 | |
61 ScopedPK11Slot slot(PK11_GetInternalSlot()); | |
62 if (!slot) | |
63 return nullptr; | |
64 | |
65 std::unique_ptr<ECPrivateKey> result(new ECPrivateKey); | |
66 | |
67 SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); | |
68 if (!oid_data) { | |
69 DLOG(ERROR) << "SECOID_FindOIDByTag: " << PORT_GetError(); | |
70 return nullptr; | |
71 } | |
72 | |
73 // SECKEYECParams is a SECItem containing the DER encoded ASN.1 ECParameters | |
74 // value. For a named curve, that is just the OBJECT IDENTIFIER of the curve. | |
75 // In addition to the oid data, the encoding requires one byte for the ASN.1 | |
76 // tag and one byte for the length (assuming the length is <= 127). | |
77 CHECK_LE(oid_data->oid.len, 127U); | |
78 std::vector<unsigned char> parameters_buf(2 + oid_data->oid.len); | |
79 SECKEYECParams ec_parameters = { | |
80 siDEROID, ¶meters_buf[0], | |
81 static_cast<unsigned>(parameters_buf.size()) | |
82 }; | |
83 | |
84 ec_parameters.data[0] = SEC_ASN1_OBJECT_ID; | |
85 ec_parameters.data[1] = static_cast<unsigned char>(oid_data->oid.len); | |
86 memcpy(ec_parameters.data + 2, oid_data->oid.data, oid_data->oid.len); | |
87 | |
88 result->key_ = PK11_GenerateKeyPair(slot.get(), | |
89 CKM_EC_KEY_PAIR_GEN, | |
90 &ec_parameters, | |
91 &result->public_key_, | |
92 PR_FALSE /* not permanent */, | |
93 PR_FALSE /* not sensitive */, | |
94 NULL); | |
95 if (!result->key_) { | |
96 DLOG(ERROR) << "PK11_GenerateKeyPair: " << PORT_GetError(); | |
97 return nullptr; | |
98 } | |
99 CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(result->public_key_)); | |
100 | |
101 return result.release(); | |
102 } | |
103 | |
104 // static | |
105 ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( | |
106 const std::string& password, | |
107 const std::vector<uint8_t>& encrypted_private_key_info, | |
108 const std::vector<uint8_t>& subject_public_key_info) { | |
109 EnsureNSSInit(); | |
110 | |
111 ScopedPK11Slot slot(PK11_GetInternalSlot()); | |
112 if (!slot) | |
113 return nullptr; | |
114 | |
115 std::unique_ptr<ECPrivateKey> result(new ECPrivateKey); | |
116 | |
117 SECItem encoded_spki = { | |
118 siBuffer, | |
119 const_cast<unsigned char*>(&subject_public_key_info[0]), | |
120 static_cast<unsigned>(subject_public_key_info.size()) | |
121 }; | |
122 CERTSubjectPublicKeyInfo* decoded_spki = SECKEY_DecodeDERSubjectPublicKeyInfo( | |
123 &encoded_spki); | |
124 if (!decoded_spki) { | |
125 DLOG(ERROR) << "SECKEY_DecodeDERSubjectPublicKeyInfo: " << PORT_GetError(); | |
126 return nullptr; | |
127 } | |
128 | |
129 bool success = ImportFromEncryptedPrivateKeyInfo( | |
130 slot.get(), | |
131 password, | |
132 &encrypted_private_key_info[0], | |
133 encrypted_private_key_info.size(), | |
134 decoded_spki, | |
135 false /* not permanent */, | |
136 false /* not sensitive */, | |
137 &result->key_, | |
138 &result->public_key_); | |
139 | |
140 SECKEY_DestroySubjectPublicKeyInfo(decoded_spki); | |
141 | |
142 if (success) { | |
143 CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(result->public_key_)); | |
144 return result.release(); | |
145 } | |
146 | |
147 return nullptr; | |
148 } | |
149 | |
150 // static | |
151 bool ECPrivateKey::ImportFromEncryptedPrivateKeyInfo( | |
152 PK11SlotInfo* slot, | |
153 const std::string& password, | |
154 const uint8_t* encrypted_private_key_info, | |
155 size_t encrypted_private_key_info_len, | |
156 CERTSubjectPublicKeyInfo* decoded_spki, | |
157 bool permanent, | |
158 bool sensitive, | |
159 SECKEYPrivateKey** key, | |
160 SECKEYPublicKey** public_key) { | |
161 if (!slot) | |
162 return false; | |
163 | |
164 *public_key = SECKEY_ExtractPublicKey(decoded_spki); | |
165 | |
166 if (!*public_key) { | |
167 DLOG(ERROR) << "SECKEY_ExtractPublicKey: " << PORT_GetError(); | |
168 return false; | |
169 } | |
170 | |
171 if (SECKEY_GetPublicKeyType(*public_key) != ecKey) { | |
172 DLOG(ERROR) << "The public key is not an EC key"; | |
173 SECKEY_DestroyPublicKey(*public_key); | |
174 *public_key = NULL; | |
175 return false; | |
176 } | |
177 | |
178 SECItem encoded_epki = { | |
179 siBuffer, | |
180 const_cast<unsigned char*>(encrypted_private_key_info), | |
181 static_cast<unsigned>(encrypted_private_key_info_len) | |
182 }; | |
183 SECKEYEncryptedPrivateKeyInfo epki; | |
184 memset(&epki, 0, sizeof(epki)); | |
185 | |
186 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); | |
187 | |
188 SECStatus rv = SEC_QuickDERDecodeItem( | |
189 arena.get(), | |
190 &epki, | |
191 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate), | |
192 &encoded_epki); | |
193 if (rv != SECSuccess) { | |
194 DLOG(ERROR) << "SEC_QuickDERDecodeItem: " << PORT_GetError(); | |
195 SECKEY_DestroyPublicKey(*public_key); | |
196 *public_key = NULL; | |
197 return false; | |
198 } | |
199 | |
200 SECItem password_item = { | |
201 siBuffer, | |
202 reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())), | |
203 static_cast<unsigned>(password.size()) | |
204 }; | |
205 | |
206 rv = ImportEncryptedECPrivateKeyInfoAndReturnKey( | |
207 slot, | |
208 &epki, | |
209 &password_item, | |
210 NULL, // nickname | |
211 &(*public_key)->u.ec.publicValue, | |
212 permanent, | |
213 sensitive, | |
214 key, | |
215 NULL); // wincx | |
216 if (rv != SECSuccess) { | |
217 DLOG(ERROR) << "ImportEncryptedECPrivateKeyInfoAndReturnKey: " | |
218 << PORT_GetError(); | |
219 SECKEY_DestroyPublicKey(*public_key); | |
220 *public_key = NULL; | |
221 return false; | |
222 } | |
223 | |
224 return true; | |
225 } | |
226 | |
227 ECPrivateKey* ECPrivateKey::Copy() const { | |
228 std::unique_ptr<ECPrivateKey> copy(new ECPrivateKey); | |
229 if (key_) { | |
230 copy->key_ = SECKEY_CopyPrivateKey(key_); | |
231 if (!copy->key_) | |
232 return NULL; | |
233 } | |
234 if (public_key_) { | |
235 copy->public_key_ = SECKEY_CopyPublicKey(public_key_); | |
236 if (!copy->public_key_) | |
237 return NULL; | |
238 } | |
239 return copy.release(); | |
240 } | |
241 | |
242 bool ECPrivateKey::ExportEncryptedPrivateKey(const std::string& password, | |
243 int iterations, | |
244 std::vector<uint8_t>* output) { | |
245 // We export as an EncryptedPrivateKeyInfo bundle instead of a plain PKCS #8 | |
246 // PrivateKeyInfo because PK11_ImportDERPrivateKeyInfoAndReturnKey doesn't | |
247 // support EC keys. | |
248 // https://bugzilla.mozilla.org/show_bug.cgi?id=327773 | |
249 SECItem password_item = { | |
250 siBuffer, | |
251 reinterpret_cast<unsigned char*>(const_cast<char*>(password.data())), | |
252 static_cast<unsigned>(password.size()) | |
253 }; | |
254 | |
255 SECKEYEncryptedPrivateKeyInfo* encrypted = PK11_ExportEncryptedPrivKeyInfo( | |
256 NULL, // Slot, optional. | |
257 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC, | |
258 &password_item, | |
259 key_, | |
260 iterations, | |
261 NULL); // wincx. | |
262 | |
263 if (!encrypted) { | |
264 DLOG(ERROR) << "PK11_ExportEncryptedPrivKeyInfo: " << PORT_GetError(); | |
265 return false; | |
266 } | |
267 | |
268 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); | |
269 SECItem der_key = {siBuffer, NULL, 0}; | |
270 SECItem* encoded_item = SEC_ASN1EncodeItem( | |
271 arena.get(), | |
272 &der_key, | |
273 encrypted, | |
274 SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate)); | |
275 SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted, PR_TRUE); | |
276 if (!encoded_item) { | |
277 DLOG(ERROR) << "SEC_ASN1EncodeItem: " << PORT_GetError(); | |
278 return false; | |
279 } | |
280 | |
281 output->assign(der_key.data, der_key.data + der_key.len); | |
282 | |
283 return true; | |
284 } | |
285 | |
286 bool ECPrivateKey::ExportPublicKey(std::vector<uint8_t>* output) { | |
287 ScopedSECItem der_pubkey( | |
288 SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_)); | |
289 if (!der_pubkey.get()) { | |
290 return false; | |
291 } | |
292 | |
293 output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len); | |
294 return true; | |
295 } | |
296 | |
297 bool ECPrivateKey::ExportRawPublicKey(std::string* output) { | |
298 // public_key_->u.ec.publicValue is an ANSI X9.62 public key which, for | |
299 // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field | |
300 // elements as 32-byte, big-endian numbers. | |
301 static const unsigned int kExpectedKeyLength = 65; | |
302 | |
303 CHECK_EQ(ecKey, SECKEY_GetPublicKeyType(public_key_)); | |
304 const unsigned char* const data = public_key_->u.ec.publicValue.data; | |
305 const unsigned int len = public_key_->u.ec.publicValue.len; | |
306 if (len != kExpectedKeyLength || data[0] != 0x04) | |
307 return false; | |
308 | |
309 output->assign(reinterpret_cast<const char*>(data + 1), | |
310 kExpectedKeyLength - 1); | |
311 return true; | |
312 } | |
313 | |
314 bool ECPrivateKey::ExportValueForTesting(std::vector<uint8_t>* output) { | |
315 // This serialization format is purely for testing equality, so just | |
316 // concatenate the raw private key (always 32 bytes for P-256) with the | |
317 // parameters. | |
318 output->clear(); | |
319 return AppendAttribute(key_, CKA_VALUE, output) && | |
320 output->size() == 32 && | |
321 AppendAttribute(key_, CKA_EC_PARAMS, output); | |
322 } | |
323 | |
324 ECPrivateKey::ECPrivateKey() : key_(NULL), public_key_(NULL) {} | |
325 | |
326 } // namespace crypto | |
OLD | NEW |