You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

webcrypto.js 5.4 KiB

8 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. (function () {
  2. 'use strict';
  3. if (window.crypto && !window.crypto.subtle && window.crypto.webkitSubtle) {
  4. window.crypto.subtle = window.crypto.webkitSubtle;
  5. }
  6. if (!window.crypto || !window.crypto.subtle) {
  7. console.error("Your browser does not support the Web Cryptography API! LessPass will not work.");
  8. return;
  9. }
  10. function importKey(masterPassword, algo, usages) {
  11. var format = 'raw';
  12. var masterPasswordArrayBuffer = Unibabel.utf8ToBuffer(masterPassword);
  13. var extractable = false;
  14. return window.crypto.subtle.importKey(format, masterPasswordArrayBuffer, algo, extractable, usages);
  15. }
  16. function deriveKey(masterKey, salt, iterations, keylen) {
  17. var algo = {
  18. name: 'PBKDF2',
  19. salt: Unibabel.utf8ToBuffer(salt),
  20. iterations: iterations,
  21. hash: 'SHA-256',
  22. };
  23. var extractable = true;
  24. var derivedKeyAlgo = {name: 'AES-CTR', length: keylen * 8};
  25. var usages = ['encrypt', 'decrypt'];
  26. return window.crypto.subtle.deriveKey(algo, masterKey, derivedKeyAlgo, extractable, usages);
  27. }
  28. function exportKey(derivedKey) {
  29. return window.crypto.subtle.exportKey('raw', derivedKey).then(function (keyArrayBuffer) {
  30. return Unibabel.bufferToHex(new Uint8Array(keyArrayBuffer));
  31. });
  32. }
  33. function encryptLogin(login, masterPassword, options) {
  34. var _options = options !== undefined ? options : {};
  35. var iterations = _options.iterations || 8192;
  36. var keylen = _options.keylen || 32;
  37. return importKey(masterPassword, 'PBKDF2', ['deriveKey'])
  38. .then(function (key) {
  39. return deriveKey(key, login, iterations, keylen);
  40. })
  41. .then(exportKey);
  42. }
  43. function signKey(masterKey, salt) {
  44. var algo = {name: 'HMAC'};
  45. var saltArrayBuffer = Unibabel.utf8ToBuffer(salt);
  46. return window.crypto.subtle.sign(algo, masterKey, saltArrayBuffer);
  47. }
  48. function _createHmac(encryptedLogin, salt) {
  49. return importKey(encryptedLogin, {name: 'HMAC', hash: {name: 'SHA-256'}}, ['sign'])
  50. .then(function (key) {
  51. return signKey(key, salt);
  52. })
  53. .then(function (derivedHmacKey) {
  54. return Unibabel.bufferToHex(new Uint8Array(derivedHmacKey))
  55. });
  56. }
  57. function _deriveEncryptedLogin(encryptedLogin, site) {
  58. var passwordOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
  59. length: 12,
  60. counter: 1
  61. };
  62. var salt = site + passwordOptions.counter.toString();
  63. return _createHmac(encryptedLogin, salt).then(function (derivedHash) {
  64. return derivedHash.substring(0, passwordOptions.length);
  65. });
  66. }
  67. function createFingerprint(str) {
  68. return _createHmac(str, '');
  69. }
  70. function renderPassword(encryptedLogin, site, passwordOptions) {
  71. return _deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) {
  72. var template = passwordOptions.template || LessPass._getPasswordTemplate(passwordOptions);
  73. return LessPass._prettyPrint(derivedEncryptedLogin, template);
  74. });
  75. }
  76. function _getPasswordTemplate(passwordTypes) {
  77. var templates = {
  78. lowercase: 'vc',
  79. uppercase: 'VC',
  80. numbers: 'n',
  81. symbols: 's',
  82. };
  83. var template = '';
  84. for (var templateKey in templates) {
  85. if (passwordTypes.hasOwnProperty(templateKey) && passwordTypes[templateKey]) {
  86. template += templates[templateKey]
  87. }
  88. }
  89. return template;
  90. }
  91. function _prettyPrint(hash, template) {
  92. var password = '';
  93. _string2charCodes(hash).forEach(function (charCode, index) {
  94. var charType = _getCharType(template, index);
  95. password += _getPasswordChar(charType, charCode);
  96. });
  97. return password;
  98. }
  99. function _string2charCodes(text) {
  100. var charCodes = [];
  101. for (var i = 0; i < text.length; i++) {
  102. charCodes.push(text.charCodeAt(i));
  103. }
  104. return charCodes;
  105. }
  106. function _getCharType(template, index) {
  107. return template[index % template.length];
  108. }
  109. function _getPasswordChar(charType, index) {
  110. var passwordsChars = {
  111. V: 'AEIOUY',
  112. C: 'BCDFGHJKLMNPQRSTVWXZ',
  113. v: 'aeiouy',
  114. c: 'bcdfghjklmnpqrstvwxz',
  115. A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ',
  116. a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz',
  117. n: '0123456789',
  118. s: '@&%?,=[]_:-+*$#!\'^~;()/.',
  119. x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.'
  120. };
  121. var passwordChar = passwordsChars[charType];
  122. return passwordChar[index % passwordChar.length];
  123. }
  124. window.LessPass = {
  125. encryptLogin: encryptLogin,
  126. renderPassword: renderPassword,
  127. createFingerprint: createFingerprint,
  128. _createHmac: _createHmac,
  129. _deriveEncryptedLogin: _deriveEncryptedLogin,
  130. _getPasswordTemplate: _getPasswordTemplate,
  131. _prettyPrint: _prettyPrint,
  132. _string2charCodes: _string2charCodes,
  133. _getCharType: _getCharType,
  134. _getPasswordChar: _getPasswordChar,
  135. };
  136. }());