|
- (function () {
- 'use strict';
-
- function utf8ToBinaryString(str) {
- var escstr = encodeURIComponent(str);
- // replaces any uri escape sequence, such as %0A,
- // with binary escape, such as 0x0A
- var binstr = escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
- return String.fromCharCode(parseInt(p1, 16));
- });
-
- return binstr;
- }
-
- function utf8ToBuffer(str) {
- var binstr = utf8ToBinaryString(str);
- var buf = binaryStringToBuffer(binstr);
- return buf;
- }
-
- function utf8ToBase64(str) {
- var binstr = utf8ToBinaryString(str);
- return btoa(binstr);
- }
-
- function binaryStringToUtf8(binstr) {
- var escstr = binstr.replace(/(.)/g, function (m, p) {
- var code = p.charCodeAt(0).toString(16).toUpperCase();
- if (code.length < 2) {
- code = '0' + code;
- }
- return '%' + code;
- });
-
- return decodeURIComponent(escstr);
- }
-
- function bufferToUtf8(buf) {
- var binstr = bufferToBinaryString(buf);
-
- return binaryStringToUtf8(binstr);
- }
-
- function base64ToUtf8(b64) {
- var binstr = atob(b64);
-
- return binaryStringToUtf8(binstr);
- }
-
- function bufferToBinaryString(buf) {
- var binstr = Array.prototype.map.call(buf, function (ch) {
- return String.fromCharCode(ch);
- }).join('');
-
- return binstr;
- }
-
- function bufferToBase64(arr) {
- var binstr = bufferToBinaryString(arr);
- return btoa(binstr);
- }
-
- function binaryStringToBuffer(binstr) {
- var buf;
-
- if ('undefined' !== typeof Uint8Array) {
- buf = new Uint8Array(binstr.length);
- } else {
- buf = [];
- }
-
- Array.prototype.forEach.call(binstr, function (ch, i) {
- buf[i] = ch.charCodeAt(0);
- });
-
- return buf;
- }
-
- function base64ToBuffer(base64) {
- var binstr = atob(base64);
- var buf = binaryStringToBuffer(binstr);
- return buf;
- }
-
- window.Unibabel = {
- utf8ToBinaryString: utf8ToBinaryString
- , utf8ToBuffer: utf8ToBuffer
- , utf8ToBase64: utf8ToBase64
- , binaryStringToUtf8: binaryStringToUtf8
- , bufferToUtf8: bufferToUtf8
- , base64ToUtf8: base64ToUtf8
- , bufferToBinaryString: bufferToBinaryString
- , bufferToBase64: bufferToBase64
- , binaryStringToBuffer: binaryStringToBuffer
- , base64ToBuffer: base64ToBuffer
-
- // compat
- , strToUtf8Arr: utf8ToBuffer
- , utf8ArrToStr: bufferToUtf8
- , arrToBase64: bufferToBase64
- , base64ToArr: base64ToBuffer
- };
-
- }());
-
- (function () {
- 'use strict';
-
- function bufferToHex(arr) {
- var i;
- var len;
- var hex = '';
- var c;
-
- for (i = 0, len = arr.length; i < len; i += 1) {
- c = arr[i].toString(16);
- if (c.length < 2) {
- c = '0' + c;
- }
- hex += c;
- }
-
- return hex;
- }
-
- function hexToBuffer(hex) {
- // TODO use Uint8Array or ArrayBuffer or DataView
- var i;
- var byteLen = hex.length / 2;
- var arr;
- var j = 0;
-
- if (byteLen !== parseInt(byteLen, 10)) {
- throw new Error("Invalid hex length '" + hex.length + "'");
- }
-
- arr = new Uint8Array(byteLen);
-
- for (i = 0; i < byteLen; i += 1) {
- arr[i] = parseInt(hex[j] + hex[j + 1], 16);
- j += 2;
- }
-
- return arr;
- }
-
- // Hex Convenience Functions
- window.Unibabel.hexToBuffer = hexToBuffer;
- window.Unibabel.bufferToHex = bufferToHex;
-
- }());
-
- (function () {
- 'use strict';
-
- if (window.crypto && !window.crypto.subtle && window.crypto.webkitSubtle) {
- window.crypto.subtle = window.crypto.webkitSubtle;
- }
- if (!window.crypto || !window.crypto.subtle) {
- console.error("Your browser does not support the Web Cryptography API! LessPass will not work.");
- return;
- }
-
- function importKey(masterPassword, algo, usages) {
- var format = 'raw';
- var masterPasswordArrayBuffer = Unibabel.utf8ToBuffer(masterPassword);
- var extractable = false;
- return window.crypto.subtle.importKey(format, masterPasswordArrayBuffer, algo, extractable, usages);
- }
-
- function deriveKey(masterKey, salt, iterations, keylen) {
- var algo = {
- name: 'PBKDF2',
- salt: Unibabel.utf8ToBuffer(salt),
- iterations: iterations,
- hash: 'SHA-256',
- };
- var extractable = true;
- var derivedKeyAlgo = {name: 'AES-CTR', length: keylen * 8};
- var usages = ['encrypt', 'decrypt'];
- return window.crypto.subtle.deriveKey(algo, masterKey, derivedKeyAlgo, extractable, usages);
- }
-
-
- function exportKey(derivedKey) {
- return window.crypto.subtle.exportKey('raw', derivedKey).then(function (keyArrayBuffer) {
- return Unibabel.bufferToHex(new Uint8Array(keyArrayBuffer));
- });
- }
-
- function encryptLogin(login, masterPassword, options) {
- var _options = options !== undefined ? options : {};
- var iterations = _options.iterations || 8192;
- var keylen = _options.keylen || 32;
-
- return importKey(masterPassword, 'PBKDF2', ['deriveKey'])
- .then(function (key) {
- return deriveKey(key, login, iterations, keylen);
- })
- .then(exportKey);
- }
-
- function signKey(masterKey, salt) {
- var algo = {name: 'HMAC'};
- var saltArrayBuffer = Unibabel.utf8ToBuffer(salt);
- return window.crypto.subtle.sign(algo, masterKey, saltArrayBuffer);
- }
-
- function _createHmac(encryptedLogin, salt) {
- return importKey(encryptedLogin, {name: 'HMAC', hash: {name: 'SHA-256'}}, ['sign'])
- .then(function (key) {
- return signKey(key, salt);
- })
- .then(function (derivedHmacKey) {
- return Unibabel.bufferToHex(new Uint8Array(derivedHmacKey))
- });
- }
-
- function _deriveEncryptedLogin(encryptedLogin, site) {
- var passwordOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
- length: 12,
- counter: 1
- };
-
- var salt = site + passwordOptions.counter.toString();
- return _createHmac(encryptedLogin, salt).then(function (derivedHash) {
- return derivedHash.substring(0, passwordOptions.length);
- });
- }
-
- function createFingerprint(str) {
- return _createHmac(str, '');
- }
-
- function renderPassword(encryptedLogin, site, passwordOptions) {
- return _deriveEncryptedLogin(encryptedLogin, site, passwordOptions).then(function (derivedEncryptedLogin) {
- var template = passwordOptions.template || LessPass._getPasswordTemplate(passwordOptions);
- return LessPass._prettyPrint(derivedEncryptedLogin, template);
- });
- }
-
- function _getPasswordTemplate(passwordTypes) {
- var templates = {
- lowercase: 'vc',
- uppercase: 'VC',
- numbers: 'n',
- symbols: 's',
- };
- var template = '';
- for (var templateKey in templates) {
- if (passwordTypes.hasOwnProperty(templateKey) && passwordTypes[templateKey]) {
- template += templates[templateKey]
- }
- }
- return template;
- }
-
-
- function _prettyPrint(hash, template) {
- var password = '';
-
- _string2charCodes(hash).forEach(function (charCode, index) {
- var charType = _getCharType(template, index);
- password += _getPasswordChar(charType, charCode);
- });
- return password;
- }
-
- function _string2charCodes(text) {
- var charCodes = [];
- for (var i = 0; i < text.length; i++) {
- charCodes.push(text.charCodeAt(i));
- }
- return charCodes;
- }
-
- function _getCharType(template, index) {
- return template[index % template.length];
- }
-
- function _getPasswordChar(charType, index) {
- var passwordsChars = {
- V: 'AEIOUY',
- C: 'BCDFGHJKLMNPQRSTVWXZ',
- v: 'aeiouy',
- c: 'bcdfghjklmnpqrstvwxz',
- A: 'AEIOUYBCDFGHJKLMNPQRSTVWXZ',
- a: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz',
- n: '0123456789',
- s: '@&%?,=[]_:-+*$#!\'^~;()/.',
- x: 'AEIOUYaeiouyBCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz0123456789@&%?,=[]_:-+*$#!\'^~;()/.'
- };
- var passwordChar = passwordsChars[charType];
- return passwordChar[index % passwordChar.length];
- }
-
- window.LessPass = {
- encryptLogin: encryptLogin,
- renderPassword: renderPassword,
- createFingerprint: createFingerprint,
- _createHmac: _createHmac,
- _deriveEncryptedLogin: _deriveEncryptedLogin,
- _getPasswordTemplate: _getPasswordTemplate,
- _prettyPrint: _prettyPrint,
- _string2charCodes: _string2charCodes,
- _getCharType: _getCharType,
- _getPasswordChar: _getPasswordChar,
- };
- }());
|