diff options
Diffstat (limited to 'toolkit/components/passwordmgr/crypto-SDR.js')
-rw-r--r-- | toolkit/components/passwordmgr/crypto-SDR.js | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/toolkit/components/passwordmgr/crypto-SDR.js b/toolkit/components/passwordmgr/crypto-SDR.js new file mode 100644 index 000000000..b0916eb29 --- /dev/null +++ b/toolkit/components/passwordmgr/crypto-SDR.js @@ -0,0 +1,207 @@ +/* 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/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper", + "resource://gre/modules/LoginHelper.jsm"); + +function LoginManagerCrypto_SDR() { + this.init(); +} + +LoginManagerCrypto_SDR.prototype = { + + classID : Components.ID("{dc6c2976-0f73-4f1f-b9ff-3d72b4e28309}"), + QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerCrypto]), + + __sdrSlot : null, // PKCS#11 slot being used by the SDR. + get _sdrSlot() { + if (!this.__sdrSlot) { + let modules = Cc["@mozilla.org/security/pkcs11moduledb;1"]. + getService(Ci.nsIPKCS11ModuleDB); + this.__sdrSlot = modules.findSlotByName(""); + } + return this.__sdrSlot; + }, + + __decoderRing : null, // nsSecretDecoderRing service + get _decoderRing() { + if (!this.__decoderRing) + this.__decoderRing = Cc["@mozilla.org/security/sdr;1"]. + getService(Ci.nsISecretDecoderRing); + return this.__decoderRing; + }, + + __utfConverter : null, // UCS2 <--> UTF8 string conversion + get _utfConverter() { + if (!this.__utfConverter) { + this.__utfConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Ci.nsIScriptableUnicodeConverter); + this.__utfConverter.charset = "UTF-8"; + } + return this.__utfConverter; + }, + + _utfConverterReset : function() { + this.__utfConverter = null; + }, + + _uiBusy : false, + + + init : function () { + // Check to see if the internal PKCS#11 token has been initialized. + // If not, set a blank password. + let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]. + getService(Ci.nsIPK11TokenDB); + + let token = tokenDB.getInternalKeyToken(); + if (token.needsUserInit) { + this.log("Initializing key3.db with default blank password."); + token.initPassword(""); + } + }, + + + /* + * encrypt + * + * Encrypts the specified string, using the SecretDecoderRing. + * + * Returns the encrypted string, or throws an exception if there was a + * problem. + */ + encrypt : function (plainText) { + let cipherText = null; + + let wasLoggedIn = this.isLoggedIn; + let canceledMP = false; + + this._uiBusy = true; + try { + let plainOctet = this._utfConverter.ConvertFromUnicode(plainText); + plainOctet += this._utfConverter.Finish(); + cipherText = this._decoderRing.encryptString(plainOctet); + } catch (e) { + this.log("Failed to encrypt string. (" + e.name + ")"); + // If the user clicks Cancel, we get NS_ERROR_FAILURE. + // (unlike decrypting, which gets NS_ERROR_NOT_AVAILABLE). + if (e.result == Cr.NS_ERROR_FAILURE) { + canceledMP = true; + throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT); + } else { + throw Components.Exception("Couldn't encrypt string", Cr.NS_ERROR_FAILURE); + } + } finally { + this._uiBusy = false; + // If we triggered a master password prompt, notify observers. + if (!wasLoggedIn && this.isLoggedIn) + this._notifyObservers("passwordmgr-crypto-login"); + else if (canceledMP) + this._notifyObservers("passwordmgr-crypto-loginCanceled"); + } + return cipherText; + }, + + + /* + * decrypt + * + * Decrypts the specified string, using the SecretDecoderRing. + * + * Returns the decrypted string, or throws an exception if there was a + * problem. + */ + decrypt : function (cipherText) { + let plainText = null; + + let wasLoggedIn = this.isLoggedIn; + let canceledMP = false; + + this._uiBusy = true; + try { + let plainOctet; + plainOctet = this._decoderRing.decryptString(cipherText); + plainText = this._utfConverter.ConvertToUnicode(plainOctet); + } catch (e) { + this.log("Failed to decrypt string: " + cipherText + + " (" + e.name + ")"); + + // In the unlikely event the converter threw, reset it. + this._utfConverterReset(); + + // If the user clicks Cancel, we get NS_ERROR_NOT_AVAILABLE. + // If the cipherText is bad / wrong key, we get NS_ERROR_FAILURE + // Wrong passwords are handled by the decoderRing reprompting; + // we get no notification. + if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) { + canceledMP = true; + throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT); + } else { + throw Components.Exception("Couldn't decrypt string", Cr.NS_ERROR_FAILURE); + } + } finally { + this._uiBusy = false; + // If we triggered a master password prompt, notify observers. + if (!wasLoggedIn && this.isLoggedIn) + this._notifyObservers("passwordmgr-crypto-login"); + else if (canceledMP) + this._notifyObservers("passwordmgr-crypto-loginCanceled"); + } + + return plainText; + }, + + + /* + * uiBusy + */ + get uiBusy() { + return this._uiBusy; + }, + + + /* + * isLoggedIn + */ + get isLoggedIn() { + let status = this._sdrSlot.status; + this.log("SDR slot status is " + status); + if (status == Ci.nsIPKCS11Slot.SLOT_READY || + status == Ci.nsIPKCS11Slot.SLOT_LOGGED_IN) + return true; + if (status == Ci.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN) + return false; + throw Components.Exception("unexpected slot status: " + status, Cr.NS_ERROR_FAILURE); + }, + + + /* + * defaultEncType + */ + get defaultEncType() { + return Ci.nsILoginManagerCrypto.ENCTYPE_SDR; + }, + + + /* + * _notifyObservers + */ + _notifyObservers : function(topic) { + this.log("Prompted for a master password, notifying for " + topic); + Services.obs.notifyObservers(null, topic, null); + }, +}; // end of nsLoginManagerCrypto_SDR implementation + +XPCOMUtils.defineLazyGetter(this.LoginManagerCrypto_SDR.prototype, "log", () => { + let logger = LoginHelper.createLogger("Login crypto"); + return logger.log.bind(logger); +}); + +var component = [LoginManagerCrypto_SDR]; +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component); |