diff options
author | wolfbeast <mcwerewolf@wolfbeast.com> | 2020-04-26 12:56:46 +0200 |
---|---|---|
committer | wolfbeast <mcwerewolf@wolfbeast.com> | 2020-04-26 12:56:46 +0200 |
commit | 98fe8b0ccbea37c2681ad8c788baa91554871f4d (patch) | |
tree | 0e269a27661fea619ab07a533e0c1fa195c3c671 /toolkit/identity | |
parent | e08a4ad1c8096fc8de64fac4f36cf0c569cb4420 (diff) | |
parent | 519030e688dd7075a7cd9f14dd6fb9e210b5a689 (diff) | |
download | UXP-4b8656a4eaa0fe8758cbddf2910089395bdfd1d6.tar UXP-4b8656a4eaa0fe8758cbddf2910089395bdfd1d6.tar.gz UXP-4b8656a4eaa0fe8758cbddf2910089395bdfd1d6.tar.lz UXP-4b8656a4eaa0fe8758cbddf2910089395bdfd1d6.tar.xz UXP-4b8656a4eaa0fe8758cbddf2910089395bdfd1d6.zip |
Merge branch 'redwood' into 28.9-platformRELBASE_20200426
Diffstat (limited to 'toolkit/identity')
-rw-r--r-- | toolkit/identity/Identity.jsm | 309 | ||||
-rw-r--r-- | toolkit/identity/IdentityCryptoService.cpp | 571 | ||||
-rw-r--r-- | toolkit/identity/IdentityProvider.jsm | 496 | ||||
-rw-r--r-- | toolkit/identity/IdentityStore.jsm | 97 | ||||
-rw-r--r-- | toolkit/identity/IdentityUtils.jsm | 111 | ||||
-rw-r--r-- | toolkit/identity/LogUtils.jsm | 103 | ||||
-rw-r--r-- | toolkit/identity/MinimalIdentity.jsm | 242 | ||||
-rw-r--r-- | toolkit/identity/RelyingParty.jsm | 367 | ||||
-rw-r--r-- | toolkit/identity/Sandbox.jsm | 152 | ||||
-rw-r--r-- | toolkit/identity/jwcrypto.jsm | 180 | ||||
-rw-r--r-- | toolkit/identity/moz.build | 25 | ||||
-rw-r--r-- | toolkit/identity/nsIIdentityCryptoService.idl | 106 |
12 files changed, 0 insertions, 2759 deletions
diff --git a/toolkit/identity/Identity.jsm b/toolkit/identity/Identity.jsm deleted file mode 100644 index a27b7c93d..000000000 --- a/toolkit/identity/Identity.jsm +++ /dev/null @@ -1,309 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["IdentityService"]; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/identity/LogUtils.jsm"); -Cu.import("resource://gre/modules/identity/IdentityStore.jsm"); -Cu.import("resource://gre/modules/identity/RelyingParty.jsm"); -Cu.import("resource://gre/modules/identity/IdentityProvider.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, - "jwcrypto", - "resource://gre/modules/identity/jwcrypto.jsm"); - -function log(...aMessageArgs) { - Logger.log.apply(Logger, ["core"].concat(aMessageArgs)); -} -function reportError(...aMessageArgs) { - Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs)); -} - -function IDService() { - Services.obs.addObserver(this, "quit-application-granted", false); - Services.obs.addObserver(this, "identity-auth-complete", false); - - this._store = IdentityStore; - this.RP = RelyingParty; - this.IDP = IdentityProvider; -} - -IDService.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), - - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { - case "quit-application-granted": - Services.obs.removeObserver(this, "quit-application-granted"); - this.shutdown(); - break; - case "identity-auth-complete": - if (!aSubject || !aSubject.wrappedJSObject) - break; - let subject = aSubject.wrappedJSObject; - log("Auth complete:", aSubject.wrappedJSObject); - // We have authenticated in order to provision an identity. - // So try again. - this.selectIdentity(subject.rpId, subject.identity); - break; - } - }, - - reset: function reset() { - // Explicitly call reset() on our RP and IDP classes. - // This is here to make testing easier. When the - // quit-application-granted signal is emitted, reset() will be - // called here, on RP, on IDP, and on the store. So you don't - // need to use this :) - this._store.reset(); - this.RP.reset(); - this.IDP.reset(); - }, - - shutdown: function shutdown() { - log("shutdown"); - Services.obs.removeObserver(this, "identity-auth-complete"); - // try to prevent abort/crash during shutdown of mochitest-browser2... - try { - Services.obs.removeObserver(this, "quit-application-granted"); - } catch (e) {} - }, - - /** - * Parse an email into username and domain if it is valid, else return null - */ - parseEmail: function parseEmail(email) { - var match = email.match(/^([^@]+)@([^@^/]+.[a-z]+)$/); - if (match) { - return { - username: match[1], - domain: match[2] - }; - } - return null; - }, - - /** - * The UX wants to add a new identity - * often followed by selectIdentity() - * - * @param aIdentity - * (string) the email chosen for login - */ - addIdentity: function addIdentity(aIdentity) { - if (this._store.fetchIdentity(aIdentity) === null) { - this._store.addIdentity(aIdentity, null, null); - } - }, - - /** - * The UX comes back and calls selectIdentity once the user has picked - * an identity. - * - * @param aRPId - * (integer) the id of the doc object obtained in .watch() and - * passed to the UX component. - * - * @param aIdentity - * (string) the email chosen for login - */ - selectIdentity: function selectIdentity(aRPId, aIdentity) { - log("selectIdentity: RP id:", aRPId, "identity:", aIdentity); - - // Get the RP that was stored when watch() was invoked. - let rp = this.RP._rpFlows[aRPId]; - if (!rp) { - reportError("selectIdentity", "Invalid RP id: ", aRPId); - return; - } - - // It's possible that we are in the process of provisioning an - // identity. - let provId = rp.provId; - - let rpLoginOptions = { - loggedInUser: aIdentity, - origin: rp.origin - }; - log("selectIdentity: provId:", provId, "origin:", rp.origin); - - // Once we have a cert, and once the user is authenticated with the - // IdP, we can generate an assertion and deliver it to the doc. - let self = this; - this.RP._generateAssertion(rp.origin, aIdentity, function hadReadyAssertion(err, assertion) { - if (!err && assertion) { - self.RP._doLogin(rp, rpLoginOptions, assertion); - return; - - } - // Need to provision an identity first. Begin by discovering - // the user's IdP. - self._discoverIdentityProvider(aIdentity, function gotIDP(err, idpParams) { - if (err) { - rp.doError(err); - return; - } - - // The idpParams tell us where to go to provision and authenticate - // the identity. - self.IDP._provisionIdentity(aIdentity, idpParams, provId, function gotID(err, aProvId) { - - // Provision identity may have created a new provision flow - // for us. To make it easier to relate provision flows with - // RP callers, we cross index the two here. - rp.provId = aProvId; - self.IDP._provisionFlows[aProvId].rpId = aRPId; - - // At this point, we already have a cert. If the user is also - // already authenticated with the IdP, then we can try again - // to generate an assertion and login. - if (err) { - // We are not authenticated. If we have already tried to - // authenticate and failed, then this is a "hard fail" and - // we give up. Otherwise we try to authenticate with the - // IdP. - - if (self.IDP._provisionFlows[aProvId].didAuthentication) { - self.IDP._cleanUpProvisionFlow(aProvId); - self.RP._cleanUpProvisionFlow(aRPId, aProvId); - log("ERROR: selectIdentity: authentication hard fail"); - rp.doError("Authentication fail."); - return; - } - // Try to authenticate with the IdP. Note that we do - // not clean up the provision flow here. We will continue - // to use it. - self.IDP._doAuthentication(aProvId, idpParams); - return; - } - - // Provisioning flows end when a certificate has been registered. - // Thus IdentityProvider's registerCertificate() cleans up the - // current provisioning flow. We only do this here on error. - self.RP._generateAssertion(rp.origin, aIdentity, function gotAssertion(err, assertion) { - if (err) { - rp.doError(err); - return; - } - self.RP._doLogin(rp, rpLoginOptions, assertion); - self.RP._cleanUpProvisionFlow(aRPId, aProvId); - return; - }); - }); - }); - }); - }, - - // methods for chrome and add-ons - - /** - * Discover the IdP for an identity - * - * @param aIdentity - * (string) the email we're logging in with - * - * @param aCallback - * (function) callback to invoke on completion - * with first-positional parameter the error. - */ - _discoverIdentityProvider: function _discoverIdentityProvider(aIdentity, aCallback) { - // XXX bug 767610 - validate email address call - // When that is available, we can remove this custom parser - var parsedEmail = this.parseEmail(aIdentity); - if (parsedEmail === null) { - aCallback("Could not parse email: " + aIdentity); - return; - } - log("_discoverIdentityProvider: identity:", aIdentity, "domain:", parsedEmail.domain); - - this._fetchWellKnownFile(parsedEmail.domain, function fetchedWellKnown(err, idpParams) { - // idpParams includes the pk, authorization url, and - // provisioning url. - - // XXX bug 769861 follow any authority delegations - // if no well-known at any point in the delegation - // fall back to browserid.org as IdP - return aCallback(err, idpParams); - }); - }, - - /** - * Fetch the well-known file from the domain. - * - * @param aDomain - * - * @param aScheme - * (string) (optional) Protocol to use. Default is https. - * This is necessary because we are unable to test - * https. - * - * @param aCallback - * - */ - _fetchWellKnownFile: function _fetchWellKnownFile(aDomain, aCallback, aScheme='https') { - // XXX bug 769854 make tests https and remove aScheme option - let url = aScheme + '://' + aDomain + "/.well-known/browserid"; - log("_fetchWellKnownFile:", url); - - // this appears to be a more successful way to get at xmlhttprequest (which supposedly will close with a window - let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Ci.nsIXMLHttpRequest); - - // XXX bug 769865 gracefully handle being off-line - // XXX bug 769866 decide on how to handle redirects - req.open("GET", url, true); - req.responseType = "json"; - req.mozBackgroundRequest = true; - req.onload = function _fetchWellKnownFile_onload() { - if (req.status < 200 || req.status >= 400) { - log("_fetchWellKnownFile", url, ": server returned status:", req.status); - return aCallback("Error"); - } - try { - let idpParams = req.response; - - // Verify that the IdP returned a valid configuration - if (! (idpParams.provisioning && - idpParams.authentication && - idpParams['public-key'])) { - let errStr= "Invalid well-known file from: " + aDomain; - log("_fetchWellKnownFile:", errStr); - return aCallback(errStr); - } - - let callbackObj = { - domain: aDomain, - idpParams: idpParams, - }; - log("_fetchWellKnownFile result: ", callbackObj); - // Yay. Valid IdP configuration for the domain. - return aCallback(null, callbackObj); - - } catch (err) { - reportError("_fetchWellKnownFile", "Bad configuration from", aDomain, err); - return aCallback(err.toString()); - } - }; - req.onerror = function _fetchWellKnownFile_onerror() { - log("_fetchWellKnownFile", "ERROR:", req.status, req.statusText); - log("ERROR: _fetchWellKnownFile:", err); - return aCallback("Error"); - }; - req.send(null); - }, - -}; - -this.IdentityService = new IDService(); diff --git a/toolkit/identity/IdentityCryptoService.cpp b/toolkit/identity/IdentityCryptoService.cpp deleted file mode 100644 index cafe07999..000000000 --- a/toolkit/identity/IdentityCryptoService.cpp +++ /dev/null @@ -1,571 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIIdentityCryptoService.h" -#include "mozilla/ModuleUtils.h" -#include "nsServiceManagerUtils.h" -#include "nsNSSShutDown.h" -#include "nsIThread.h" -#include "nsThreadUtils.h" -#include "nsCOMPtr.h" -#include "nsProxyRelease.h" -#include "nsString.h" -#include "mozilla/ArrayUtils.h" // ArrayLength -#include "mozilla/Base64.h" -#include "ScopedNSSTypes.h" -#include "NSSErrorsService.h" - -#include "nss.h" -#include "pk11pub.h" -#include "secmod.h" -#include "secerr.h" -#include "keyhi.h" -#include "cryptohi.h" - -#include <limits.h> - -using namespace mozilla; - -namespace { - -void -HexEncode(const SECItem * it, nsACString & result) -{ - const char * digits = "0123456789ABCDEF"; - result.SetCapacity((it->len * 2) + 1); - result.SetLength(it->len * 2); - char * p = result.BeginWriting(); - for (unsigned int i = 0; i < it->len; ++i) { - *p++ = digits[it->data[i] >> 4]; - *p++ = digits[it->data[i] & 0x0f]; - } -} - -#define DSA_KEY_TYPE_STRING (NS_LITERAL_CSTRING("DS160")) -#define RSA_KEY_TYPE_STRING (NS_LITERAL_CSTRING("RS256")) - -class KeyPair : public nsIIdentityKeyPair, public nsNSSShutDownObject -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIIDENTITYKEYPAIR - - KeyPair(SECKEYPrivateKey* aPrivateKey, SECKEYPublicKey* aPublicKey, - nsIEventTarget* aOperationThread); - -private: - ~KeyPair() - { - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) { - return; - } - destructorSafeDestroyNSSReference(); - shutdown(ShutdownCalledFrom::Object); - } - - void virtualDestroyNSSReference() override - { - destructorSafeDestroyNSSReference(); - } - - void destructorSafeDestroyNSSReference() - { - SECKEY_DestroyPrivateKey(mPrivateKey); - mPrivateKey = nullptr; - SECKEY_DestroyPublicKey(mPublicKey); - mPublicKey = nullptr; - } - - SECKEYPrivateKey * mPrivateKey; - SECKEYPublicKey * mPublicKey; - nsCOMPtr<nsIEventTarget> mThread; - - KeyPair(const KeyPair &) = delete; - void operator=(const KeyPair &) = delete; -}; - -NS_IMPL_ISUPPORTS(KeyPair, nsIIdentityKeyPair) - -class KeyGenRunnable : public Runnable, public nsNSSShutDownObject -{ -public: - NS_DECL_NSIRUNNABLE - - KeyGenRunnable(KeyType keyType, nsIIdentityKeyGenCallback * aCallback, - nsIEventTarget* aOperationThread); - -private: - ~KeyGenRunnable() - { - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) { - return; - } - destructorSafeDestroyNSSReference(); - shutdown(ShutdownCalledFrom::Object); - } - - virtual void virtualDestroyNSSReference() override - { - destructorSafeDestroyNSSReference(); - } - - void destructorSafeDestroyNSSReference() - { - } - - const KeyType mKeyType; // in - nsMainThreadPtrHandle<nsIIdentityKeyGenCallback> mCallback; // in - nsresult mRv; // out - nsCOMPtr<nsIIdentityKeyPair> mKeyPair; // out - nsCOMPtr<nsIEventTarget> mThread; - - KeyGenRunnable(const KeyGenRunnable &) = delete; - void operator=(const KeyGenRunnable &) = delete; -}; - -class SignRunnable : public Runnable, public nsNSSShutDownObject -{ -public: - NS_DECL_NSIRUNNABLE - - SignRunnable(const nsACString & textToSign, SECKEYPrivateKey * privateKey, - nsIIdentitySignCallback * aCallback); - -private: - ~SignRunnable() - { - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) { - return; - } - destructorSafeDestroyNSSReference(); - shutdown(ShutdownCalledFrom::Object); - } - - void virtualDestroyNSSReference() override - { - destructorSafeDestroyNSSReference(); - } - - void destructorSafeDestroyNSSReference() - { - SECKEY_DestroyPrivateKey(mPrivateKey); - mPrivateKey = nullptr; - } - - const nsCString mTextToSign; // in - SECKEYPrivateKey* mPrivateKey; // in - nsMainThreadPtrHandle<nsIIdentitySignCallback> mCallback; // in - nsresult mRv; // out - nsCString mSignature; // out - -private: - SignRunnable(const SignRunnable &) = delete; - void operator=(const SignRunnable &) = delete; -}; - -class IdentityCryptoService final : public nsIIdentityCryptoService -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIIDENTITYCRYPTOSERVICE - - IdentityCryptoService() { } - nsresult Init() - { - nsresult rv; - nsCOMPtr<nsISupports> dummyUsedToEnsureNSSIsInitialized - = do_GetService("@mozilla.org/psm;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIThread> thread; - rv = NS_NewNamedThread("IdentityCrypto", getter_AddRefs(thread)); - NS_ENSURE_SUCCESS(rv, rv); - - mThread = thread.forget(); - - return NS_OK; - } - -private: - ~IdentityCryptoService() { } - IdentityCryptoService(const KeyPair &) = delete; - void operator=(const IdentityCryptoService &) = delete; - - nsCOMPtr<nsIEventTarget> mThread; -}; - -NS_IMPL_ISUPPORTS(IdentityCryptoService, nsIIdentityCryptoService) - -NS_IMETHODIMP -IdentityCryptoService::GenerateKeyPair( - const nsACString & keyTypeString, nsIIdentityKeyGenCallback * callback) -{ - KeyType keyType; - if (keyTypeString.Equals(RSA_KEY_TYPE_STRING)) { - keyType = rsaKey; - } else if (keyTypeString.Equals(DSA_KEY_TYPE_STRING)) { - keyType = dsaKey; - } else { - return NS_ERROR_UNEXPECTED; - } - - nsCOMPtr<nsIRunnable> r = new KeyGenRunnable(keyType, callback, mThread); - nsresult rv = mThread->Dispatch(r.forget(), NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -IdentityCryptoService::Base64UrlEncode(const nsACString & utf8Input, - nsACString & result) -{ - return Base64URLEncode(utf8Input.Length(), - reinterpret_cast<const uint8_t*>(utf8Input.BeginReading()), - Base64URLEncodePaddingPolicy::Include, result); -} - -KeyPair::KeyPair(SECKEYPrivateKey * privateKey, SECKEYPublicKey * publicKey, - nsIEventTarget* operationThread) - : mPrivateKey(privateKey) - , mPublicKey(publicKey) - , mThread(operationThread) -{ - MOZ_ASSERT(!NS_IsMainThread()); -} - -NS_IMETHODIMP -KeyPair::GetHexRSAPublicKeyExponent(nsACString & result) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE); - NS_ENSURE_TRUE(mPublicKey->keyType == rsaKey, NS_ERROR_NOT_AVAILABLE); - HexEncode(&mPublicKey->u.rsa.publicExponent, result); - return NS_OK; -} - -NS_IMETHODIMP -KeyPair::GetHexRSAPublicKeyModulus(nsACString & result) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE); - NS_ENSURE_TRUE(mPublicKey->keyType == rsaKey, NS_ERROR_NOT_AVAILABLE); - HexEncode(&mPublicKey->u.rsa.modulus, result); - return NS_OK; -} - -NS_IMETHODIMP -KeyPair::GetHexDSAPrime(nsACString & result) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE); - NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE); - HexEncode(&mPublicKey->u.dsa.params.prime, result); - return NS_OK; -} - -NS_IMETHODIMP -KeyPair::GetHexDSASubPrime(nsACString & result) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE); - NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE); - HexEncode(&mPublicKey->u.dsa.params.subPrime, result); - return NS_OK; -} - -NS_IMETHODIMP -KeyPair::GetHexDSAGenerator(nsACString & result) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE); - NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE); - HexEncode(&mPublicKey->u.dsa.params.base, result); - return NS_OK; -} - -NS_IMETHODIMP -KeyPair::GetHexDSAPublicValue(nsACString & result) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE); - NS_ENSURE_TRUE(mPublicKey->keyType == dsaKey, NS_ERROR_NOT_AVAILABLE); - HexEncode(&mPublicKey->u.dsa.publicValue, result); - return NS_OK; -} - -NS_IMETHODIMP -KeyPair::GetKeyType(nsACString & result) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_ENSURE_TRUE(mPublicKey, NS_ERROR_NOT_AVAILABLE); - - switch (mPublicKey->keyType) { - case rsaKey: result = RSA_KEY_TYPE_STRING; return NS_OK; - case dsaKey: result = DSA_KEY_TYPE_STRING; return NS_OK; - default: return NS_ERROR_UNEXPECTED; - } -} - -NS_IMETHODIMP -KeyPair::Sign(const nsACString & textToSign, - nsIIdentitySignCallback* callback) -{ - MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsIRunnable> r = new SignRunnable(textToSign, mPrivateKey, - callback); - - return mThread->Dispatch(r, NS_DISPATCH_NORMAL); -} - -KeyGenRunnable::KeyGenRunnable(KeyType keyType, - nsIIdentityKeyGenCallback * callback, - nsIEventTarget* operationThread) - : mKeyType(keyType) - , mCallback(new nsMainThreadPtrHolder<nsIIdentityKeyGenCallback>(callback)) - , mRv(NS_ERROR_NOT_INITIALIZED) - , mThread(operationThread) -{ -} - -MOZ_MUST_USE nsresult -GenerateKeyPair(PK11SlotInfo * slot, - SECKEYPrivateKey ** privateKey, - SECKEYPublicKey ** publicKey, - CK_MECHANISM_TYPE mechanism, - void * params) -{ - *publicKey = nullptr; - *privateKey = PK11_GenerateKeyPair(slot, mechanism, params, publicKey, - PR_FALSE /*isPerm*/, - PR_TRUE /*isSensitive*/, - nullptr /*&pwdata*/); - if (!*privateKey) { - MOZ_ASSERT(!*publicKey); - return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); - } - if (!*publicKey) { - SECKEY_DestroyPrivateKey(*privateKey); - *privateKey = nullptr; - MOZ_CRASH("PK11_GnerateKeyPair returned private key without public key"); - } - - return NS_OK; -} - - -MOZ_MUST_USE nsresult -GenerateRSAKeyPair(PK11SlotInfo * slot, - SECKEYPrivateKey ** privateKey, - SECKEYPublicKey ** publicKey) -{ - MOZ_ASSERT(!NS_IsMainThread()); - - PK11RSAGenParams rsaParams; - rsaParams.keySizeInBits = 2048; - rsaParams.pe = 0x10001; - return GenerateKeyPair(slot, privateKey, publicKey, CKM_RSA_PKCS_KEY_PAIR_GEN, - &rsaParams); -} - -MOZ_MUST_USE nsresult -GenerateDSAKeyPair(PK11SlotInfo * slot, - SECKEYPrivateKey ** privateKey, - SECKEYPublicKey ** publicKey) -{ - MOZ_ASSERT(!NS_IsMainThread()); - - // XXX: These could probably be static const arrays, but this way we avoid - // compiler warnings and also we avoid having to worry much about whether the - // functions that take these inputs will (unexpectedly) modify them. - - // Using NIST parameters. Some other BrowserID components require that these - // exact parameters are used. - uint8_t P[] = { - 0xFF,0x60,0x04,0x83,0xDB,0x6A,0xBF,0xC5,0xB4,0x5E,0xAB,0x78, - 0x59,0x4B,0x35,0x33,0xD5,0x50,0xD9,0xF1,0xBF,0x2A,0x99,0x2A, - 0x7A,0x8D,0xAA,0x6D,0xC3,0x4F,0x80,0x45,0xAD,0x4E,0x6E,0x0C, - 0x42,0x9D,0x33,0x4E,0xEE,0xAA,0xEF,0xD7,0xE2,0x3D,0x48,0x10, - 0xBE,0x00,0xE4,0xCC,0x14,0x92,0xCB,0xA3,0x25,0xBA,0x81,0xFF, - 0x2D,0x5A,0x5B,0x30,0x5A,0x8D,0x17,0xEB,0x3B,0xF4,0xA0,0x6A, - 0x34,0x9D,0x39,0x2E,0x00,0xD3,0x29,0x74,0x4A,0x51,0x79,0x38, - 0x03,0x44,0xE8,0x2A,0x18,0xC4,0x79,0x33,0x43,0x8F,0x89,0x1E, - 0x22,0xAE,0xEF,0x81,0x2D,0x69,0xC8,0xF7,0x5E,0x32,0x6C,0xB7, - 0x0E,0xA0,0x00,0xC3,0xF7,0x76,0xDF,0xDB,0xD6,0x04,0x63,0x8C, - 0x2E,0xF7,0x17,0xFC,0x26,0xD0,0x2E,0x17 - }; - - uint8_t Q[] = { - 0xE2,0x1E,0x04,0xF9,0x11,0xD1,0xED,0x79,0x91,0x00,0x8E,0xCA, - 0xAB,0x3B,0xF7,0x75,0x98,0x43,0x09,0xC3 - }; - - uint8_t G[] = { - 0xC5,0x2A,0x4A,0x0F,0xF3,0xB7,0xE6,0x1F,0xDF,0x18,0x67,0xCE, - 0x84,0x13,0x83,0x69,0xA6,0x15,0x4F,0x4A,0xFA,0x92,0x96,0x6E, - 0x3C,0x82,0x7E,0x25,0xCF,0xA6,0xCF,0x50,0x8B,0x90,0xE5,0xDE, - 0x41,0x9E,0x13,0x37,0xE0,0x7A,0x2E,0x9E,0x2A,0x3C,0xD5,0xDE, - 0xA7,0x04,0xD1,0x75,0xF8,0xEB,0xF6,0xAF,0x39,0x7D,0x69,0xE1, - 0x10,0xB9,0x6A,0xFB,0x17,0xC7,0xA0,0x32,0x59,0x32,0x9E,0x48, - 0x29,0xB0,0xD0,0x3B,0xBC,0x78,0x96,0xB1,0x5B,0x4A,0xDE,0x53, - 0xE1,0x30,0x85,0x8C,0xC3,0x4D,0x96,0x26,0x9A,0xA8,0x90,0x41, - 0xF4,0x09,0x13,0x6C,0x72,0x42,0xA3,0x88,0x95,0xC9,0xD5,0xBC, - 0xCA,0xD4,0xF3,0x89,0xAF,0x1D,0x7A,0x4B,0xD1,0x39,0x8B,0xD0, - 0x72,0xDF,0xFA,0x89,0x62,0x33,0x39,0x7A - }; - - static_assert(MOZ_ARRAY_LENGTH(P) == 1024 / CHAR_BIT, "bad DSA P"); - static_assert(MOZ_ARRAY_LENGTH(Q) == 160 / CHAR_BIT, "bad DSA Q"); - static_assert(MOZ_ARRAY_LENGTH(G) == 1024 / CHAR_BIT, "bad DSA G"); - - PQGParams pqgParams = { - nullptr /*arena*/, - { siBuffer, P, static_cast<unsigned int>(mozilla::ArrayLength(P)) }, - { siBuffer, Q, static_cast<unsigned int>(mozilla::ArrayLength(Q)) }, - { siBuffer, G, static_cast<unsigned int>(mozilla::ArrayLength(G)) } - }; - - return GenerateKeyPair(slot, privateKey, publicKey, CKM_DSA_KEY_PAIR_GEN, - &pqgParams); -} - -NS_IMETHODIMP -KeyGenRunnable::Run() -{ - if (!NS_IsMainThread()) { - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) { - mRv = NS_ERROR_NOT_AVAILABLE; - } else { - // We always want to use the internal slot for BrowserID; in particular, - // we want to avoid smartcard slots. - PK11SlotInfo *slot = PK11_GetInternalSlot(); - if (!slot) { - mRv = NS_ERROR_UNEXPECTED; - } else { - SECKEYPrivateKey *privk = nullptr; - SECKEYPublicKey *pubk = nullptr; - - switch (mKeyType) { - case rsaKey: - mRv = GenerateRSAKeyPair(slot, &privk, &pubk); - break; - case dsaKey: - mRv = GenerateDSAKeyPair(slot, &privk, &pubk); - break; - default: - MOZ_CRASH("unknown key type"); - } - - PK11_FreeSlot(slot); - - if (NS_SUCCEEDED(mRv)) { - MOZ_ASSERT(privk); - MOZ_ASSERT(pubk); - // mKeyPair will take over ownership of privk and pubk - mKeyPair = new KeyPair(privk, pubk, mThread); - } - } - } - - NS_DispatchToMainThread(this); - } else { - // Back on Main Thread - (void) mCallback->GenerateKeyPairFinished(mRv, mKeyPair); - } - return NS_OK; -} - -SignRunnable::SignRunnable(const nsACString & aText, - SECKEYPrivateKey * privateKey, - nsIIdentitySignCallback * aCallback) - : mTextToSign(aText) - , mPrivateKey(SECKEY_CopyPrivateKey(privateKey)) - , mCallback(new nsMainThreadPtrHolder<nsIIdentitySignCallback>(aCallback)) - , mRv(NS_ERROR_NOT_INITIALIZED) -{ -} - -NS_IMETHODIMP -SignRunnable::Run() -{ - if (!NS_IsMainThread()) { - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) { - mRv = NS_ERROR_NOT_AVAILABLE; - } else { - // We need the output in PKCS#11 format, not DER encoding, so we must use - // PK11_HashBuf and PK11_Sign instead of SEC_SignData. - - SECItem sig = { siBuffer, nullptr, 0 }; - int sigLength = PK11_SignatureLen(mPrivateKey); - if (sigLength <= 0) { - mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); - } else if (!SECITEM_AllocItem(nullptr, &sig, sigLength)) { - mRv = mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); - } else { - uint8_t hash[32]; // big enough for SHA-1 or SHA-256 - SECOidTag hashAlg = mPrivateKey->keyType == dsaKey ? SEC_OID_SHA1 - : SEC_OID_SHA256; - SECItem hashItem = { siBuffer, hash, - hashAlg == SEC_OID_SHA1 ? 20u : 32u }; - - mRv = MapSECStatus(PK11_HashBuf(hashAlg, hash, - const_cast<uint8_t*>(reinterpret_cast<const uint8_t *>( - mTextToSign.get())), - mTextToSign.Length())); - if (NS_SUCCEEDED(mRv)) { - mRv = MapSECStatus(PK11_Sign(mPrivateKey, &sig, &hashItem)); - } - if (NS_SUCCEEDED(mRv)) { - mRv = Base64URLEncode(sig.len, sig.data, - Base64URLEncodePaddingPolicy::Include, - mSignature); - } - SECITEM_FreeItem(&sig, false); - } - } - - NS_DispatchToMainThread(this); - } else { - // Back on Main Thread - (void) mCallback->SignFinished(mRv, mSignature); - } - - return NS_OK; -} - -// XPCOM module registration - -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(IdentityCryptoService, Init) - -#define NS_IDENTITYCRYPTOSERVICE_CID \ - {0xbea13a3a, 0x44e8, 0x4d7f, {0xa0, 0xa2, 0x2c, 0x67, 0xf8, 0x4e, 0x3a, 0x97}} - -NS_DEFINE_NAMED_CID(NS_IDENTITYCRYPTOSERVICE_CID); - -const mozilla::Module::CIDEntry kCIDs[] = { - { &kNS_IDENTITYCRYPTOSERVICE_CID, false, nullptr, IdentityCryptoServiceConstructor }, - { nullptr } -}; - -const mozilla::Module::ContractIDEntry kContracts[] = { - { "@mozilla.org/identity/crypto-service;1", &kNS_IDENTITYCRYPTOSERVICE_CID }, - { nullptr } -}; - -const mozilla::Module kModule = { - mozilla::Module::kVersion, - kCIDs, - kContracts -}; - -} // unnamed namespace - -NSMODULE_DEFN(identity) = &kModule; diff --git a/toolkit/identity/IdentityProvider.jsm b/toolkit/identity/IdentityProvider.jsm deleted file mode 100644 index 11529bfba..000000000 --- a/toolkit/identity/IdentityProvider.jsm +++ /dev/null @@ -1,496 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/identity/LogUtils.jsm"); -Cu.import("resource://gre/modules/identity/Sandbox.jsm"); - -this.EXPORTED_SYMBOLS = ["IdentityProvider"]; -const FALLBACK_PROVIDER = "browserid.org"; - -XPCOMUtils.defineLazyModuleGetter(this, - "jwcrypto", - "resource://gre/modules/identity/jwcrypto.jsm"); - -function log(...aMessageArgs) { - Logger.log.apply(Logger, ["IDP"].concat(aMessageArgs)); -} -function reportError(...aMessageArgs) { - Logger.reportError.apply(Logger, ["IDP"].concat(aMessageArgs)); -} - - -function IdentityProviderService() { - XPCOMUtils.defineLazyModuleGetter(this, - "_store", - "resource://gre/modules/identity/IdentityStore.jsm", - "IdentityStore"); - - this.reset(); -} - -IdentityProviderService.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), - _sandboxConfigured: false, - - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { - case "quit-application-granted": - Services.obs.removeObserver(this, "quit-application-granted"); - this.shutdown(); - break; - } - }, - - reset: function IDP_reset() { - // Clear the provisioning flows. Provision flows contain an - // identity, idpParams (how to reach the IdP to provision and - // authenticate), a callback (a completion callback for when things - // are done), and a provisioningFrame (which is the provisioning - // sandbox). Additionally, two callbacks will be attached: - // beginProvisioningCallback and genKeyPairCallback. - this._provisionFlows = {}; - - // Clear the authentication flows. Authentication flows attach - // to provision flows. In the process of provisioning an id, it - // may be necessary to authenticate with an IdP. The authentication - // flow maintains the state of that authentication process. - this._authenticationFlows = {}; - }, - - getProvisionFlow: function getProvisionFlow(aProvId, aErrBack) { - let provFlow = this._provisionFlows[aProvId]; - if (provFlow) { - return provFlow; - } - - let err = "No provisioning flow found with id " + aProvId; - log("ERROR:", err); - if (typeof aErrBack === 'function') { - aErrBack(err); - } - - return undefined; - }, - - shutdown: function RP_shutdown() { - this.reset(); - - if (this._sandboxConfigured) { - // Tear down message manager listening on the hidden window - Cu.import("resource://gre/modules/DOMIdentity.jsm"); - DOMIdentity._configureMessages(Services.appShell.hiddenDOMWindow, false); - this._sandboxConfigured = false; - } - - Services.obs.removeObserver(this, "quit-application-granted"); - }, - - get securityLevel() { - return 1; - }, - - get certDuration() { - switch (this.securityLevel) { - default: - return 3600; - } - }, - - /** - * Provision an Identity - * - * @param aIdentity - * (string) the email we're logging in with - * - * @param aIDPParams - * (object) parameters of the IdP - * - * @param aCallback - * (function) callback to invoke on completion - * with first-positional parameter the error. - */ - _provisionIdentity: function _provisionIdentity(aIdentity, aIDPParams, aProvId, aCallback) { - let provPath = aIDPParams.idpParams.provisioning; - let url = Services.io.newURI("https://" + aIDPParams.domain, null, null).resolve(provPath); - log("_provisionIdentity: identity:", aIdentity, "url:", url); - - // If aProvId is not null, then we already have a flow - // with a sandbox. Otherwise, get a sandbox and create a - // new provision flow. - - if (aProvId) { - // Re-use an existing sandbox - log("_provisionIdentity: re-using sandbox in provisioning flow with id:", aProvId); - this._provisionFlows[aProvId].provisioningSandbox.reload(); - - } else { - this._createProvisioningSandbox(url, function createdSandbox(aSandbox) { - // create a provisioning flow, using the sandbox id, and - // stash callback associated with this provisioning workflow. - - let provId = aSandbox.id; - this._provisionFlows[provId] = { - identity: aIdentity, - idpParams: aIDPParams, - securityLevel: this.securityLevel, - provisioningSandbox: aSandbox, - callback: function doCallback(aErr) { - aCallback(aErr, provId); - }, - }; - - log("_provisionIdentity: Created sandbox and provisioning flow with id:", provId); - // XXX bug 769862 - provisioning flow should timeout after N seconds - - }.bind(this)); - } - }, - - // DOM Methods - /** - * the provisioning iframe sandbox has called navigator.id.beginProvisioning() - * - * @param aCaller - * (object) the iframe sandbox caller with all callbacks and - * other information. Callbacks include: - * - doBeginProvisioningCallback(id, duration_s) - * - doGenKeyPairCallback(pk) - */ - beginProvisioning: function beginProvisioning(aCaller) { - log("beginProvisioning:", aCaller.id); - - // Expect a flow for this caller already to be underway. - let provFlow = this.getProvisionFlow(aCaller.id, aCaller.doError); - - // keep the caller object around - provFlow.caller = aCaller; - - let identity = provFlow.identity; - let frame = provFlow.provisioningFrame; - - // Determine recommended length of cert. - let duration = this.certDuration; - - // Make a record that we have begun provisioning. This is required - // for genKeyPair. - provFlow.didBeginProvisioning = true; - - // Let the sandbox know to invoke the callback to beginProvisioning with - // the identity and cert length. - return aCaller.doBeginProvisioningCallback(identity, duration); - }, - - /** - * the provisioning iframe sandbox has called - * navigator.id.raiseProvisioningFailure() - * - * @param aProvId - * (int) the identifier of the provisioning flow tied to that sandbox - * @param aReason - */ - raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) { - reportError("Provisioning failure", aReason); - - // look up the provisioning caller and its callback - let provFlow = this.getProvisionFlow(aProvId); - - // Sandbox is deleted in _cleanUpProvisionFlow in case we re-use it. - - // This may be either a "soft" or "hard" fail. If it's a - // soft fail, we'll flow through setAuthenticationFlow, where - // the provision flow data will be copied into a new auth - // flow. If it's a hard fail, then the callback will be - // responsible for cleaning up the now defunct provision flow. - - // invoke the callback with an error. - provFlow.callback(aReason); - }, - - /** - * When navigator.id.genKeyPair is called from provisioning iframe sandbox. - * Generates a keypair for the current user being provisioned. - * - * @param aProvId - * (int) the identifier of the provisioning caller tied to that sandbox - * - * It is an error to call genKeypair without receiving the callback for - * the beginProvisioning() call first. - */ - genKeyPair: function genKeyPair(aProvId) { - // Look up the provisioning caller and make sure it's valid. - let provFlow = this.getProvisionFlow(aProvId); - - if (!provFlow.didBeginProvisioning) { - let errStr = "ERROR: genKeyPair called before beginProvisioning"; - log(errStr); - provFlow.callback(errStr); - return; - } - - // Ok generate a keypair - jwcrypto.generateKeyPair(jwcrypto.ALGORITHMS.DS160, function gkpCb(err, kp) { - log("in gkp callback"); - if (err) { - log("ERROR: genKeyPair:", err); - provFlow.callback(err); - return; - } - - provFlow.kp = kp; - - // Serialize the publicKey of the keypair and send it back to the - // sandbox. - log("genKeyPair: generated keypair for provisioning flow with id:", aProvId); - provFlow.caller.doGenKeyPairCallback(provFlow.kp.serializedPublicKey); - }.bind(this)); - }, - - /** - * When navigator.id.registerCertificate is called from provisioning iframe - * sandbox. - * - * Sets the certificate for the user for which a certificate was requested - * via a preceding call to beginProvisioning (and genKeypair). - * - * @param aProvId - * (integer) the identifier of the provisioning caller tied to that - * sandbox - * - * @param aCert - * (String) A JWT representing the signed certificate for the user - * being provisioned, provided by the IdP. - */ - registerCertificate: function registerCertificate(aProvId, aCert) { - log("registerCertificate:", aProvId, aCert); - - // look up provisioning caller, make sure it's valid. - let provFlow = this.getProvisionFlow(aProvId); - - if (!provFlow.caller) { - reportError("registerCertificate", "No provision flow or caller"); - return; - } - if (!provFlow.kp) { - let errStr = "Cannot register a certificate without a keypair"; - reportError("registerCertificate", errStr); - provFlow.callback(errStr); - return; - } - - // store the keypair and certificate just provided in IDStore. - this._store.addIdentity(provFlow.identity, provFlow.kp, aCert); - - // Great success! - provFlow.callback(null); - - // Clean up the flow. - this._cleanUpProvisionFlow(aProvId); - }, - - /** - * Begin the authentication process with an IdP - * - * @param aProvId - * (int) the identifier of the provisioning flow which failed - * - * @param aCallback - * (function) to invoke upon completion, with - * first-positional-param error. - */ - _doAuthentication: function _doAuthentication(aProvId, aIDPParams) { - log("_doAuthentication: provId:", aProvId, "idpParams:", aIDPParams); - // create an authentication caller and its identifier AuthId - // stash aIdentity, idpparams, and callback in it. - - // extract authentication URL from idpParams - let authPath = aIDPParams.idpParams.authentication; - let authURI = Services.io.newURI("https://" + aIDPParams.domain, null, null).resolve(authPath); - - // beginAuthenticationFlow causes the "identity-auth" topic to be - // observed. Since it's sending a notification to the DOM, there's - // no callback. We wait for the DOM to trigger the next phase of - // provisioning. - this._beginAuthenticationFlow(aProvId, authURI); - - // either we bind the AuthID to the sandbox ourselves, or UX does that, - // in which case we need to tell UX the AuthId. - // Currently, the UX creates the UI and gets the AuthId from the window - // and sets is with setAuthenticationFlow - }, - - /** - * The authentication frame has called navigator.id.beginAuthentication - * - * IMPORTANT: the aCaller is *always* non-null, even if this is called from - * a regular content page. We have to make sure, on every DOM call, that - * aCaller is an expected authentication-flow identifier. If not, we throw - * an error or something. - * - * @param aCaller - * (object) the authentication caller - * - */ - beginAuthentication: function beginAuthentication(aCaller) { - log("beginAuthentication: caller id:", aCaller.id); - - // Begin the authentication flow after having concluded a provisioning - // flow. The aCaller that the DOM gives us will have the same ID as - // the provisioning flow we just concluded. (see setAuthenticationFlow) - let authFlow = this._authenticationFlows[aCaller.id]; - if (!authFlow) { - return aCaller.doError("beginAuthentication: no flow for caller id", aCaller.id); - } - - authFlow.caller = aCaller; - - let identity = this._provisionFlows[authFlow.provId].identity; - - // tell the UI to start the authentication process - log("beginAuthentication: authFlow:", aCaller.id, "identity:", identity); - return authFlow.caller.doBeginAuthenticationCallback(identity); - }, - - /** - * The auth frame has called navigator.id.completeAuthentication - * - * @param aAuthId - * (int) the identifier of the authentication caller tied to that sandbox - * - */ - completeAuthentication: function completeAuthentication(aAuthId) { - log("completeAuthentication:", aAuthId); - - // look up the AuthId caller, and get its callback. - let authFlow = this._authenticationFlows[aAuthId]; - if (!authFlow) { - reportError("completeAuthentication", "No auth flow with id", aAuthId); - return; - } - let provId = authFlow.provId; - - // delete caller - delete authFlow['caller']; - delete this._authenticationFlows[aAuthId]; - - let provFlow = this.getProvisionFlow(provId); - provFlow.didAuthentication = true; - let subject = { - rpId: provFlow.rpId, - identity: provFlow.identity, - }; - Services.obs.notifyObservers({ wrappedJSObject: subject }, "identity-auth-complete", aAuthId); - }, - - /** - * The auth frame has called navigator.id.cancelAuthentication - * - * @param aAuthId - * (int) the identifier of the authentication caller - * - */ - cancelAuthentication: function cancelAuthentication(aAuthId) { - log("cancelAuthentication:", aAuthId); - - // look up the AuthId caller, and get its callback. - let authFlow = this._authenticationFlows[aAuthId]; - if (!authFlow) { - reportError("cancelAuthentication", "No auth flow with id:", aAuthId); - return; - } - let provId = authFlow.provId; - - // delete caller - delete authFlow['caller']; - delete this._authenticationFlows[aAuthId]; - - let provFlow = this.getProvisionFlow(provId); - provFlow.didAuthentication = true; - Services.obs.notifyObservers(null, "identity-auth-complete", aAuthId); - - // invoke callback with ERROR. - let errStr = "Authentication canceled by IDP"; - log("ERROR: cancelAuthentication:", errStr); - provFlow.callback(errStr); - }, - - /** - * Called by the UI to set the ID and caller for the authentication flow after it gets its ID - */ - setAuthenticationFlow: function(aAuthId, aProvId) { - // this is the transition point between the two flows, - // provision and authenticate. We tell the auth flow which - // provisioning flow it is started from. - log("setAuthenticationFlow: authId:", aAuthId, "provId:", aProvId); - this._authenticationFlows[aAuthId] = { provId: aProvId }; - this._provisionFlows[aProvId].authId = aAuthId; - }, - - /** - * Load the provisioning URL in a hidden frame to start the provisioning - * process. - */ - _createProvisioningSandbox: function _createProvisioningSandbox(aURL, aCallback) { - log("_createProvisioningSandbox:", aURL); - - if (!this._sandboxConfigured) { - // Configure message manager listening on the hidden window - Cu.import("resource://gre/modules/DOMIdentity.jsm"); - DOMIdentity._configureMessages(Services.appShell.hiddenDOMWindow, true); - this._sandboxConfigured = true; - } - - new Sandbox(aURL, aCallback); - }, - - /** - * Load the authentication UI to start the authentication process. - */ - _beginAuthenticationFlow: function _beginAuthenticationFlow(aProvId, aURL) { - log("_beginAuthenticationFlow:", aProvId, aURL); - let propBag = {provId: aProvId}; - - Services.obs.notifyObservers({wrappedJSObject:propBag}, "identity-auth", aURL); - }, - - /** - * Clean up a provision flow and the authentication flow and sandbox - * that may be attached to it. - */ - _cleanUpProvisionFlow: function _cleanUpProvisionFlow(aProvId) { - log('_cleanUpProvisionFlow:', aProvId); - let prov = this._provisionFlows[aProvId]; - - // Clean up the sandbox, if there is one. - if (prov.provisioningSandbox) { - let sandbox = this._provisionFlows[aProvId]['provisioningSandbox']; - if (sandbox.free) { - log('_cleanUpProvisionFlow: freeing sandbox'); - sandbox.free(); - } - delete this._provisionFlows[aProvId]['provisioningSandbox']; - } - - // Clean up a related authentication flow, if there is one. - if (this._authenticationFlows[prov.authId]) { - delete this._authenticationFlows[prov.authId]; - } - - // Finally delete the provision flow - delete this._provisionFlows[aProvId]; - } - -}; - -this.IdentityProvider = new IdentityProviderService(); diff --git a/toolkit/identity/IdentityStore.jsm b/toolkit/identity/IdentityStore.jsm deleted file mode 100644 index 1827a839e..000000000 --- a/toolkit/identity/IdentityStore.jsm +++ /dev/null @@ -1,97 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -this.EXPORTED_SYMBOLS = ["IdentityStore"]; - -// the data store for IDService -// written as a separate thing so it can easily be mocked -function IDServiceStore() { - this.reset(); -} - -// Note: eventually these methods may be async, but we haven no need for this -// for now, since we're not storing to disk. -IDServiceStore.prototype = { - addIdentity: function addIdentity(aEmail, aKeyPair, aCert) { - this._identities[aEmail] = {keyPair: aKeyPair, cert: aCert}; - }, - fetchIdentity: function fetchIdentity(aEmail) { - return aEmail in this._identities ? this._identities[aEmail] : null; - }, - removeIdentity: function removeIdentity(aEmail) { - let data = this._identities[aEmail]; - delete this._identities[aEmail]; - return data; - }, - getIdentities: function getIdentities() { - // XXX - should clone? - return this._identities; - }, - clearCert: function clearCert(aEmail) { - // XXX - should remove key from store? - this._identities[aEmail].cert = null; - this._identities[aEmail].keyPair = null; - }, - - /** - * set the login state for a given origin - * - * @param aOrigin - * (string) a web origin - * - * @param aState - * (boolean) whether or not the user is logged in - * - * @param aEmail - * (email) the email address the user is logged in with, - * or, if not logged in, the default email for that origin. - */ - setLoginState: function setLoginState(aOrigin, aState, aEmail) { - if (aState && !aEmail) { - throw "isLoggedIn cannot be set to true without an email"; - } - return this._loginStates[aOrigin] = {isLoggedIn: aState, email: aEmail}; - }, - getLoginState: function getLoginState(aOrigin) { - return aOrigin in this._loginStates ? this._loginStates[aOrigin] : null; - }, - clearLoginState: function clearLoginState(aOrigin) { - delete this._loginStates[aOrigin]; - }, - - reset: function Store_reset() { - // _identities associates emails with keypairs and certificates - this._identities = {}; - - // _loginStates associates. remote origins with a login status and - // the email the user has chosen as his or her identity when logging - // into that origin. - this._loginStates = {}; - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), - - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { - case "quit-application-granted": - Services.obs.removeObserver(this, "quit-application-granted"); - this.reset(); - break; - } - }, -}; - -this.IdentityStore = new IDServiceStore(); diff --git a/toolkit/identity/IdentityUtils.jsm b/toolkit/identity/IdentityUtils.jsm deleted file mode 100644 index a34c0b133..000000000 --- a/toolkit/identity/IdentityUtils.jsm +++ /dev/null @@ -1,111 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// functions common to Identity.jsm and MinimalIdentity.jsm - -"use strict"; - -this.EXPORTED_SYMBOLS = [ - "checkDeprecated", - "checkRenamed", - "getRandomId", - "objectCopy", - "makeMessageObject", -]; - -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", - "@mozilla.org/uuid-generator;1", - "nsIUUIDGenerator"); - -XPCOMUtils.defineLazyModuleGetter(this, "Logger", - "resource://gre/modules/identity/LogUtils.jsm"); - -function log(...aMessageArgs) { - Logger.log.apply(Logger, ["Identity"].concat(aMessageArgs)); -} - -function defined(item) { - return typeof item !== 'undefined'; -} - -var checkDeprecated = this.checkDeprecated = function checkDeprecated(aOptions, aField) { - if (defined(aOptions[aField])) { - log("WARNING: field is deprecated:", aField); - return true; - } - return false; -}; - -this.checkRenamed = function checkRenamed(aOptions, aOldName, aNewName) { - if (defined(aOptions[aOldName]) && - defined(aOptions[aNewName])) { - let err = "You cannot provide both " + aOldName + " and " + aNewName; - Logger.reportError(err); - throw new Error(err); - } - - if (checkDeprecated(aOptions, aOldName)) { - aOptions[aNewName] = aOptions[aOldName]; - delete(aOptions[aOldName]); - } -}; - -this.getRandomId = function getRandomId() { - return uuidgen.generateUUID().toString(); -}; - -/* - * copy source object into target, excluding private properties - * (those whose names begin with an underscore) - */ -this.objectCopy = function objectCopy(source, target) { - let desc; - Object.getOwnPropertyNames(source).forEach(function(name) { - if (name[0] !== '_') { - desc = Object.getOwnPropertyDescriptor(source, name); - Object.defineProperty(target, name, desc); - } - }); -}; - -this.makeMessageObject = function makeMessageObject(aRpCaller) { - let options = {}; - - options.id = aRpCaller.id; - options.origin = aRpCaller.origin; - - // Backwards compatibility with Persona beta: - // loggedInUser can be undefined, null, or a string - options.loggedInUser = aRpCaller.loggedInUser; - - // Special flag for internal calls for Persona in b2g - options._internal = aRpCaller._internal; - - Object.keys(aRpCaller).forEach(function(option) { - // Duplicate the callerobject, scrubbing out functions and other - // internal variables (like _mm, the message manager object) - if (!Object.hasOwnProperty(this, option) - && option[0] !== '_' - && typeof aRpCaller[option] !== 'function') { - options[option] = aRpCaller[option]; - } - }); - - // check validity of message structure - if ((typeof options.id === 'undefined') || - (typeof options.origin === 'undefined')) { - let err = "id and origin required in relying-party message: " + JSON.stringify(options); - reportError(err); - throw new Error(err); - } - - return options; -} - diff --git a/toolkit/identity/LogUtils.jsm b/toolkit/identity/LogUtils.jsm deleted file mode 100644 index ec0f4c420..000000000 --- a/toolkit/identity/LogUtils.jsm +++ /dev/null @@ -1,103 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["Logger"]; -const PREF_DEBUG = "toolkit.identity.debug"; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -function IdentityLogger() { - Services.prefs.addObserver(PREF_DEBUG, this, false); - this._debug = Services.prefs.getBoolPref(PREF_DEBUG); - return this; -} - -IdentityLogger.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), - - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { - case "nsPref:changed": - this._debug = Services.prefs.getBoolPref(PREF_DEBUG); - break; - - case "quit-application-granted": - Services.prefs.removeObserver(PREF_DEBUG, this); - break; - - default: - this.log("Logger observer", "Unknown topic:", aTopic); - break; - } - }, - - _generateLogMessage: function _generateLogMessage(aPrefix, args) { - // create a string representation of a list of arbitrary things - let strings = []; - - // XXX bug 770418 - args look like flattened array, not list of strings - - args.forEach(function(arg) { - if (typeof arg === 'string') { - strings.push(arg); - } else if (typeof arg === 'undefined') { - strings.push('undefined'); - } else if (arg === null) { - strings.push('null'); - } else { - try { - strings.push(JSON.stringify(arg, null, 2)); - } catch (err) { - strings.push("<<something>>"); - } - } - }); - return 'Identity ' + aPrefix + ': ' + strings.join(' '); - }, - - /** - * log() - utility function to print a list of arbitrary things - * - * Enable with about:config pref toolkit.identity.debug - */ - log: function log(aPrefix, ...args) { - if (!this._debug) { - return; - } - let output = this._generateLogMessage(aPrefix, args); - dump(output + "\n"); - - // Additionally, make the output visible in the Error Console - Services.console.logStringMessage(output); - }, - - /** - * reportError() - report an error through component utils as well as - * our log function - */ - reportError: function reportError(aPrefix, ...aArgs) { - let prefix = aPrefix + ' ERROR'; - - // Report the error in the browser - let output = this._generateLogMessage(aPrefix, aArgs); - Cu.reportError(output); - dump("ERROR: " + output + "\n"); - for (let frame = Components.stack.caller; frame; frame = frame.caller) { - dump(frame + "\n"); - } - } - -}; - -this.Logger = new IdentityLogger(); diff --git a/toolkit/identity/MinimalIdentity.jsm b/toolkit/identity/MinimalIdentity.jsm deleted file mode 100644 index bceb65659..000000000 --- a/toolkit/identity/MinimalIdentity.jsm +++ /dev/null @@ -1,242 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * This alternate implementation of IdentityService provides just the - * channels for navigator.id, leaving the certificate storage to a - * server-provided app. - * - * On b2g, the messages identity-controller-watch, -request, and - * -logout, are observed by the component SignInToWebsite.jsm. - */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["IdentityService"]; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/identity/LogUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "objectCopy", - "resource://gre/modules/identity/IdentityUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject", - "resource://gre/modules/identity/IdentityUtils.jsm"); - -function log(...aMessageArgs) { - Logger.log.apply(Logger, ["minimal core"].concat(aMessageArgs)); -} -function reportError(...aMessageArgs) { - Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs)); -} - -function IDService() { - Services.obs.addObserver(this, "quit-application-granted", false); - - // simplify, it's one object - this.RP = this; - this.IDP = this; - - // keep track of flows - this._rpFlows = {}; - this._authFlows = {}; - this._provFlows = {}; -} - -IDService.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), - - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { - case "quit-application-granted": - this.shutdown(); - break; - } - }, - - shutdown: function() { - Services.obs.removeObserver(this, "quit-application-granted"); - }, - - /** - * Parse an email into username and domain if it is valid, else return null - */ - parseEmail: function parseEmail(email) { - var match = email.match(/^([^@]+)@([^@^/]+.[a-z]+)$/); - if (match) { - return { - username: match[1], - domain: match[2] - }; - } - return null; - }, - - /** - * Register a listener for a given windowID as a result of a call to - * navigator.id.watch(). - * - * @param aCaller - * (Object) an object that represents the caller document, and - * is expected to have properties: - * - id (unique, e.g. uuid) - * - loggedInUser (string or null) - * - origin (string) - * - * and a bunch of callbacks - * - doReady() - * - doLogin() - * - doLogout() - * - doError() - * - doCancel() - * - */ - watch: function watch(aRpCaller) { - // store the caller structure and notify the UI observers - this._rpFlows[aRpCaller.id] = aRpCaller; - - log("flows:", Object.keys(this._rpFlows).join(', ')); - - let options = makeMessageObject(aRpCaller); - log("sending identity-controller-watch:", options); - Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-watch", null); - }, - - /* - * The RP has gone away; remove handles to the hidden iframe. - * It's probable that the frame will already have been cleaned up. - */ - unwatch: function unwatch(aRpId, aTargetMM) { - let rp = this._rpFlows[aRpId]; - if (!rp) { - return; - } - - let options = makeMessageObject({ - id: aRpId, - origin: rp.origin, - messageManager: aTargetMM - }); - log("sending identity-controller-unwatch for id", options.id, options.origin); - Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-unwatch", null); - - // Stop sending messages to this window - delete this._rpFlows[aRpId]; - }, - - /** - * Initiate a login with user interaction as a result of a call to - * navigator.id.request(). - * - * @param aRPId - * (integer) the id of the doc object obtained in .watch() - * - * @param aOptions - * (Object) options including privacyPolicy, termsOfService - */ - request: function request(aRPId, aOptions) { - let rp = this._rpFlows[aRPId]; - if (!rp) { - reportError("request() called before watch()"); - return; - } - - // Notify UX to display identity picker. - // Pass the doc id to UX so it can pass it back to us later. - let options = makeMessageObject(rp); - objectCopy(aOptions, options); - Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-request", null); - }, - - /** - * Invoked when a user wishes to logout of a site (for instance, when clicking - * on an in-content logout button). - * - * @param aRpCallerId - * (integer) the id of the doc object obtained in .watch() - * - */ - logout: function logout(aRpCallerId) { - let rp = this._rpFlows[aRpCallerId]; - if (!rp) { - reportError("logout() called before watch()"); - return; - } - - let options = makeMessageObject(rp); - Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-logout", null); - }, - - childProcessShutdown: function childProcessShutdown(messageManager) { - Object.keys(this._rpFlows).forEach(function(key) { - if (this._rpFlows[key]._mm === messageManager) { - log("child process shutdown for rp", key, "- deleting flow"); - delete this._rpFlows[key]; - } - }, this); - }, - - /* - * once the UI-and-display-logic components have received - * notifications, they call back with direct invocation of the - * following functions (doLogin, doLogout, or doReady) - */ - - doLogin: function doLogin(aRpCallerId, aAssertion, aInternalParams) { - let rp = this._rpFlows[aRpCallerId]; - if (!rp) { - log("WARNING: doLogin found no rp to go with callerId " + aRpCallerId); - return; - } - - rp.doLogin(aAssertion, aInternalParams); - }, - - doLogout: function doLogout(aRpCallerId) { - let rp = this._rpFlows[aRpCallerId]; - if (!rp) { - log("WARNING: doLogout found no rp to go with callerId " + aRpCallerId); - return; - } - - // Logout from every site with the same origin - let origin = rp.origin; - Object.keys(this._rpFlows).forEach(function(key) { - let rp = this._rpFlows[key]; - if (rp.origin === origin) { - rp.doLogout(); - } - }.bind(this)); - }, - - doReady: function doReady(aRpCallerId) { - let rp = this._rpFlows[aRpCallerId]; - if (!rp) { - log("WARNING: doReady found no rp to go with callerId " + aRpCallerId); - return; - } - - rp.doReady(); - }, - - doCancel: function doCancel(aRpCallerId) { - let rp = this._rpFlows[aRpCallerId]; - if (!rp) { - log("WARNING: doCancel found no rp to go with callerId " + aRpCallerId); - return; - } - - rp.doCancel(); - } -}; - -this.IdentityService = new IDService(); diff --git a/toolkit/identity/RelyingParty.jsm b/toolkit/identity/RelyingParty.jsm deleted file mode 100644 index 5996383ca..000000000 --- a/toolkit/identity/RelyingParty.jsm +++ /dev/null @@ -1,367 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/identity/LogUtils.jsm"); -Cu.import("resource://gre/modules/identity/IdentityStore.jsm"); - -this.EXPORTED_SYMBOLS = ["RelyingParty"]; - -XPCOMUtils.defineLazyModuleGetter(this, "objectCopy", - "resource://gre/modules/identity/IdentityUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, - "jwcrypto", - "resource://gre/modules/identity/jwcrypto.jsm"); - -function log(...aMessageArgs) { - Logger.log.apply(Logger, ["RP"].concat(aMessageArgs)); -} -function reportError(...aMessageArgs) { - Logger.reportError.apply(Logger, ["RP"].concat(aMessageArgs)); -} - -function IdentityRelyingParty() { - // The store is a singleton shared among Identity, RelyingParty, and - // IdentityProvider. The Identity module takes care of resetting - // state in the _store on shutdown. - this._store = IdentityStore; - - this.reset(); -} - -IdentityRelyingParty.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), - - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { - case "quit-application-granted": - Services.obs.removeObserver(this, "quit-application-granted"); - this.shutdown(); - break; - - } - }, - - reset: function RP_reset() { - // Forget all documents that call in. (These are sometimes - // referred to as callers.) - this._rpFlows = {}; - }, - - shutdown: function RP_shutdown() { - this.reset(); - Services.obs.removeObserver(this, "quit-application-granted"); - }, - - /** - * Register a listener for a given windowID as a result of a call to - * navigator.id.watch(). - * - * @param aCaller - * (Object) an object that represents the caller document, and - * is expected to have properties: - * - id (unique, e.g. uuid) - * - loggedInUser (string or null) - * - origin (string) - * - * and a bunch of callbacks - * - doReady() - * - doLogin() - * - doLogout() - * - doError() - * - doCancel() - * - */ - watch: function watch(aRpCaller) { - this._rpFlows[aRpCaller.id] = aRpCaller; - let origin = aRpCaller.origin; - let state = this._store.getLoginState(origin) || { isLoggedIn: false, email: null }; - - log("watch: rpId:", aRpCaller.id, - "origin:", origin, - "loggedInUser:", aRpCaller.loggedInUser, - "loggedIn:", state.isLoggedIn, - "email:", state.email); - - // If the user is already logged in, then there are three cases - // to deal with: - // - // 1. the email is valid and unchanged: 'ready' - // 2. the email is null: 'login'; 'ready' - // 3. the email has changed: 'login'; 'ready' - if (state.isLoggedIn) { - if (state.email && aRpCaller.loggedInUser === state.email) { - this._notifyLoginStateChanged(aRpCaller.id, state.email); - return aRpCaller.doReady(); - - } else if (aRpCaller.loggedInUser === null) { - // Generate assertion for existing login - let options = {loggedInUser: state.email, origin: origin}; - return this._doLogin(aRpCaller, options); - } - // A loggedInUser different from state.email has been specified. - // Change login identity. - - let options = {loggedInUser: state.email, origin: origin}; - return this._doLogin(aRpCaller, options); - - // If the user is not logged in, there are two cases: - // - // 1. a logged in email was provided: 'ready'; 'logout' - // 2. not logged in, no email given: 'ready'; - - } - if (aRpCaller.loggedInUser) { - return this._doLogout(aRpCaller, {origin: origin}); - } - return aRpCaller.doReady(); - }, - - /** - * A utility for watch() to set state and notify the dom - * on login - * - * Note that this calls _getAssertion - */ - _doLogin: function _doLogin(aRpCaller, aOptions, aAssertion) { - log("_doLogin: rpId:", aRpCaller.id, "origin:", aOptions.origin); - - let loginWithAssertion = function loginWithAssertion(assertion) { - this._store.setLoginState(aOptions.origin, true, aOptions.loggedInUser); - this._notifyLoginStateChanged(aRpCaller.id, aOptions.loggedInUser); - aRpCaller.doLogin(assertion); - aRpCaller.doReady(); - }.bind(this); - - if (aAssertion) { - loginWithAssertion(aAssertion); - } else { - this._getAssertion(aOptions, function gotAssertion(err, assertion) { - if (err) { - reportError("_doLogin:", "Failed to get assertion on login attempt:", err); - this._doLogout(aRpCaller); - } else { - loginWithAssertion(assertion); - } - }.bind(this)); - } - }, - - /** - * A utility for watch() to set state and notify the dom - * on logout. - */ - _doLogout: function _doLogout(aRpCaller, aOptions) { - log("_doLogout: rpId:", aRpCaller.id, "origin:", aOptions.origin); - - let state = this._store.getLoginState(aOptions.origin) || {}; - - state.isLoggedIn = false; - this._notifyLoginStateChanged(aRpCaller.id, null); - - aRpCaller.doLogout(); - aRpCaller.doReady(); - }, - - /** - * For use with login or logout, emit 'identity-login-state-changed' - * - * The notification will send the rp caller id in the properties, - * and the email of the user in the message. - * - * @param aRpCallerId - * (integer) The id of the RP caller - * - * @param aIdentity - * (string) The email of the user whose login state has changed - */ - _notifyLoginStateChanged: function _notifyLoginStateChanged(aRpCallerId, aIdentity) { - log("_notifyLoginStateChanged: rpId:", aRpCallerId, "identity:", aIdentity); - - let options = {rpId: aRpCallerId}; - Services.obs.notifyObservers({wrappedJSObject: options}, - "identity-login-state-changed", - aIdentity); - }, - - /** - * Initiate a login with user interaction as a result of a call to - * navigator.id.request(). - * - * @param aRPId - * (integer) the id of the doc object obtained in .watch() - * - * @param aOptions - * (Object) options including privacyPolicy, termsOfService - */ - request: function request(aRPId, aOptions) { - log("request: rpId:", aRPId); - let rp = this._rpFlows[aRPId]; - - // Notify UX to display identity picker. - // Pass the doc id to UX so it can pass it back to us later. - let options = {rpId: aRPId, origin: rp.origin}; - objectCopy(aOptions, options); - - // Append URLs after resolving - let baseURI = Services.io.newURI(rp.origin, null, null); - for (let optionName of ["privacyPolicy", "termsOfService"]) { - if (aOptions[optionName]) { - options[optionName] = baseURI.resolve(aOptions[optionName]); - } - } - - Services.obs.notifyObservers({wrappedJSObject: options}, "identity-request", null); - }, - - /** - * Invoked when a user wishes to logout of a site (for instance, when clicking - * on an in-content logout button). - * - * @param aRpCallerId - * (integer) the id of the doc object obtained in .watch() - * - */ - logout: function logout(aRpCallerId) { - log("logout: RP caller id:", aRpCallerId); - let rp = this._rpFlows[aRpCallerId]; - if (rp && rp.origin) { - let origin = rp.origin; - log("logout: origin:", origin); - this._doLogout(rp, {origin: origin}); - } else { - log("logout: no RP found with id:", aRpCallerId); - } - // We don't delete this._rpFlows[aRpCallerId], because - // the user might log back in again. - }, - - getDefaultEmailForOrigin: function getDefaultEmailForOrigin(aOrigin) { - let identities = this.getIdentitiesForSite(aOrigin); - let result = identities.lastUsed || null; - log("getDefaultEmailForOrigin:", aOrigin, "->", result); - return result; - }, - - /** - * Return the list of identities a user may want to use to login to aOrigin. - */ - getIdentitiesForSite: function getIdentitiesForSite(aOrigin) { - let rv = { result: [] }; - for (let id in this._store.getIdentities()) { - rv.result.push(id); - } - let loginState = this._store.getLoginState(aOrigin); - if (loginState && loginState.email) - rv.lastUsed = loginState.email; - return rv; - }, - - /** - * Obtain a BrowserID assertion with the specified characteristics. - * - * @param aCallback - * (Function) Callback to be called with (err, assertion) where 'err' - * can be an Error or NULL, and 'assertion' can be NULL or a valid - * BrowserID assertion. If no callback is provided, an exception is - * thrown. - * - * @param aOptions - * (Object) An object that may contain the following properties: - * - * "audience" : The audience for which the assertion is to be - * issued. If this property is not set an exception - * will be thrown. - * - * Any properties not listed above will be ignored. - */ - _getAssertion: function _getAssertion(aOptions, aCallback) { - let audience = aOptions.origin; - let email = aOptions.loggedInUser || this.getDefaultEmailForOrigin(audience); - log("_getAssertion: audience:", audience, "email:", email); - if (!audience) { - throw "audience required for _getAssertion"; - } - - // We might not have any identity info for this email - if (!this._store.fetchIdentity(email)) { - this._store.addIdentity(email, null, null); - } - - let cert = this._store.fetchIdentity(email)['cert']; - if (cert) { - this._generateAssertion(audience, email, function generatedAssertion(err, assertion) { - if (err) { - log("ERROR: _getAssertion:", err); - } - log("_getAssertion: generated assertion:", assertion); - return aCallback(err, assertion); - }); - } - }, - - /** - * Generate an assertion, including provisioning via IdP if necessary, - * but no user interaction, so if provisioning fails, aCallback is invoked - * with an error. - * - * @param aAudience - * (string) web origin - * - * @param aIdentity - * (string) the email we're logging in with - * - * @param aCallback - * (function) callback to invoke on completion - * with first-positional parameter the error. - */ - _generateAssertion: function _generateAssertion(aAudience, aIdentity, aCallback) { - log("_generateAssertion: audience:", aAudience, "identity:", aIdentity); - - let id = this._store.fetchIdentity(aIdentity); - if (! (id && id.cert)) { - let errStr = "Cannot generate an assertion without a certificate"; - log("ERROR: _generateAssertion:", errStr); - aCallback(errStr); - return; - } - - let kp = id.keyPair; - - if (!kp) { - let errStr = "Cannot generate an assertion without a keypair"; - log("ERROR: _generateAssertion:", errStr); - aCallback(errStr); - return; - } - - jwcrypto.generateAssertion(id.cert, kp, aAudience, aCallback); - }, - - /** - * Clean up references to the provisioning flow for the specified RP. - */ - _cleanUpProvisionFlow: function RP_cleanUpProvisionFlow(aRPId, aProvId) { - let rp = this._rpFlows[aRPId]; - if (rp) { - delete rp['provId']; - } else { - log("Error: Couldn't delete provision flow ", aProvId, " for RP ", aRPId); - } - }, - -}; - -this.RelyingParty = new IdentityRelyingParty(); diff --git a/toolkit/identity/Sandbox.jsm b/toolkit/identity/Sandbox.jsm deleted file mode 100644 index 68757c212..000000000 --- a/toolkit/identity/Sandbox.jsm +++ /dev/null @@ -1,152 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["Sandbox"]; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -const XHTML_NS = "http://www.w3.org/1999/xhtml"; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, - "Logger", - "resource://gre/modules/identity/LogUtils.jsm"); - -/** - * An object that represents a sandbox in an iframe loaded with aURL. The - * callback provided to the constructor will be invoked when the sandbox is - * ready to be used. The callback will receive this object as its only argument. - * - * You must call free() when you are finished with the sandbox to explicitly - * free up all associated resources. - * - * @param aURL - * (string) URL to load in the sandbox. - * - * @param aCallback - * (function) Callback to be invoked with a Sandbox, when ready. - */ -this.Sandbox = function Sandbox(aURL, aCallback) { - // Normalize the URL so the comparison in _makeSandboxContentLoaded works - this._url = Services.io.newURI(aURL, null, null).spec; - this._log("Creating sandbox for:", this._url); - this._createFrame(); - this._createSandbox(aCallback); -}; - -this.Sandbox.prototype = { - - /** - * Use the outer window ID as the identifier of the sandbox. - */ - get id() { - return this._frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; - }, - - /** - * Reload the URL in the sandbox. This is useful to reuse a Sandbox (same - * id and URL). - */ - reload: function Sandbox_reload(aCallback) { - this._log("reload:", this.id, ":", this._url); - this._createSandbox(function createdSandbox(aSandbox) { - this._log("reloaded sandbox id:", aSandbox.id); - aCallback(aSandbox); - }.bind(this)); - }, - - /** - * Frees the sandbox and releases the iframe created to host it. - */ - free: function Sandbox_free() { - this._log("free:", this.id); - this._container.removeChild(this._frame); - this._frame = null; - this._container = null; - this._url = null; - }, - - /** - * Creates an empty, hidden iframe and sets it to the _frame - * property of this object. - */ - _createFrame: function Sandbox__createFrame() { - let hiddenWindow = Services.appShell.hiddenDOMWindow; - let doc = hiddenWindow.document; - - // Insert iframe in to create docshell. - let frame = doc.createElementNS(XHTML_NS, "iframe"); - frame.setAttribute("mozframetype", "content"); - frame.sandbox = "allow-forms allow-scripts allow-same-origin"; - frame.style.visibility = "collapse"; - doc.documentElement.appendChild(frame); - - let docShell = frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShell); - - // Stop about:blank from being loaded. - docShell.stop(Ci.nsIWebNavigation.STOP_NETWORK); - - // Disable some types of content - docShell.allowAuth = false; - docShell.allowPlugins = false; - docShell.allowImages = false; - docShell.allowMedia = false; - docShell.allowWindowControl = false; - - // Disable stylesheet loading since the document is not visible. - let markupDocViewer = docShell.contentViewer; - markupDocViewer.authorStyleDisabled = true; - - // Set instance properties. - this._frame = frame; - this._container = doc.documentElement; - }, - - _createSandbox: function Sandbox__createSandbox(aCallback) { - let self = this; - function _makeSandboxContentLoaded(event) { - self._log("_makeSandboxContentLoaded:", self.id, - event.target.location.toString()); - if (event.target != self._frame.contentDocument) { - return; - } - self._frame.removeEventListener( - "DOMWindowCreated", _makeSandboxContentLoaded, true - ); - - aCallback(self); - } - - this._frame.addEventListener("DOMWindowCreated", - _makeSandboxContentLoaded, - true); - - // Load the iframe. - let webNav = this._frame.contentWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation); - - webNav.loadURI( - this._url, - Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE, - null, // referrer - null, // postData - null // headers - ); - - }, - - _log: function Sandbox__log(...aMessageArgs) { - Logger.log.apply(Logger, ["sandbox"].concat(aMessageArgs)); - }, - -}; diff --git a/toolkit/identity/jwcrypto.jsm b/toolkit/identity/jwcrypto.jsm deleted file mode 100644 index 4bcba730f..000000000 --- a/toolkit/identity/jwcrypto.jsm +++ /dev/null @@ -1,180 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/identity/LogUtils.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, - "IdentityCryptoService", - "@mozilla.org/identity/crypto-service;1", - "nsIIdentityCryptoService"); - -this.EXPORTED_SYMBOLS = ["jwcrypto"]; - -const ALGORITHMS = { RS256: "RS256", DS160: "DS160" }; -const DURATION_MS = 1000 * 60 * 2; // 2 minutes default assertion lifetime - -function log(...aMessageArgs) { - Logger.log.apply(Logger, ["jwcrypto"].concat(aMessageArgs)); -} - -function generateKeyPair(aAlgorithmName, aCallback) { - log("Generate key pair; alg =", aAlgorithmName); - - IdentityCryptoService.generateKeyPair(aAlgorithmName, function(rv, aKeyPair) { - if (!Components.isSuccessCode(rv)) { - return aCallback("key generation failed"); - } - - var publicKey; - - switch (aKeyPair.keyType) { - case ALGORITHMS.RS256: - publicKey = { - algorithm: "RS", - exponent: aKeyPair.hexRSAPublicKeyExponent, - modulus: aKeyPair.hexRSAPublicKeyModulus - }; - break; - - case ALGORITHMS.DS160: - publicKey = { - algorithm: "DS", - y: aKeyPair.hexDSAPublicValue, - p: aKeyPair.hexDSAPrime, - q: aKeyPair.hexDSASubPrime, - g: aKeyPair.hexDSAGenerator - }; - break; - - default: - return aCallback("unknown key type"); - } - - let keyWrapper = { - serializedPublicKey: JSON.stringify(publicKey), - _kp: aKeyPair - }; - - return aCallback(null, keyWrapper); - }); -} - -function sign(aPayload, aKeypair, aCallback) { - aKeypair._kp.sign(aPayload, function(rv, signature) { - if (!Components.isSuccessCode(rv)) { - log("ERROR: signer.sign failed"); - return aCallback("Sign failed"); - } - log("signer.sign: success"); - return aCallback(null, signature); - }); -} - -function jwcryptoClass() -{ -} - -jwcryptoClass.prototype = { - /* - * Determine the expiration of the assertion. Returns expiry date - * in milliseconds as integer. - * - * @param localtimeOffsetMsec (optional) - * The number of milliseconds that must be added to the local clock - * for it to agree with the server. For example, if the local clock - * if two minutes fast, localtimeOffsetMsec would be -120000 - * - * @param now (options) - * Current date in milliseconds. Useful for mocking clock - * skew in testing. - */ - getExpiration: function(duration=DURATION_MS, localtimeOffsetMsec=0, now=Date.now()) { - return now + localtimeOffsetMsec + duration; - }, - - isCertValid: function(aCert, aCallback) { - // XXX check expiration, bug 769850 - aCallback(true); - }, - - generateKeyPair: function(aAlgorithmName, aCallback) { - log("generating"); - generateKeyPair(aAlgorithmName, aCallback); - }, - - /* - * Generate an assertion and return it through the provided callback. - * - * @param aCert - * Identity certificate - * - * @param aKeyPair - * KeyPair object - * - * @param aAudience - * Audience of the assertion - * - * @param aOptions (optional) - * Can include: - * { - * localtimeOffsetMsec: <clock offset in milliseconds>, - * now: <current date in milliseconds> - * duration: <validity duration for this assertion in milliseconds> - * } - * - * localtimeOffsetMsec is the number of milliseconds that need to be - * added to the local clock time to make it concur with the server. - * For example, if the local clock is two minutes fast, the offset in - * milliseconds would be -120000. - * - * @param aCallback - * Function to invoke with resulting assertion. Assertion - * will be string or null on failure. - */ - generateAssertion: function(aCert, aKeyPair, aAudience, aOptions, aCallback) { - if (typeof aOptions == "function") { - aCallback = aOptions; - aOptions = { }; - } - - // for now, we hack the algorithm name - // XXX bug 769851 - var header = {"alg": "DS128"}; - var headerBytes = IdentityCryptoService.base64UrlEncode( - JSON.stringify(header)); - - var payload = { - exp: this.getExpiration( - aOptions.duration, aOptions.localtimeOffsetMsec, aOptions.now), - aud: aAudience - }; - var payloadBytes = IdentityCryptoService.base64UrlEncode( - JSON.stringify(payload)); - - log("payload bytes", payload, payloadBytes); - sign(headerBytes + "." + payloadBytes, aKeyPair, function(err, signature) { - if (err) - return aCallback(err); - - var signedAssertion = headerBytes + "." + payloadBytes + "." + signature; - return aCallback(null, aCert + "~" + signedAssertion); - }); - } - -}; - -this.jwcrypto = new jwcryptoClass(); -this.jwcrypto.ALGORITHMS = ALGORITHMS; diff --git a/toolkit/identity/moz.build b/toolkit/identity/moz.build deleted file mode 100644 index fd2ba9c8c..000000000 --- a/toolkit/identity/moz.build +++ /dev/null @@ -1,25 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -XPIDL_SOURCES += ['nsIIdentityCryptoService.idl'] - -XPIDL_MODULE = 'identity' - -SOURCES += ['IdentityCryptoService.cpp'] - -EXTRA_JS_MODULES.identity += [ - 'Identity.jsm', - 'IdentityProvider.jsm', - 'IdentityStore.jsm', - 'IdentityUtils.jsm', - 'jwcrypto.jsm', - 'LogUtils.jsm', - 'MinimalIdentity.jsm', - 'RelyingParty.jsm', - 'Sandbox.jsm', -] - -FINAL_LIBRARY = 'xul' diff --git a/toolkit/identity/nsIIdentityCryptoService.idl b/toolkit/identity/nsIIdentityCryptoService.idl deleted file mode 100644 index 90149e2e8..000000000 --- a/toolkit/identity/nsIIdentityCryptoService.idl +++ /dev/null @@ -1,106 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -interface nsIURI; -interface nsIIdentityKeyGenCallback; -interface nsIIdentitySignCallback; - -/* Naming and calling conventions: - * - * A"hex" prefix means "hex-encoded string representation of a byte sequence" - * e.g. "ae34bcdf123" - * - * A "base64url" prefix means "base-64-URL-encoded string repressentation of a - * byte sequence. - * e.g. "eyJhbGciOiJSUzI1NiJ9" - * http://en.wikipedia.org/wiki/Base64#Variants_summary_table - * we use the padded approach to base64-url-encoding - * - * Callbacks take an "in nsresult rv" argument that indicates whether the async - * operation succeeded. On success, rv will be a success code - * (NS_SUCCEEDED(rv) / Components.isSuccessCode(rv)) and the remaining - * arguments are as defined in the documentation for the callback. When the - * operation fails, rv will be a failure code (NS_FAILED(rv) / - * !Components.isSuccessCode(rv)) and the values of the remaining arguments will - * be unspecified. - * - * Key Types: - * - * "RS256": RSA + SHA-256. - * - * "DS160": DSA with SHA-1. A 1024-bit prime and a 160-bit subprime with SHA-1. - * - * we use these abbreviated algorithm names as per the JWA spec - * http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-02 - */ - -// "@mozilla.org/identity/crypto-service;1" -[scriptable, builtinclass, uuid(f087e6bc-dd33-4f6c-a106-dd786e052ee9)] -interface nsIIdentityCryptoService : nsISupports -{ - void generateKeyPair(in AUTF8String algorithm, - in nsIIdentityKeyGenCallback callback); - - ACString base64UrlEncode(in AUTF8String toEncode); -}; - -/** - * This interface provides a keypair and signing interface for Identity functionality - */ -[scriptable, uuid(73962dc7-8ee7-4346-a12b-b039e1d9b54d)] -interface nsIIdentityKeyPair : nsISupports -{ - readonly attribute AUTF8String keyType; - - // RSA properties, only accessible when keyType == "RS256" - - readonly attribute AUTF8String hexRSAPublicKeyExponent; - readonly attribute AUTF8String hexRSAPublicKeyModulus; - - // DSA properties, only accessible when keyType == "DS128" - readonly attribute AUTF8String hexDSAPrime; // p - readonly attribute AUTF8String hexDSASubPrime; // q - readonly attribute AUTF8String hexDSAGenerator; // g - readonly attribute AUTF8String hexDSAPublicValue; // y - - void sign(in AUTF8String aText, - in nsIIdentitySignCallback callback); - - // XXX implement verification bug 769856 - // AUTF8String verify(in AUTF8String aSignature, in AUTF8String encodedPublicKey); - -}; - -/** - * This interface provides a JavaScript callback object used to collect the - * nsIIdentityServeKeyPair when the keygen operation is complete - * - * though there is discussion as to whether we need the nsresult, - * we keep it so we can track deeper crypto errors. - */ -[scriptable, function, uuid(90f24ca2-2b05-4ca9-8aec-89d38e2f905a)] -interface nsIIdentityKeyGenCallback : nsISupports -{ - void generateKeyPairFinished(in nsresult rv, - in nsIIdentityKeyPair keyPair); -}; - -/** - * This interface provides a JavaScript callback object used to collect the - * AUTF8String signature - */ -[scriptable, function, uuid(2d3e5036-374b-4b47-a430-1196b67b890f)] -interface nsIIdentitySignCallback : nsISupports -{ - /** On success, base64urlSignature is the base-64-URL-encoded signature - * - * For RS256 signatures, XXX bug 769858 - * - * For DSA128 signatures, the signature is the r value concatenated with the - * s value, each component padded with leading zeroes as necessary. - */ - void signFinished(in nsresult rv, in ACString base64urlSignature); -}; |