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"
17 #include "nsIInterfaceRequestor.h"
18 #include "nsIInterfaceRequestorUtils.h"
19 #include "nsIObserverService.h"
20 #include "nsITokenPasswordDialogs.h"
21 #include "nsNSSComponent.h"
22 #include "nsNSSHelper.h"
24 #include "nsPK11TokenDB.h"
26 #include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt
28 static mozilla::LazyLogModule
gSDRLog("sdrlog");
30 using namespace mozilla
;
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
) {
44 rv
= sdrService
->EncryptString(plaintext
, cipherText
);
46 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
)]() {
58 aPromise
->MaybeReject(rv
);
60 aPromise
->MaybeResolve(cipherTexts
);
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
) {
75 rv
= sdrService
->DecryptString(encryptedString
, plainText
);
78 if (rv
== NS_ERROR_NOT_AVAILABLE
) {
79 // Master Password entry was canceled. Don't keep prompting again.
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);
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
)]() {
103 aPromise
->MaybeReject(rv
);
105 aPromise
->MaybeResolve(plainTexts
);
108 NS_DispatchToMainThread(runnable
.forget());
111 nsresult
SecretDecoderRing::Encrypt(const nsACString
& data
,
112 /*out*/ nsACString
& result
) {
113 UniquePK11SlotInfo
slot(PK11_GetInternalKeySlot());
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
);
125 /* Force authentication */
126 if (PK11_Authenticate(slot
.get(), true, ctx
) != SECSuccess
) {
127 return NS_ERROR_FAILURE
;
130 /* Use default key id */
132 keyid
.data
= nullptr;
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
);
146 nsresult
SecretDecoderRing::Decrypt(const nsACString
& data
,
147 /*out*/ nsACString
& result
) {
148 /* Find token with SDR key */
149 UniquePK11SlotInfo
slot(PK11_GetInternalKeySlot());
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
;
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
);
173 SecretDecoderRing::EncryptString(const nsACString
& text
,
174 /*out*/ nsACString
& encryptedBase64Text
) {
175 nsAutoCString encryptedText
;
176 nsresult rv
= Encrypt(text
, encryptedText
);
181 rv
= Base64Encode(encryptedText
, encryptedBase64Text
);
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
;
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
);
215 nsCOMPtr
<nsIEventTarget
> target(
216 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
));
218 return NS_ERROR_FAILURE
;
220 nsresult rv
= target
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
221 if (NS_WARN_IF(NS_FAILED(rv
))) {
225 promise
.forget(aPromise
);
230 SecretDecoderRing::DecryptString(const nsACString
& encryptedBase64Text
,
231 /*out*/ nsACString
& decryptedText
) {
232 nsAutoCString encryptedText
;
233 nsresult rv
= Base64Decode(encryptedBase64Text
, encryptedText
);
238 rv
= Decrypt(encryptedText
, decryptedText
);
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
;
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
);
273 nsCOMPtr
<nsIEventTarget
> target(
274 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
));
276 return NS_ERROR_FAILURE
;
278 nsresult rv
= target
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
279 if (NS_WARN_IF(NS_FAILED(rv
))) {
283 promise
.forget(aPromise
);
288 SecretDecoderRing::ChangePassword() {
289 UniquePK11SlotInfo
slot(PK11_GetInternalKeySlot());
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
);
306 nsCOMPtr
<nsIInterfaceRequestor
> ctx
= new PipUIContext();
307 bool canceled
; // Ignored
308 return dialogs
->SetPassword(ctx
, token
, &canceled
);
312 SecretDecoderRing::Logout() {
314 nsCOMPtr
<nsINSSComponent
> nssComponent(do_GetService(NS_NSSCOMPONENT_CID
));
316 return NS_ERROR_NOT_AVAILABLE
;
318 return nssComponent
->ClearSSLExternalAndInternalSessionCache();
322 SecretDecoderRing::LogoutAndTeardown() {
324 nsCOMPtr
<nsINSSComponent
> nssComponent(do_GetService(NS_NSSCOMPONENT_CID
));
326 return NS_ERROR_NOT_AVAILABLE
;
329 // LogoutAuthenticatedPK11 also clears the SSL caches.
330 nsresult rv
= nssComponent
->LogoutAuthenticatedPK11();
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
338 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
340 os
->NotifyObservers(nullptr, "net:prune-dead-connections", nullptr);