1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsIIdentityCryptoService.h"
8 #include "nsServiceManagerUtils.h"
9 #include "nsIThread.h"
10 #include "nsThreadUtils.h"
11 #include "nsCOMPtr.h"
12 #include "nsProxyRelease.h"
13 #include "nsString.h"
14 #include "mozilla/ArrayUtils.h" // ArrayLength
15 #include "mozilla/Base64.h"
16 #include "mozilla/Components.h"
17 #include "ScopedNSSTypes.h"
18 #include "NSSErrorsService.h"
19
20 #include "nss.h"
21 #include "pk11pub.h"
22 #include "secmod.h"
23 #include "secerr.h"
24 #include "keyhi.h"
25 #include "cryptohi.h"
26
27 #include <limits.h>
28
29 using namespace mozilla;
30
31 namespace {
32
HexEncode(const SECItem * it,nsACString & result)33 void HexEncode(const SECItem* it, nsACString& result) {
34 static const char digits[] = "0123456789ABCDEF";
35 result.SetLength(it->len * 2);
36 char* p = result.BeginWriting();
37 for (unsigned int i = 0; i < it->len; ++i) {
38 *p++ = digits[it->data[i] >> 4];
39 *p++ = digits[it->data[i] & 0x0f];
40 }
41 }
42
43 #define DSA_KEY_TYPE_STRING (NS_LITERAL_CSTRING("DS160"))
44 #define RSA_KEY_TYPE_STRING (NS_LITERAL_CSTRING("RS256"))
45
46 class KeyPair : public nsIIdentityKeyPair {
47 public:
48 NS_DECL_THREADSAFE_ISUPPORTS
49 NS_DECL_NSIIDENTITYKEYPAIR
50
51 KeyPair(SECKEYPrivateKey* aPrivateKey, SECKEYPublicKey* aPublicKey,
52 nsIEventTarget* aOperationThread);
53
54 private:
~KeyPair()55 virtual ~KeyPair() {
56 if (mPrivateKey) {
57 SECKEY_DestroyPrivateKey(mPrivateKey);
58 }
59 if (mPublicKey) {
60 SECKEY_DestroyPublicKey(mPublicKey);
61 }
62 }
63
64 SECKEYPrivateKey* mPrivateKey;
65 SECKEYPublicKey* mPublicKey;
66 nsCOMPtr<nsIEventTarget> mThread;
67
68 KeyPair(const KeyPair&) = delete;
69 void operator=(const KeyPair&) = delete;
70 };
71
72 NS_IMPL_ISUPPORTS(KeyPair, nsIIdentityKeyPair)
73
74 class KeyGenRunnable : public Runnable {
75 public:
76 NS_DECL_NSIRUNNABLE
77
78 KeyGenRunnable(KeyType keyType, nsIIdentityKeyGenCallback* aCallback,
79 nsIEventTarget* aOperationThread);
80
81 private:
82 const KeyType mKeyType; // in
83 nsMainThreadPtrHandle<nsIIdentityKeyGenCallback> mCallback; // in
84 nsresult mRv; // out
85 nsCOMPtr<nsIIdentityKeyPair> mKeyPair; // out
86 nsCOMPtr<nsIEventTarget> mThread;
87
88 KeyGenRunnable(const KeyGenRunnable&) = delete;
89 void operator=(const KeyGenRunnable&) = delete;
90 };
91
92 class SignRunnable : public Runnable {
93 public:
94 NS_DECL_NSIRUNNABLE
95
96 SignRunnable(const nsACString& textToSign, SECKEYPrivateKey* privateKey,
97 nsIIdentitySignCallback* aCallback);
98
99 private:
~SignRunnable()100 ~SignRunnable() override {
101 if (mPrivateKey) {
102 SECKEY_DestroyPrivateKey(mPrivateKey);
103 }
104 }
105
106 const nsCString mTextToSign; // in
107 SECKEYPrivateKey* mPrivateKey; // in
108 nsMainThreadPtrHandle<nsIIdentitySignCallback> mCallback; // in
109 nsresult mRv; // out
110 nsCString mSignature; // out
111
112 SignRunnable(const SignRunnable&) = delete;
113 void operator=(const SignRunnable&) = delete;
114 };
115
116 class IdentityCryptoService final : public nsIIdentityCryptoService {
117 public:
118 NS_DECL_THREADSAFE_ISUPPORTS
119 NS_DECL_NSIIDENTITYCRYPTOSERVICE
120
121 IdentityCryptoService() = default;
Init()122 nsresult Init() {
123 nsresult rv;
124 nsCOMPtr<nsISupports> dummyUsedToEnsureNSSIsInitialized =
125 do_GetService("@mozilla.org/psm;1", &rv);
126 NS_ENSURE_SUCCESS(rv, rv);
127
128 nsCOMPtr<nsIThread> thread;
129 rv = NS_NewNamedThread("IdentityCrypto", getter_AddRefs(thread));
130 NS_ENSURE_SUCCESS(rv, rv);
131
132 mThread = std::move(thread);
133
134 return NS_OK;
135 }
136
137 private:
138 ~IdentityCryptoService() = default;
139 IdentityCryptoService(const KeyPair&) = delete;
140 void operator=(const IdentityCryptoService&) = delete;
141
142 nsCOMPtr<nsIEventTarget> mThread;
143 };
144
NS_IMPL_ISUPPORTS(IdentityCryptoService,nsIIdentityCryptoService)145 NS_IMPL_ISUPPORTS(IdentityCryptoService, nsIIdentityCryptoService)
146
147 NS_IMETHODIMP
148 IdentityCryptoService::GenerateKeyPair(const nsACString& keyTypeString,
149 nsIIdentityKeyGenCallback* callback) {
150 KeyType keyType;
151 if (keyTypeString.Equals(RSA_KEY_TYPE_STRING)) {
152 keyType = rsaKey;
153 } else if (keyTypeString.Equals(DSA_KEY_TYPE_STRING)) {
154 keyType = dsaKey;
155 } else {
156 return NS_ERROR_UNEXPECTED;
157 }
158
159 nsCOMPtr<nsIRunnable> r = new KeyGenRunnable(keyType, callback, mThread);
160 nsresult rv = mThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
161 NS_ENSURE_SUCCESS(rv, rv);
162
163 return NS_OK;
164 }
165
166 NS_IMETHODIMP
Base64UrlEncode(const nsACString & utf8Input,nsACString & result)167 IdentityCryptoService::Base64UrlEncode(const nsACString& utf8Input,
168 nsACString& result) {
169 return Base64URLEncode(
170 utf8Input.Length(),
171 reinterpret_cast<const uint8_t*>(utf8Input.BeginReading()),
172 Base64URLEncodePaddingPolicy::Include, result);
173 }
174
KeyPair(SECKEYPrivateKey * privateKey,SECKEYPublicKey * publicKey,nsIEventTarget * operationThread)175 KeyPair::KeyPair(SECKEYPrivateKey* privateKey, SECKEYPublicKey* publicKey,
176 nsIEventTarget* operationThread)
177 : mPrivateKey(privateKey), mPublicKey(publicKey), mThread(operationThread) {
178 MOZ_ASSERT(!NS_IsMainThread());
179 }
180
181 NS_IMETHODIMP
GetHexRSAPublicKeyExponent(nsACString & result)182 KeyPair::GetHexRSAPublicKeyExponent(nsACString& result) {
183 MOZ_ASSERT(NS_IsMainThread());
184 NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
185 NS_ENSURE_TRUE(mPublicKey->keyType == rsaKey, NS_ERROR_NOT_AVAILABLE);
186 HexEncode(&mPublicKey->u.rsa.publicExponent, result);
187 return NS_OK;
188 }
189
190 NS_IMETHODIMP
GetHexRSAPublicKeyModulus(nsACString & result)191 KeyPair::GetHexRSAPublicKeyModulus(nsACString& result) {
192 MOZ_ASSERT(NS_IsMainThread());
193 NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
194 NS_ENSURE_TRUE(mPublicKey->keyType == rsaKey, NS_ERROR_NOT_AVAILABLE);
195 HexEncode(&mPublicKey->u.rsa.modulus, result);
196 return NS_OK;
197 }
198
199 NS_IMETHODIMP
GetHexDSAPrime(nsACString & result)200 KeyPair::GetHexDSAPrime(nsACString& result) {
201 MOZ_ASSERT(NS_IsMainThread());
202 NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
203 NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE);
204 HexEncode(&mPublicKey->u.dsa.params.prime, result);
205 return NS_OK;
206 }
207
208 NS_IMETHODIMP
GetHexDSASubPrime(nsACString & result)209 KeyPair::GetHexDSASubPrime(nsACString& result) {
210 MOZ_ASSERT(NS_IsMainThread());
211 NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
212 NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE);
213 HexEncode(&mPublicKey->u.dsa.params.subPrime, result);
214 return NS_OK;
215 }
216
217 NS_IMETHODIMP
GetHexDSAGenerator(nsACString & result)218 KeyPair::GetHexDSAGenerator(nsACString& result) {
219 MOZ_ASSERT(NS_IsMainThread());
220 NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
221 NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE);
222 HexEncode(&mPublicKey->u.dsa.params.base, result);
223 return NS_OK;
224 }
225
226 NS_IMETHODIMP
GetHexDSAPublicValue(nsACString & result)227 KeyPair::GetHexDSAPublicValue(nsACString& result) {
228 MOZ_ASSERT(NS_IsMainThread());
229 NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
230 NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE);
231 HexEncode(&mPublicKey->u.dsa.publicValue, result);
232 return NS_OK;
233 }
234
235 NS_IMETHODIMP
GetKeyType(nsACString & result)236 KeyPair::GetKeyType(nsACString& result) {
237 MOZ_ASSERT(NS_IsMainThread());
238 NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE);
239
240 switch (mPublicKey->keyType) {
241 case rsaKey:
242 result = RSA_KEY_TYPE_STRING;
243 return NS_OK;
244 case dsaKey:
245 result = DSA_KEY_TYPE_STRING;
246 return NS_OK;
247 default:
248 return NS_ERROR_UNEXPECTED;
249 }
250 }
251
252 NS_IMETHODIMP
Sign(const nsACString & textToSign,nsIIdentitySignCallback * callback)253 KeyPair::Sign(const nsACString& textToSign, nsIIdentitySignCallback* callback) {
254 MOZ_ASSERT(NS_IsMainThread());
255 nsCOMPtr<nsIRunnable> r = new SignRunnable(textToSign, mPrivateKey, callback);
256
257 return mThread->Dispatch(r, NS_DISPATCH_NORMAL);
258 }
259
KeyGenRunnable(KeyType keyType,nsIIdentityKeyGenCallback * callback,nsIEventTarget * operationThread)260 KeyGenRunnable::KeyGenRunnable(KeyType keyType,
261 nsIIdentityKeyGenCallback* callback,
262 nsIEventTarget* operationThread)
263 : mozilla::Runnable("KeyGenRunnable"),
264 mKeyType(keyType),
265 mCallback(new nsMainThreadPtrHolder<nsIIdentityKeyGenCallback>(
266 "KeyGenRunnable::mCallback", callback)),
267 mRv(NS_ERROR_NOT_INITIALIZED),
268 mThread(operationThread) {}
269
GenerateKeyPair(PK11SlotInfo * slot,SECKEYPrivateKey ** privateKey,SECKEYPublicKey ** publicKey,CK_MECHANISM_TYPE mechanism,void * params)270 MOZ_MUST_USE nsresult GenerateKeyPair(PK11SlotInfo* slot,
271 SECKEYPrivateKey** privateKey,
272 SECKEYPublicKey** publicKey,
273 CK_MECHANISM_TYPE mechanism,
274 void* params) {
275 *publicKey = nullptr;
276 *privateKey = PK11_GenerateKeyPair(
277 slot, mechanism, params, publicKey, PR_FALSE /*isPerm*/,
278 PR_TRUE /*isSensitive*/, nullptr /*&pwdata*/);
279 if (!*privateKey) {
280 MOZ_ASSERT(!*publicKey);
281 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
282 }
283 if (!*publicKey) {
284 SECKEY_DestroyPrivateKey(*privateKey);
285 *privateKey = nullptr;
286 MOZ_CRASH("PK11_GnerateKeyPair returned private key without public key");
287 }
288
289 return NS_OK;
290 }
291
GenerateRSAKeyPair(PK11SlotInfo * slot,SECKEYPrivateKey ** privateKey,SECKEYPublicKey ** publicKey)292 MOZ_MUST_USE nsresult GenerateRSAKeyPair(PK11SlotInfo* slot,
293 SECKEYPrivateKey** privateKey,
294 SECKEYPublicKey** publicKey) {
295 MOZ_ASSERT(!NS_IsMainThread());
296
297 PK11RSAGenParams rsaParams;
298 rsaParams.keySizeInBits = 2048;
299 rsaParams.pe = 0x10001;
300 return GenerateKeyPair(slot, privateKey, publicKey, CKM_RSA_PKCS_KEY_PAIR_GEN,
301 &rsaParams);
302 }
303
GenerateDSAKeyPair(PK11SlotInfo * slot,SECKEYPrivateKey ** privateKey,SECKEYPublicKey ** publicKey)304 MOZ_MUST_USE nsresult GenerateDSAKeyPair(PK11SlotInfo* slot,
305 SECKEYPrivateKey** privateKey,
306 SECKEYPublicKey** publicKey) {
307 MOZ_ASSERT(!NS_IsMainThread());
308
309 // XXX: These could probably be static const arrays, but this way we avoid
310 // compiler warnings and also we avoid having to worry much about whether the
311 // functions that take these inputs will (unexpectedly) modify them.
312
313 // Using NIST parameters. Some other BrowserID components require that these
314 // exact parameters are used.
315 uint8_t P[] = {
316 0xFF, 0x60, 0x04, 0x83, 0xDB, 0x6A, 0xBF, 0xC5, 0xB4, 0x5E, 0xAB, 0x78,
317 0x59, 0x4B, 0x35, 0x33, 0xD5, 0x50, 0xD9, 0xF1, 0xBF, 0x2A, 0x99, 0x2A,
318 0x7A, 0x8D, 0xAA, 0x6D, 0xC3, 0x4F, 0x80, 0x45, 0xAD, 0x4E, 0x6E, 0x0C,
319 0x42, 0x9D, 0x33, 0x4E, 0xEE, 0xAA, 0xEF, 0xD7, 0xE2, 0x3D, 0x48, 0x10,
320 0xBE, 0x00, 0xE4, 0xCC, 0x14, 0x92, 0xCB, 0xA3, 0x25, 0xBA, 0x81, 0xFF,
321 0x2D, 0x5A, 0x5B, 0x30, 0x5A, 0x8D, 0x17, 0xEB, 0x3B, 0xF4, 0xA0, 0x6A,
322 0x34, 0x9D, 0x39, 0x2E, 0x00, 0xD3, 0x29, 0x74, 0x4A, 0x51, 0x79, 0x38,
323 0x03, 0x44, 0xE8, 0x2A, 0x18, 0xC4, 0x79, 0x33, 0x43, 0x8F, 0x89, 0x1E,
324 0x22, 0xAE, 0xEF, 0x81, 0x2D, 0x69, 0xC8, 0xF7, 0x5E, 0x32, 0x6C, 0xB7,
325 0x0E, 0xA0, 0x00, 0xC3, 0xF7, 0x76, 0xDF, 0xDB, 0xD6, 0x04, 0x63, 0x8C,
326 0x2E, 0xF7, 0x17, 0xFC, 0x26, 0xD0, 0x2E, 0x17};
327
328 uint8_t Q[] = {0xE2, 0x1E, 0x04, 0xF9, 0x11, 0xD1, 0xED, 0x79, 0x91, 0x00,
329 0x8E, 0xCA, 0xAB, 0x3B, 0xF7, 0x75, 0x98, 0x43, 0x09, 0xC3};
330
331 uint8_t G[] = {
332 0xC5, 0x2A, 0x4A, 0x0F, 0xF3, 0xB7, 0xE6, 0x1F, 0xDF, 0x18, 0x67, 0xCE,
333 0x84, 0x13, 0x83, 0x69, 0xA6, 0x15, 0x4F, 0x4A, 0xFA, 0x92, 0x96, 0x6E,
334 0x3C, 0x82, 0x7E, 0x25, 0xCF, 0xA6, 0xCF, 0x50, 0x8B, 0x90, 0xE5, 0xDE,
335 0x41, 0x9E, 0x13, 0x37, 0xE0, 0x7A, 0x2E, 0x9E, 0x2A, 0x3C, 0xD5, 0xDE,
336 0xA7, 0x04, 0xD1, 0x75, 0xF8, 0xEB, 0xF6, 0xAF, 0x39, 0x7D, 0x69, 0xE1,
337 0x10, 0xB9, 0x6A, 0xFB, 0x17, 0xC7, 0xA0, 0x32, 0x59, 0x32, 0x9E, 0x48,
338 0x29, 0xB0, 0xD0, 0x3B, 0xBC, 0x78, 0x96, 0xB1, 0x5B, 0x4A, 0xDE, 0x53,
339 0xE1, 0x30, 0x85, 0x8C, 0xC3, 0x4D, 0x96, 0x26, 0x9A, 0xA8, 0x90, 0x41,
340 0xF4, 0x09, 0x13, 0x6C, 0x72, 0x42, 0xA3, 0x88, 0x95, 0xC9, 0xD5, 0xBC,
341 0xCA, 0xD4, 0xF3, 0x89, 0xAF, 0x1D, 0x7A, 0x4B, 0xD1, 0x39, 0x8B, 0xD0,
342 0x72, 0xDF, 0xFA, 0x89, 0x62, 0x33, 0x39, 0x7A};
343
344 static_assert(MOZ_ARRAY_LENGTH(P) == 1024 / CHAR_BIT, "bad DSA P");
345 static_assert(MOZ_ARRAY_LENGTH(Q) == 160 / CHAR_BIT, "bad DSA Q");
346 static_assert(MOZ_ARRAY_LENGTH(G) == 1024 / CHAR_BIT, "bad DSA G");
347
348 PQGParams pqgParams = {
349 nullptr /*arena*/,
350 {siBuffer, P, static_cast<unsigned int>(mozilla::ArrayLength(P))},
351 {siBuffer, Q, static_cast<unsigned int>(mozilla::ArrayLength(Q))},
352 {siBuffer, G, static_cast<unsigned int>(mozilla::ArrayLength(G))}};
353
354 return GenerateKeyPair(slot, privateKey, publicKey, CKM_DSA_KEY_PAIR_GEN,
355 &pqgParams);
356 }
357
358 NS_IMETHODIMP
Run()359 KeyGenRunnable::Run() {
360 if (!NS_IsMainThread()) {
361 // We always want to use the internal slot for BrowserID; in particular,
362 // we want to avoid smartcard slots.
363 PK11SlotInfo* slot = PK11_GetInternalSlot();
364 if (!slot) {
365 mRv = NS_ERROR_UNEXPECTED;
366 } else {
367 SECKEYPrivateKey* privk = nullptr;
368 SECKEYPublicKey* pubk = nullptr;
369
370 switch (mKeyType) {
371 case rsaKey:
372 mRv = GenerateRSAKeyPair(slot, &privk, &pubk);
373 break;
374 case dsaKey:
375 mRv = GenerateDSAKeyPair(slot, &privk, &pubk);
376 break;
377 default:
378 MOZ_CRASH("unknown key type");
379 }
380
381 PK11_FreeSlot(slot);
382
383 if (NS_SUCCEEDED(mRv)) {
384 MOZ_ASSERT(privk);
385 MOZ_ASSERT(pubk);
386 // mKeyPair will take over ownership of privk and pubk
387 mKeyPair = new KeyPair(privk, pubk, mThread);
388 }
389 }
390
391 NS_DispatchToMainThread(this);
392 } else {
393 // Back on Main Thread
394 (void)mCallback->GenerateKeyPairFinished(mRv, mKeyPair);
395 }
396 return NS_OK;
397 }
398
SignRunnable(const nsACString & aText,SECKEYPrivateKey * privateKey,nsIIdentitySignCallback * aCallback)399 SignRunnable::SignRunnable(const nsACString& aText,
400 SECKEYPrivateKey* privateKey,
401 nsIIdentitySignCallback* aCallback)
402 : mozilla::Runnable("SignRunnable"),
403 mTextToSign(aText),
404 mPrivateKey(SECKEY_CopyPrivateKey(privateKey)),
405 mCallback(new nsMainThreadPtrHolder<nsIIdentitySignCallback>(
406 "SignRunnable::mCallback", aCallback)),
407 mRv(NS_ERROR_NOT_INITIALIZED) {}
408
409 NS_IMETHODIMP
Run()410 SignRunnable::Run() {
411 if (!NS_IsMainThread()) {
412 // We need the output in PKCS#11 format, not DER encoding, so we must use
413 // PK11_HashBuf and PK11_Sign instead of SEC_SignData.
414
415 SECItem sig = {siBuffer, nullptr, 0};
416 int sigLength = PK11_SignatureLen(mPrivateKey);
417 if (sigLength <= 0) {
418 mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
419 } else if (!SECITEM_AllocItem(nullptr, &sig, sigLength)) {
420 mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
421 } else {
422 uint8_t hash[32]; // big enough for SHA-1 or SHA-256
423 SECOidTag hashAlg =
424 mPrivateKey->keyType == dsaKey ? SEC_OID_SHA1 : SEC_OID_SHA256;
425 SECItem hashItem = {siBuffer, hash, hashAlg == SEC_OID_SHA1 ? 20u : 32u};
426
427 mRv = MapSECStatus(
428 PK11_HashBuf(hashAlg, hash,
429 const_cast<uint8_t*>(
430 reinterpret_cast<const uint8_t*>(mTextToSign.get())),
431 mTextToSign.Length()));
432 if (NS_SUCCEEDED(mRv)) {
433 mRv = MapSECStatus(PK11_Sign(mPrivateKey, &sig, &hashItem));
434 }
435 if (NS_SUCCEEDED(mRv)) {
436 mRv =
437 Base64URLEncode(sig.len, sig.data,
438 Base64URLEncodePaddingPolicy::Include, mSignature);
439 }
440 SECITEM_FreeItem(&sig, false);
441 }
442
443 NS_DispatchToMainThread(this);
444 } else {
445 // Back on Main Thread
446 (void)mCallback->SignFinished(mRv, mSignature);
447 }
448
449 return NS_OK;
450 }
451 } // unnamed namespace
452
453 // XPCOM module registration
454
NS_IMPL_COMPONENT_FACTORY(nsIIdentityCryptoService)455 NS_IMPL_COMPONENT_FACTORY(nsIIdentityCryptoService) {
456 auto inst = MakeRefPtr<IdentityCryptoService>();
457 if (NS_SUCCEEDED(inst->Init())) {
458 return inst.forget().downcast<nsIIdentityCryptoService>();
459 }
460 return nullptr;
461 }
462