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.
 
 
 
 
 
 

110 lines
3.3 KiB

  1. var pbkdf2 = require('pbkdf2');
  2. var createHmac = require('create-hmac');
  3. module.exports = {
  4. encryptLogin: _encryptLogin,
  5. renderPassword: _renderPassword,
  6. createFingerprint: createFingerprint,
  7. _deriveEncryptedLogin,
  8. _getPasswordTemplate,
  9. _prettyPrint,
  10. _string2charCodes,
  11. _getCharType,
  12. _getPasswordChar,
  13. _createHmac
  14. };
  15. function _encryptLogin(login, masterPassword, {iterations = 8192, keylen = 32}={}) {
  16. return new Promise((resolve, reject) => {
  17. if (!login || !masterPassword) {
  18. reject('login and master password parameters could not be empty');
  19. }
  20. pbkdf2.pbkdf2(masterPassword, login, iterations, keylen, 'sha256', (error, key) => {
  21. if (error) {
  22. reject('error in pbkdf2');
  23. } else {
  24. resolve(key.toString('hex'));
  25. }
  26. });
  27. })
  28. }
  29. function _renderPassword(encryptedLogin, site, passwordOptions) {
  30. return _deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) {
  31. const template = passwordOptions.template || _getPasswordTemplate(passwordOptions);
  32. return _prettyPrint(derivedEncryptedLogin, template);
  33. });
  34. }
  35. function _createHmac(encryptedLogin, salt) {
  36. return new Promise(resolve => {
  37. resolve(createHmac('sha256', new Buffer(encryptedLogin)).update(salt).digest('hex'));
  38. });
  39. }
  40. function _deriveEncryptedLogin(encryptedLogin, site, passwordOptions = {length: 12, counter: 1}) {
  41. const salt = site + passwordOptions.counter.toString();
  42. return _createHmac(encryptedLogin, salt).then(derivedHash => {
  43. return derivedHash.substring(0, passwordOptions.length);
  44. });
  45. }
  46. function _getPasswordTemplate(passwordTypes) {
  47. const templates = {
  48. lowercase: 'vc',
  49. uppercase: 'VC',
  50. numbers: 'n',
  51. symbols: 's',
  52. };
  53. let template = '';
  54. for (let templateKey in templates) {
  55. if (passwordTypes.hasOwnProperty(templateKey) && passwordTypes[templateKey]) {
  56. template += templates[templateKey]
  57. }
  58. }
  59. return template;
  60. }
  61. function _prettyPrint(hash, template) {
  62. let password = '';
  63. _string2charCodes(hash).forEach((charCode, index) => {
  64. const charType = _getCharType(template, index);
  65. password += _getPasswordChar(charType, charCode);
  66. });
  67. return password;
  68. }
  69. function _string2charCodes(text) {
  70. const charCodes = [];
  71. for (let i = 0; i < text.length; i++) {
  72. charCodes.push(text.charCodeAt(i));
  73. }
  74. return charCodes;
  75. }
  76. function _getCharType(template, index) {
  77. return template[index % template.length];
  78. }
  79. function _getPasswordChar(charType, index) {
  80. const passwordsChars = {
  81. V: 'AEIOUY',
  82. C: 'BCDFGHJKLMNPQRSTVWXZ',
  83. v: 'aeiouy',
  84. c: 'bcdfghjklmnpqrstvwxz',
  85. A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ',
  86. a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz',
  87. n: '0123456789',
  88. s: '@&%?,=[]_:-+*$#!\'^~;()/.',
  89. x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.'
  90. };
  91. const passwordChar = passwordsChars[charType];
  92. return passwordChar[index % passwordChar.length];
  93. }
  94. function createFingerprint(str) {
  95. return new Promise(resolve => {
  96. resolve(createHmac('sha256', new Buffer(str)).digest('hex'))
  97. });
  98. }