Bug 1935611 - Fix libyuv/libpng link failed for loongarch64. r=glandium,tnikkel,ng
[gecko.git] / security / manager / ssl / SecretDecoderRing.cpp
blob8cfebde8f912861fbeb1fe5a6bc4ae2d86bfbeb2
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SecretDecoderRing.h"
9 #include "ScopedNSSTypes.h"
10 #include "mozilla/Base64.h"
11 #include "mozilla/Casting.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/Services.h"
14 #include "mozilla/ErrorResult.h"
15 #include "mozilla/dom/Promise.h"
16 #include "nsCOMPtr.h"
17 #include "nsIInterfaceRequestor.h"
18 #include "nsIInterfaceRequestorUtils.h"
19 #include "nsIObserverService.h"
20 #include "nsITokenPasswordDialogs.h"
21 #include "nsNSSComponent.h"
22 #include "nsNSSHelper.h"
23 #include "nsNetCID.h"
24 #include "nsPK11TokenDB.h"
25 #include "pk11func.h"
26 #include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt
28 static mozilla::LazyLogModule gSDRLog("sdrlog");
30 using namespace mozilla;
31 using dom::Promise;
33 NS_IMPL_ISUPPORTS(SecretDecoderRing, nsISecretDecoderRing)
35 void BackgroundSdrEncryptStrings(const nsTArray<nsCString>& plaintexts,
36 RefPtr<Promise>& aPromise) {
37 nsCOMPtr<nsISecretDecoderRing> sdrService =
38 do_GetService(NS_SECRETDECODERRING_CONTRACTID);
39 nsTArray<nsString> cipherTexts(plaintexts.Length());
41 nsresult rv = NS_ERROR_FAILURE;
42 for (const auto& plaintext : plaintexts) {
43 nsCString cipherText;
44 rv = sdrService->EncryptString(plaintext, cipherText);
46 if (NS_WARN_IF(NS_FAILED(rv))) {
47 break;
50 cipherTexts.AppendElement(NS_ConvertASCIItoUTF16(cipherText));
53 nsCOMPtr<nsIRunnable> runnable(
54 NS_NewRunnableFunction("BackgroundSdrEncryptStringsResolve",
55 [rv, aPromise = std::move(aPromise),
56 cipherTexts = std::move(cipherTexts)]() {
57 if (NS_FAILED(rv)) {
58 aPromise->MaybeReject(rv);
59 } else {
60 aPromise->MaybeResolve(cipherTexts);
62 }));
63 NS_DispatchToMainThread(runnable.forget());
66 void BackgroundSdrDecryptStrings(const nsTArray<nsCString>& encryptedStrings,
67 RefPtr<Promise>& aPromise) {
68 nsCOMPtr<nsISecretDecoderRing> sdrService =
69 do_GetService(NS_SECRETDECODERRING_CONTRACTID);
70 nsTArray<nsString> plainTexts(encryptedStrings.Length());
72 nsresult rv = NS_ERROR_FAILURE;
73 for (const auto& encryptedString : encryptedStrings) {
74 nsCString plainText;
75 rv = sdrService->DecryptString(encryptedString, plainText);
77 if (NS_FAILED(rv)) {
78 if (rv == NS_ERROR_NOT_AVAILABLE) {
79 // Master Password entry was canceled. Don't keep prompting again.
80 break;
83 // NS_ERROR_ILLEGAL_VALUE or NS_ERROR_FAILURE could be due to bad data for
84 // a single string but we still want to decrypt the others.
85 // Callers of `decryptMany` in crypto-SDR.js assume there will be an
86 // equal number of usernames and passwords so use an empty string to keep
87 // this assumption true.
88 MOZ_LOG(gSDRLog, LogLevel::Warning,
89 ("Couldn't decrypt string: %s", encryptedString.get()));
90 plainTexts.AppendElement(nullptr);
91 rv = NS_OK;
92 continue;
95 plainTexts.AppendElement(NS_ConvertUTF8toUTF16(plainText));
98 nsCOMPtr<nsIRunnable> runnable(
99 NS_NewRunnableFunction("BackgroundSdrDecryptStringsResolve",
100 [rv, aPromise = std::move(aPromise),
101 plainTexts = std::move(plainTexts)]() {
102 if (NS_FAILED(rv)) {
103 aPromise->MaybeReject(rv);
104 } else {
105 aPromise->MaybeResolve(plainTexts);
107 }));
108 NS_DispatchToMainThread(runnable.forget());
111 nsresult SecretDecoderRing::Encrypt(const nsACString& data,
112 /*out*/ nsACString& result) {
113 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
114 if (!slot) {
115 return NS_ERROR_NOT_AVAILABLE;
118 /* Make sure token is initialized. */
119 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
120 nsresult rv = setPassword(slot.get(), ctx);
121 if (NS_FAILED(rv)) {
122 return rv;
125 /* Force authentication */
126 if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) {
127 return NS_ERROR_FAILURE;
130 /* Use default key id */
131 SECItem keyid;
132 keyid.data = nullptr;
133 keyid.len = 0;
134 SECItem request;
135 request.data = BitwiseCast<unsigned char*, const char*>(data.BeginReading());
136 request.len = data.Length();
137 ScopedAutoSECItem reply;
138 if (PK11SDR_Encrypt(&keyid, &request, &reply, ctx) != SECSuccess) {
139 return NS_ERROR_FAILURE;
142 result.Assign(BitwiseCast<char*, unsigned char*>(reply.data), reply.len);
143 return NS_OK;
146 nsresult SecretDecoderRing::Decrypt(const nsACString& data,
147 /*out*/ nsACString& result) {
148 /* Find token with SDR key */
149 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
150 if (!slot) {
151 return NS_ERROR_NOT_AVAILABLE;
154 /* Force authentication */
155 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
156 if (PK11_Authenticate(slot.get(), true, ctx) != SECSuccess) {
157 return NS_ERROR_NOT_AVAILABLE;
160 SECItem request;
161 request.data = BitwiseCast<unsigned char*, const char*>(data.BeginReading());
162 request.len = data.Length();
163 ScopedAutoSECItem reply;
164 if (PK11SDR_Decrypt(&request, &reply, ctx) != SECSuccess) {
165 return NS_ERROR_FAILURE;
168 result.Assign(BitwiseCast<char*, unsigned char*>(reply.data), reply.len);
169 return NS_OK;
172 NS_IMETHODIMP
173 SecretDecoderRing::EncryptString(const nsACString& text,
174 /*out*/ nsACString& encryptedBase64Text) {
175 nsAutoCString encryptedText;
176 nsresult rv = Encrypt(text, encryptedText);
177 if (NS_FAILED(rv)) {
178 return rv;
181 rv = Base64Encode(encryptedText, encryptedBase64Text);
182 if (NS_FAILED(rv)) {
183 return rv;
186 return NS_OK;
189 NS_IMETHODIMP
190 SecretDecoderRing::AsyncEncryptStrings(const nsTArray<nsCString>& plaintexts,
191 JSContext* aCx, Promise** aPromise) {
192 MOZ_RELEASE_ASSERT(NS_IsMainThread());
193 NS_ENSURE_ARG(!plaintexts.IsEmpty());
194 NS_ENSURE_ARG_POINTER(aCx);
195 NS_ENSURE_ARG_POINTER(aPromise);
197 nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
198 if (NS_WARN_IF(!globalObject)) {
199 return NS_ERROR_UNEXPECTED;
202 ErrorResult result;
203 RefPtr<Promise> promise = Promise::Create(globalObject, result);
204 if (NS_WARN_IF(result.Failed())) {
205 return result.StealNSResult();
208 // plaintexts are already expected to be UTF-8.
209 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
210 "BackgroundSdrEncryptStrings",
211 [promise, plaintexts = plaintexts.Clone()]() mutable {
212 BackgroundSdrEncryptStrings(plaintexts, promise);
213 }));
215 nsCOMPtr<nsIEventTarget> target(
216 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
217 if (!target) {
218 return NS_ERROR_FAILURE;
220 nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
221 if (NS_WARN_IF(NS_FAILED(rv))) {
222 return rv;
225 promise.forget(aPromise);
226 return NS_OK;
229 NS_IMETHODIMP
230 SecretDecoderRing::DecryptString(const nsACString& encryptedBase64Text,
231 /*out*/ nsACString& decryptedText) {
232 nsAutoCString encryptedText;
233 nsresult rv = Base64Decode(encryptedBase64Text, encryptedText);
234 if (NS_FAILED(rv)) {
235 return rv;
238 rv = Decrypt(encryptedText, decryptedText);
239 if (NS_FAILED(rv)) {
240 return rv;
243 return NS_OK;
246 NS_IMETHODIMP
247 SecretDecoderRing::AsyncDecryptStrings(
248 const nsTArray<nsCString>& encryptedStrings, JSContext* aCx,
249 Promise** aPromise) {
250 MOZ_RELEASE_ASSERT(NS_IsMainThread());
251 NS_ENSURE_ARG(!encryptedStrings.IsEmpty());
252 NS_ENSURE_ARG_POINTER(aCx);
253 NS_ENSURE_ARG_POINTER(aPromise);
255 nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
256 if (NS_WARN_IF(!globalObject)) {
257 return NS_ERROR_UNEXPECTED;
260 ErrorResult result;
261 RefPtr<Promise> promise = Promise::Create(globalObject, result);
262 if (NS_WARN_IF(result.Failed())) {
263 return result.StealNSResult();
266 // encryptedStrings are expected to be base64.
267 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
268 "BackgroundSdrDecryptStrings",
269 [promise, encryptedStrings = encryptedStrings.Clone()]() mutable {
270 BackgroundSdrDecryptStrings(encryptedStrings, promise);
271 }));
273 nsCOMPtr<nsIEventTarget> target(
274 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
275 if (!target) {
276 return NS_ERROR_FAILURE;
278 nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
279 if (NS_WARN_IF(NS_FAILED(rv))) {
280 return rv;
283 promise.forget(aPromise);
284 return NS_OK;
287 NS_IMETHODIMP
288 SecretDecoderRing::ChangePassword() {
289 UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
290 if (!slot) {
291 return NS_ERROR_NOT_AVAILABLE;
294 // nsPK11Token::nsPK11Token takes its own reference to slot, so we pass a
295 // non-owning pointer here.
296 nsCOMPtr<nsIPK11Token> token = new nsPK11Token(slot.get());
298 nsCOMPtr<nsITokenPasswordDialogs> dialogs;
299 nsresult rv = getNSSDialogs(getter_AddRefs(dialogs),
300 NS_GET_IID(nsITokenPasswordDialogs),
301 NS_TOKENPASSWORDSDIALOG_CONTRACTID);
302 if (NS_FAILED(rv)) {
303 return rv;
306 nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
307 bool canceled; // Ignored
308 return dialogs->SetPassword(ctx, token, &canceled);
311 NS_IMETHODIMP
312 SecretDecoderRing::Logout() {
313 PK11_LogoutAll();
314 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
315 if (!nssComponent) {
316 return NS_ERROR_NOT_AVAILABLE;
318 return nssComponent->ClearSSLExternalAndInternalSessionCache();
321 NS_IMETHODIMP
322 SecretDecoderRing::LogoutAndTeardown() {
323 PK11_LogoutAll();
324 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
325 if (!nssComponent) {
326 return NS_ERROR_NOT_AVAILABLE;
329 // LogoutAuthenticatedPK11 also clears the SSL caches.
330 nsresult rv = nssComponent->LogoutAuthenticatedPK11();
331 if (NS_FAILED(rv)) {
332 return rv;
335 // After we just logged out, we need to prune dead connections to make
336 // sure that all connections that should be stopped, are stopped. See
337 // bug 517584.
338 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
339 if (os) {
340 os->NotifyObservers(nullptr, "net:prune-dead-connections", nullptr);
343 return NS_OK;