Bug 1935611 - Fix libyuv/libpng link failed for loongarch64. r=glandium,tnikkel,ng
[gecko.git] / security / manager / ssl / nsPKCS12Blob.cpp
blob05c90bd427638a0fdbc1d67c75bc6b858c3fb15f
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsPKCS12Blob.h"
7 #include "mozilla/Assertions.h"
8 #include "mozilla/Casting.h"
9 #include "mozilla/Logging.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/StaticPrefs_security.h"
12 #include "mozilla/Unused.h"
13 #include "mozpkix/pkixtypes.h"
14 #include "nsIFile.h"
15 #include "nsIInputStream.h"
16 #include "nsIX509CertDB.h"
17 #include "nsNetUtil.h"
18 #include "nsNSSCertHelper.h"
19 #include "nsNSSCertificate.h"
20 #include "nsNSSHelper.h"
21 #include "nsReadableUtils.h"
22 #include "nsTArray.h"
23 #include "nsThreadUtils.h"
24 #include "p12plcy.h"
25 #include "ScopedNSSTypes.h"
26 #include "secerr.h"
28 using namespace mozilla;
29 extern LazyLogModule gPIPNSSLog;
31 #define PIP_PKCS12_BUFFER_SIZE 2048
32 #define PIP_PKCS12_NOSMARTCARD_EXPORT 4
33 #define PIP_PKCS12_RESTORE_FAILED 5
34 #define PIP_PKCS12_BACKUP_FAILED 6
35 #define PIP_PKCS12_NSS_ERROR 7
37 nsPKCS12Blob::nsPKCS12Blob() : mUIContext(new PipUIContext()) {}
39 // Given a file handle, read a PKCS#12 blob from that file, decode it, and
40 // import the results into the internal database.
41 nsresult nsPKCS12Blob::ImportFromFile(nsIFile* aFile,
42 const nsAString& aPassword,
43 uint32_t& aError) {
44 uint32_t passwordBufferLength;
45 UniquePtr<uint8_t[]> passwordBuffer;
47 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
48 if (!slot) {
49 return NS_ERROR_FAILURE;
52 passwordBuffer = stringToBigEndianBytes(aPassword, passwordBufferLength);
54 // initialize the decoder
55 SECItem unicodePw = {siBuffer, passwordBuffer.get(), passwordBufferLength};
56 UniqueSEC_PKCS12DecoderContext dcx(
57 SEC_PKCS12DecoderStart(&unicodePw, slot.get(), nullptr, nullptr, nullptr,
58 nullptr, nullptr, nullptr));
59 if (!dcx) {
60 return NS_ERROR_FAILURE;
62 // read input aFile and feed it to the decoder
63 PRErrorCode nssError;
64 nsresult rv = inputToDecoder(dcx, aFile, nssError);
65 if (NS_FAILED(rv)) {
66 return rv;
68 if (nssError != 0) {
69 aError = handlePRErrorCode(nssError);
70 return NS_OK;
72 // verify the blob
73 SECStatus srv = SEC_PKCS12DecoderVerify(dcx.get());
74 if (srv != SECSuccess) {
75 aError = handlePRErrorCode(PR_GetError());
76 return NS_OK;
78 // validate bags
79 srv = SEC_PKCS12DecoderValidateBags(dcx.get(), nicknameCollision);
80 if (srv != SECSuccess) {
81 aError = handlePRErrorCode(PR_GetError());
82 return NS_OK;
84 // import cert and key
85 srv = SEC_PKCS12DecoderImportBags(dcx.get());
86 if (srv != SECSuccess) {
87 aError = handlePRErrorCode(PR_GetError());
88 return NS_OK;
90 aError = nsIX509CertDB::Success;
91 return NS_OK;
94 static bool isExtractable(UniqueSECKEYPrivateKey& privKey) {
95 ScopedAutoSECItem value;
96 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey.get(),
97 CKA_EXTRACTABLE, &value);
98 if (rv != SECSuccess) {
99 return false;
102 bool isExtractable = false;
103 if ((value.len == 1) && value.data) {
104 isExtractable = !!(*(CK_BBOOL*)value.data);
106 return isExtractable;
109 // Having already loaded the certs, form them into a blob (loading the keys
110 // also), encode the blob, and stuff it into the file.
111 nsresult nsPKCS12Blob::ExportToFile(nsIFile* aFile,
112 const nsTArray<RefPtr<nsIX509Cert>>& aCerts,
113 const nsAString& aPassword,
114 uint32_t& aError) {
115 nsCString passwordUtf8 = NS_ConvertUTF16toUTF8(aPassword);
116 uint32_t passwordBufferLength = passwordUtf8.Length();
117 aError = nsIX509CertDB::Success;
118 // The conversion to UCS2 is executed by sec_pkcs12_encode_password when
119 // necessary (for some older PKCS12 algorithms). The NSS 3.31 and newer
120 // expects password to be in the utf8 encoding to support modern encoders.
121 UniquePtr<unsigned char[]> passwordBuffer(
122 reinterpret_cast<unsigned char*>(ToNewCString(passwordUtf8)));
123 if (!passwordBuffer.get()) {
124 return NS_OK;
126 UniqueSEC_PKCS12ExportContext ecx(
127 SEC_PKCS12CreateExportContext(nullptr, nullptr, nullptr, nullptr));
128 if (!ecx) {
129 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
130 return NS_OK;
132 bool useModernCrypto =
133 StaticPrefs::security_pki_use_modern_crypto_with_pkcs12();
134 // add password integrity
135 SECItem unicodePw = {siBuffer, passwordBuffer.get(), passwordBufferLength};
136 SECStatus srv = SEC_PKCS12AddPasswordIntegrity(
137 ecx.get(), &unicodePw, useModernCrypto ? SEC_OID_SHA256 : SEC_OID_SHA1);
138 if (srv != SECSuccess) {
139 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
140 return NS_OK;
142 for (auto& cert : aCerts) {
143 UniqueCERTCertificate nssCert(cert->GetCert());
144 if (!nssCert) {
145 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
146 return NS_OK;
148 // We can probably only successfully export certs that are on the internal
149 // token. Most, if not all, smart card vendors won't let you extract the
150 // private key (in any way shape or form) from the card. So let's punt if
151 // the cert is not in the internal db.
152 if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) {
153 // We aren't the internal token, see if the key is extractable.
154 UniqueSECKEYPrivateKey privKey(
155 PK11_FindKeyByDERCert(nssCert->slot, nssCert.get(), mUIContext));
156 if (privKey && !isExtractable(privKey)) {
157 // This is informative. If a serious error occurs later it will
158 // override it later and return.
159 aError = nsIX509CertDB::ERROR_PKCS12_NOSMARTCARD_EXPORT;
160 continue;
164 // certSafe and keySafe are owned by ecx.
165 SEC_PKCS12SafeInfo* certSafe;
166 SEC_PKCS12SafeInfo* keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx.get());
167 // We use SEC_OID_AES_128_CBC for the password and SEC_OID_AES_256_CBC
168 // for the certificate because it's a default for openssl an pk12util
169 // command.
170 if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
171 certSafe = keySafe;
172 } else {
173 SECOidTag privAlg =
174 useModernCrypto ? SEC_OID_AES_128_CBC
175 : SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
176 certSafe =
177 SEC_PKCS12CreatePasswordPrivSafe(ecx.get(), &unicodePw, privAlg);
179 if (!certSafe || !keySafe) {
180 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
181 return NS_OK;
183 // add the cert and key to the blob
184 SECOidTag algorithm =
185 useModernCrypto
186 ? SEC_OID_AES_256_CBC
187 : SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
188 srv = SEC_PKCS12AddCertAndKey(ecx.get(), certSafe, nullptr, nssCert.get(),
189 CERT_GetDefaultCertDB(), keySafe, nullptr,
190 true, &unicodePw, algorithm);
191 if (srv != SECSuccess) {
192 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
193 return NS_OK;
197 UniquePRFileDesc prFile;
198 PRFileDesc* rawPRFile;
199 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
200 0664, &rawPRFile);
201 if (NS_FAILED(rv) || !rawPRFile) {
202 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
203 return NS_OK;
205 prFile.reset(rawPRFile);
206 // encode and write
207 srv = SEC_PKCS12Encode(ecx.get(), writeExportFile, prFile.get());
208 if (srv != SECSuccess) {
209 aError = nsIX509CertDB::ERROR_PKCS12_BACKUP_FAILED;
211 return NS_OK;
214 // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to a buffer of
215 // octets. Must handle byte order correctly.
216 UniquePtr<uint8_t[]> nsPKCS12Blob::stringToBigEndianBytes(
217 const nsAString& uni, uint32_t& bytesLength) {
218 if (uni.IsVoid()) {
219 bytesLength = 0;
220 return nullptr;
223 uint32_t wideLength = uni.Length() + 1; // +1 for the null terminator.
224 bytesLength = wideLength * 2;
225 auto buffer = MakeUnique<uint8_t[]>(bytesLength);
227 // We have to use a cast here because on Windows, uni.get() returns
228 // char16ptr_t instead of char16_t*.
229 mozilla::NativeEndian::copyAndSwapToBigEndian(
230 buffer.get(), static_cast<const char16_t*>(uni.BeginReading()),
231 wideLength);
233 return buffer;
236 // Given a decoder, read bytes from file and input them to the decoder.
237 nsresult nsPKCS12Blob::inputToDecoder(UniqueSEC_PKCS12DecoderContext& dcx,
238 nsIFile* file, PRErrorCode& nssError) {
239 nssError = 0;
241 nsCOMPtr<nsIInputStream> fileStream;
242 nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), file);
243 if (NS_FAILED(rv)) {
244 return rv;
247 char buf[PIP_PKCS12_BUFFER_SIZE];
248 uint32_t amount;
249 while (true) {
250 rv = fileStream->Read(buf, PIP_PKCS12_BUFFER_SIZE, &amount);
251 if (NS_FAILED(rv)) {
252 return rv;
254 // feed the file data into the decoder
255 SECStatus srv =
256 SEC_PKCS12DecoderUpdate(dcx.get(), (unsigned char*)buf, amount);
257 if (srv != SECSuccess) {
258 nssError = PR_GetError();
259 return NS_OK;
261 if (amount < PIP_PKCS12_BUFFER_SIZE) {
262 break;
265 return NS_OK;
268 // What to do when the nickname collides with one already in the db.
269 SECItem* nsPKCS12Blob::nicknameCollision(SECItem* oldNick, PRBool* cancel,
270 void* wincx) {
271 *cancel = false;
272 int count = 1;
273 nsCString nickname;
274 nsAutoString nickFromProp;
275 nsresult rv = GetPIPNSSBundleString("P12DefaultNickname", nickFromProp);
276 if (NS_FAILED(rv)) {
277 return nullptr;
279 NS_ConvertUTF16toUTF8 nickFromPropC(nickFromProp);
280 // The user is trying to import a PKCS#12 file that doesn't have the
281 // attribute we use to set the nickname. So in order to reduce the
282 // number of interactions we require with the user, we'll build a nickname
283 // for the user. The nickname isn't prominently displayed in the UI,
284 // so it's OK if we generate one on our own here.
285 // XXX If the NSS API were smarter and actually passed a pointer to
286 // the CERTCertificate* we're importing we could actually just
287 // call default_nickname (which is what the issuance code path
288 // does) and come up with a reasonable nickname. Alas, the NSS
289 // API limits our ability to produce a useful nickname without
290 // bugging the user. :(
291 while (1) {
292 // If we've gotten this far, that means there isn't a certificate
293 // in the database that has the same subject name as the cert we're
294 // trying to import. So we need to come up with a "nickname" to
295 // satisfy the NSS requirement or fail in trying to import.
296 // Basically we use a default nickname from a properties file and
297 // see if a certificate exists with that nickname. If there isn't, then
298 // create update the count by one and append the string '#1' Or
299 // whatever the count currently is, and look for a cert with
300 // that nickname. Keep updating the count until we find a nickname
301 // without a corresponding cert.
302 // XXX If a user imports *many* certs without the 'friendly name'
303 // attribute, then this may take a long time. :(
304 nickname = nickFromPropC;
305 if (count > 1) {
306 nickname.AppendPrintf(" #%d", count);
308 UniqueCERTCertificate cert(
309 CERT_FindCertByNickname(CERT_GetDefaultCertDB(), nickname.get()));
310 if (!cert) {
311 break;
313 count++;
315 UniqueSECItem newNick(
316 SECITEM_AllocItem(nullptr, nullptr, nickname.Length() + 1));
317 if (!newNick) {
318 return nullptr;
320 memcpy(newNick->data, nickname.get(), nickname.Length());
321 newNick->data[nickname.Length()] = 0;
323 return newNick.release();
326 // write bytes to the exported PKCS#12 file
327 void nsPKCS12Blob::writeExportFile(void* arg, const char* buf,
328 unsigned long len) {
329 PRFileDesc* file = static_cast<PRFileDesc*>(arg);
330 MOZ_RELEASE_ASSERT(file);
331 PR_Write(file, buf, len);
334 // Translate PRErrorCode to nsIX509CertDB error
335 uint32_t nsPKCS12Blob::handlePRErrorCode(PRErrorCode aPrerr) {
336 MOZ_ASSERT(aPrerr != 0);
337 uint32_t error = nsIX509CertDB::ERROR_UNKNOWN;
338 switch (aPrerr) {
339 case SEC_ERROR_PKCS12_CERT_COLLISION:
340 error = nsIX509CertDB::ERROR_PKCS12_DUPLICATE_DATA;
341 break;
342 // INVALID_ARGS is returned on bad password when importing cert
343 // exported from firefox or generated by openssl
344 case SEC_ERROR_INVALID_ARGS:
345 case SEC_ERROR_BAD_PASSWORD:
346 error = nsIX509CertDB::ERROR_BAD_PASSWORD;
347 break;
348 case SEC_ERROR_BAD_DER:
349 case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
350 case SEC_ERROR_PKCS12_INVALID_MAC:
351 error = nsIX509CertDB::ERROR_DECODE_ERROR;
352 break;
353 case SEC_ERROR_PKCS12_DUPLICATE_DATA:
354 error = nsIX509CertDB::ERROR_PKCS12_DUPLICATE_DATA;
355 break;
357 return error;