crypto-SDR.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
  5. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  6. Components.utils.import("resource://gre/modules/Services.jsm");
  7. XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
  8. "resource://gre/modules/LoginHelper.jsm");
  9. function LoginManagerCrypto_SDR() {
  10. this.init();
  11. }
  12. LoginManagerCrypto_SDR.prototype = {
  13. classID : Components.ID("{dc6c2976-0f73-4f1f-b9ff-3d72b4e28309}"),
  14. QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerCrypto]),
  15. __sdrSlot : null, // PKCS#11 slot being used by the SDR.
  16. get _sdrSlot() {
  17. if (!this.__sdrSlot) {
  18. let modules = Cc["@mozilla.org/security/pkcs11moduledb;1"].
  19. getService(Ci.nsIPKCS11ModuleDB);
  20. this.__sdrSlot = modules.findSlotByName("");
  21. }
  22. return this.__sdrSlot;
  23. },
  24. __decoderRing : null, // nsSecretDecoderRing service
  25. get _decoderRing() {
  26. if (!this.__decoderRing)
  27. this.__decoderRing = Cc["@mozilla.org/security/sdr;1"].
  28. getService(Ci.nsISecretDecoderRing);
  29. return this.__decoderRing;
  30. },
  31. __utfConverter : null, // UCS2 <--> UTF8 string conversion
  32. get _utfConverter() {
  33. if (!this.__utfConverter) {
  34. this.__utfConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
  35. createInstance(Ci.nsIScriptableUnicodeConverter);
  36. this.__utfConverter.charset = "UTF-8";
  37. }
  38. return this.__utfConverter;
  39. },
  40. _utfConverterReset : function() {
  41. this.__utfConverter = null;
  42. },
  43. _uiBusy : false,
  44. init : function () {
  45. // Check to see if the internal PKCS#11 token has been initialized.
  46. // If not, set a blank password.
  47. let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].
  48. getService(Ci.nsIPK11TokenDB);
  49. let token = tokenDB.getInternalKeyToken();
  50. if (token.needsUserInit) {
  51. this.log("Initializing key3.db with default blank password.");
  52. token.initPassword("");
  53. }
  54. },
  55. /*
  56. * encrypt
  57. *
  58. * Encrypts the specified string, using the SecretDecoderRing.
  59. *
  60. * Returns the encrypted string, or throws an exception if there was a
  61. * problem.
  62. */
  63. encrypt : function (plainText) {
  64. let cipherText = null;
  65. let wasLoggedIn = this.isLoggedIn;
  66. let canceledMP = false;
  67. this._uiBusy = true;
  68. try {
  69. let plainOctet = this._utfConverter.ConvertFromUnicode(plainText);
  70. plainOctet += this._utfConverter.Finish();
  71. cipherText = this._decoderRing.encryptString(plainOctet);
  72. } catch (e) {
  73. this.log("Failed to encrypt string. (" + e.name + ")");
  74. // If the user clicks Cancel, we get NS_ERROR_FAILURE.
  75. // (unlike decrypting, which gets NS_ERROR_NOT_AVAILABLE).
  76. if (e.result == Cr.NS_ERROR_FAILURE) {
  77. canceledMP = true;
  78. throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT);
  79. } else {
  80. throw Components.Exception("Couldn't encrypt string", Cr.NS_ERROR_FAILURE);
  81. }
  82. } finally {
  83. this._uiBusy = false;
  84. // If we triggered a master password prompt, notify observers.
  85. if (!wasLoggedIn && this.isLoggedIn)
  86. this._notifyObservers("passwordmgr-crypto-login");
  87. else if (canceledMP)
  88. this._notifyObservers("passwordmgr-crypto-loginCanceled");
  89. }
  90. return cipherText;
  91. },
  92. /*
  93. * decrypt
  94. *
  95. * Decrypts the specified string, using the SecretDecoderRing.
  96. *
  97. * Returns the decrypted string, or throws an exception if there was a
  98. * problem.
  99. */
  100. decrypt : function (cipherText) {
  101. let plainText = null;
  102. let wasLoggedIn = this.isLoggedIn;
  103. let canceledMP = false;
  104. this._uiBusy = true;
  105. try {
  106. let plainOctet;
  107. plainOctet = this._decoderRing.decryptString(cipherText);
  108. plainText = this._utfConverter.ConvertToUnicode(plainOctet);
  109. } catch (e) {
  110. this.log("Failed to decrypt string: " + cipherText +
  111. " (" + e.name + ")");
  112. // In the unlikely event the converter threw, reset it.
  113. this._utfConverterReset();
  114. // If the user clicks Cancel, we get NS_ERROR_NOT_AVAILABLE.
  115. // If the cipherText is bad / wrong key, we get NS_ERROR_FAILURE
  116. // Wrong passwords are handled by the decoderRing reprompting;
  117. // we get no notification.
  118. if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) {
  119. canceledMP = true;
  120. throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT);
  121. } else {
  122. throw Components.Exception("Couldn't decrypt string", Cr.NS_ERROR_FAILURE);
  123. }
  124. } finally {
  125. this._uiBusy = false;
  126. // If we triggered a master password prompt, notify observers.
  127. if (!wasLoggedIn && this.isLoggedIn)
  128. this._notifyObservers("passwordmgr-crypto-login");
  129. else if (canceledMP)
  130. this._notifyObservers("passwordmgr-crypto-loginCanceled");
  131. }
  132. return plainText;
  133. },
  134. /*
  135. * uiBusy
  136. */
  137. get uiBusy() {
  138. return this._uiBusy;
  139. },
  140. /*
  141. * isLoggedIn
  142. */
  143. get isLoggedIn() {
  144. let status = this._sdrSlot.status;
  145. this.log("SDR slot status is " + status);
  146. if (status == Ci.nsIPKCS11Slot.SLOT_READY ||
  147. status == Ci.nsIPKCS11Slot.SLOT_LOGGED_IN)
  148. return true;
  149. if (status == Ci.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN)
  150. return false;
  151. throw Components.Exception("unexpected slot status: " + status, Cr.NS_ERROR_FAILURE);
  152. },
  153. /*
  154. * defaultEncType
  155. */
  156. get defaultEncType() {
  157. return Ci.nsILoginManagerCrypto.ENCTYPE_SDR;
  158. },
  159. /*
  160. * _notifyObservers
  161. */
  162. _notifyObservers : function(topic) {
  163. this.log("Prompted for a master password, notifying for " + topic);
  164. Services.obs.notifyObservers(null, topic, null);
  165. },
  166. }; // end of nsLoginManagerCrypto_SDR implementation
  167. XPCOMUtils.defineLazyGetter(this.LoginManagerCrypto_SDR.prototype, "log", () => {
  168. let logger = LoginHelper.createLogger("Login crypto");
  169. return logger.log.bind(logger);
  170. });
  171. var component = [LoginManagerCrypto_SDR];
  172. this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);