├── README
├── icon.png
├── icon_128.png
├── icon_64.png
├── manifest.json
├── popup.css
├── popup.html
└── popup.js
/README:
--------------------------------------------------------------------------------
1 | Create passwords for different websites (or anything else) using a single
2 | secret key (if you lose your password for a website, you can regenerate it if
3 | you remember your secret key.)
4 |
5 | Algorithm
6 | ---------
7 |
8 | password = base64(pbkdf2(secret, username@url))
9 |
10 | (return first max password length characters)
11 | PBKDF2 uses SHA-256, 5000 iterations.
12 |
13 | License
14 | --------
15 |
16 | The MIT License
17 |
18 | Copyright (c) 2010-2012 Dmitry Chestnykh
19 |
20 | Permission is hereby granted, free of charge, to any person obtaining a copy
21 | of this software and associated documentation files (the "Software"), to deal
22 | in the Software without restriction, including without limitation the rights
23 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24 | copies of the Software, and to permit persons to whom the Software is
25 | furnished to do so, subject to the following conditions:
26 |
27 | The above copyright notice and this permission notice shall be included in
28 | all copies or substantial portions of the Software.
29 |
30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36 | THE SOFTWARE.
37 |
38 | Includes portions of sjcl (BSD License, see source).
39 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dchest/cryptopass/c0397af267a1133cdaa6d0b1c8ec6d34c56ba417/icon.png
--------------------------------------------------------------------------------
/icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dchest/cryptopass/c0397af267a1133cdaa6d0b1c8ec6d34c56ba417/icon_128.png
--------------------------------------------------------------------------------
/icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dchest/cryptopass/c0397af267a1133cdaa6d0b1c8ec6d34c56ba417/icon_64.png
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "CryptoPass",
3 | "version": "1.12",
4 | "manifest_version": 2,
5 | "description" : "Generate different passwords from single master password using cryptographically strong method (PBKDF2 with SHA-256).",
6 | "icons": {
7 | "128": "icon_128.png",
8 | "64": "icon_64.png"
9 | },
10 | "permissions": [
11 | "activeTab"
12 | ],
13 | "browser_action": {
14 | "default_title": "CryptoPass",
15 | "default_icon": "icon.png",
16 | "default_popup": "popup.html"
17 | },
18 | "content_security_policy": "default-src 'self'"
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/popup.css:
--------------------------------------------------------------------------------
1 | body {
2 | font: 10pt "Lucida Grande", Helvetica, Arial, sans-serif;
3 | width: 350px;
4 | border-radius: 10px;
5 | color: #303942;
6 | margin-right: 20px;
7 | user-select: none;
8 | -webkit-user-select: none;
9 | cursor: default;
10 | }
11 | table {
12 | margin: 0;
13 | width: 100%;
14 | }
15 | .label {
16 | text-align: right;
17 | padding-right: 5px;
18 | }
19 | #secret, #url, #username, #result {
20 | width: 100%;
21 | }
22 | .length, #length {
23 | font-size: 8pt;
24 | color: #999;
25 | }
26 | .centered {
27 | text-align: center;
28 | }
29 | #result-box {
30 | display: none;
31 | text-align: center;
32 | }
33 | #warning {
34 | display: none;
35 | font-size: 10px;
36 | color: #cc0011;
37 | padding: 3px 0;
38 | text-align: center;
39 | margin-top: -10px;
40 | }
41 | input {
42 | border: 1px solid rgba(0, 0, 0, 0.25);
43 | padding: 3px 3px;
44 | border-radius: 2px;
45 | outline: none;
46 | }
47 | input:focus {
48 | border-color: #4D90FE;
49 | }
50 | .buttons {
51 | text-align: right;
52 | }
53 | button {
54 | /*
55 | background: #f4f4f4 -webkit-gradient(linear,left top,left bottom,
56 | color-stop(0, #fafafa),color-stop(1, #ccc));
57 | border: 1px solid #aaa;
58 | border-radius: 5px;
59 | padding: 3px 10px;
60 | margin-top: 3px;
61 | text-shadow: 0 1px #f4f4f4;
62 | */
63 | background-image: -webkit-linear-gradient(#e8e8e8, #e8e8e8 38%, #d8d8d8);
64 | background-color: rgba(0, 0, 0, 0.3);
65 | box-shadow: 0 1px 0 rgba(0, 0, 0, 0.12), inset 0 1px 2px rgba(255, 255, 255, 0.95);
66 | color: black;
67 | text-shadow: 0 1px 0 #F0F0F0;
68 | border: 1px solid rgba(0, 0, 0, 0.25);
69 | border-radius: 2px;
70 | margin: 0 1px 0 0;
71 | outline: none;
72 | }
73 | button:focus {
74 | border-color: #4D90FE;
75 | }
76 | button:hover {
77 | background-image: -webkit-linear-gradient(#F0F0F0, #F0F0F0 38%, #E0E0E0);
78 | border: 1px solid rgba(0, 0, 0, 0.3);
79 | }
80 | button:active {
81 | /*background: #f4f4f4 -webkit-gradient(linear,left top,left bottom,
82 | color-stop(1, #ddd),color-stop(0, #bbb))
83 | */
84 | background-image: none;
85 | background-color: rgba(0, 0, 0, 0.15);
86 | box-shadow: none;
87 | }
88 | .gray {
89 | color:#666;
90 | }
91 | .working {
92 | font-size: 9pt;
93 | color: #ccc;
94 | }
95 |
--------------------------------------------------------------------------------
/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CryptoPass
7 |
8 |
9 |
10 |
11 |
12 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/popup.js:
--------------------------------------------------------------------------------
1 | /* SJCL LICENSE
2 | *
3 | * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions are
8 | * met:
9 | *
10 | * 1. Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | *
13 | * 2. Redistributions in binary form must reproduce the above
14 | * copyright notice, this list of conditions and the following
15 | * disclaimer in the documentation and/or other materials provided
16 | * with the distribution.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | * DISCLAIMED. IN NO EVENT SHALL OR CONTRIBUTORS BE
22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
28 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | *
30 | * The views and conclusions contained in the software and documentation
31 | * are those of the authors and should not be interpreted as representing
32 | * official policies, either expressed or implied, of the authors.
33 | */
34 |
35 | // sjcl.js
36 |
37 | /** @fileOverview Javascript cryptography implementation.
38 | *
39 | * Crush to remove comments, shorten variable names and
40 | * generally reduce transmission size.
41 | *
42 | * @author Emily Stark
43 | * @author Mike Hamburg
44 | * @author Dan Boneh
45 | */
46 |
47 | "use strict";
48 | /*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
49 | /*global document, window, escape, unescape */
50 |
51 | /** @namespace The Stanford Javascript Crypto Library, top-level namespace. */
52 | var sjcl = {
53 | /** @namespace Symmetric ciphers. */
54 | cipher: {},
55 |
56 | /** @namespace Hash functions. Right now only SHA256 is implemented. */
57 | hash: {},
58 |
59 | /** @namespace Block cipher modes of operation. */
60 | mode: {},
61 |
62 | /** @namespace Miscellaneous. HMAC and PBKDF2. */
63 | misc: {},
64 |
65 | /**
66 | * @namespace Bit array encoders and decoders.
67 | *
68 | * @description
69 | * The members of this namespace are functions which translate between
70 | * SJCL's bitArrays and other objects (usually strings). Because it
71 | * isn't always clear which direction is encoding and which is decoding,
72 | * the method names are "fromBits" and "toBits".
73 | */
74 | codec: {},
75 |
76 | /** @namespace Exceptions. */
77 | exception: {
78 | /** @class Ciphertext is corrupt. */
79 | corrupt: function(message) {
80 | this.toString = function() { return "CORRUPT: "+this.message; };
81 | this.message = message;
82 | },
83 |
84 | /** @class Invalid parameter. */
85 | invalid: function(message) {
86 | this.toString = function() { return "INVALID: "+this.message; };
87 | this.message = message;
88 | },
89 |
90 | /** @class Bug or missing feature in SJCL. */
91 | bug: function(message) {
92 | this.toString = function() { return "BUG: "+this.message; };
93 | this.message = message;
94 | },
95 |
96 | /** @class Something isn't ready. */
97 | notReady: function(message) {
98 | this.toString = function() { return "NOT READY: "+this.message; };
99 | this.message = message;
100 | }
101 | }
102 | };
103 |
104 | // pbkdf2.js
105 |
106 | /** @fileOverview Password-based key-derivation function, version 2.0.
107 | *
108 | * @author Emily Stark
109 | * @author Mike Hamburg
110 | * @author Dan Boneh
111 | */
112 |
113 | /** Password-Based Key-Derivation Function, version 2.0.
114 | *
115 | * Generate keys from passwords using PBKDF2-HMAC-SHA256.
116 | *
117 | * This is the method specified by RSA's PKCS #5 standard.
118 | *
119 | * @param {bitArray|String} password The password.
120 | * @param {bitArray|String} salt The salt. Should have lots of entropy.
121 | * @param {Number} [count=1000] The number of iterations. Higher numbers make the function slower but more secure.
122 | * @param {Number} [length] The length of the derived key. Defaults to the
123 | output size of the hash function.
124 | * @param {Object} [Prff=sjcl.misc.hmac] The pseudorandom function family.
125 | * @return {bitArray} the derived key.
126 | */
127 | sjcl.misc.pbkdf2 = function (password, salt, count, length, Prff) {
128 | count = count || 1000;
129 |
130 | if (length < 0 || count < 0) {
131 | throw sjcl.exception.invalid("invalid params to pbkdf2");
132 | }
133 |
134 | if (typeof password === "string") {
135 | password = sjcl.codec.utf8String.toBits(password);
136 | }
137 |
138 | if (typeof salt === "string") {
139 | salt = sjcl.codec.utf8String.toBits(salt);
140 | }
141 |
142 | Prff = Prff || sjcl.misc.hmac;
143 |
144 | var prf = new Prff(password),
145 | u, ui, i, j, k, out = [], b = sjcl.bitArray;
146 |
147 | for (k = 1; 32 * out.length < (length || 1); k++) {
148 | u = ui = prf.encrypt(b.concat(salt,[k]));
149 |
150 | for (i=1; i bs) {
186 | key = Hash.hash(key);
187 | }
188 |
189 | for (i=0; i>>7 ^ a>>>18 ^ a>>>3 ^ a<<25 ^ a<<14) +
421 | (b>>>17 ^ b>>>19 ^ b>>>10 ^ b<<15 ^ b<<13) +
422 | w[i&15] + w[(i+9) & 15]) | 0;
423 | }
424 |
425 | tmp = (tmp + h7 + (h4>>>6 ^ h4>>>11 ^ h4>>>25 ^ h4<<26 ^ h4<<21 ^ h4<<7) + (h6 ^ h4&(h5^h6)) + k[i]); // | 0;
426 |
427 | // shift register
428 | h7 = h6; h6 = h5; h5 = h4;
429 | h4 = h3 + tmp | 0;
430 | h3 = h2; h2 = h1; h1 = h0;
431 |
432 | h0 = (tmp + ((h1&h2) ^ (h3&(h1^h2))) + (h1>>>2 ^ h1>>>13 ^ h1>>>22 ^ h1<<30 ^ h1<<19 ^ h1<<10)) | 0;
433 | }
434 |
435 | h[0] = h[0]+h0 | 0;
436 | h[1] = h[1]+h1 | 0;
437 | h[2] = h[2]+h2 | 0;
438 | h[3] = h[3]+h3 | 0;
439 | h[4] = h[4]+h4 | 0;
440 | h[5] = h[5]+h5 | 0;
441 | h[6] = h[6]+h6 | 0;
442 | h[7] = h[7]+h7 | 0;
443 | }
444 | };
445 |
446 |
447 |
448 | // codecString.js
449 |
450 | /** @fileOverview Bit array codec implementations.
451 | *
452 | * @author Emily Stark
453 | * @author Mike Hamburg
454 | * @author Dan Boneh
455 | */
456 |
457 | /** @namespace UTF-8 strings */
458 | sjcl.codec.utf8String = {
459 | /** Convert from a bitArray to a UTF-8 string. */
460 | fromBits: function (arr) {
461 | var out = "", bl = sjcl.bitArray.bitLength(arr), i, tmp;
462 | for (i=0; i>> 24);
467 | tmp <<= 8;
468 | }
469 | return decodeURIComponent(escape(out));
470 | },
471 |
472 | /** Convert from a UTF-8 string to a bitArray. */
473 | toBits: function (str) {
474 | str = unescape(encodeURIComponent(str));
475 | var out = [], i, tmp=0;
476 | for (i=0; i
503 | * These objects are the currency accepted by SJCL's crypto functions.
504 | *
505 | *
506 | *
507 | * Most of our crypto primitives operate on arrays of 4-byte words internally,
508 | * but many of them can take arguments that are not a multiple of 4 bytes.
509 | * This library encodes arrays of bits (whose size need not be a multiple of 8
510 | * bits) as arrays of 32-bit words. The bits are packed, big-endian, into an
511 | * array of words, 32 bits at a time. Since the words are double-precision
512 | * floating point numbers, they fit some extra data. We use this (in a private,
513 | * possibly-changing manner) to encode the number of bits actually present
514 | * in the last word of the array.
515 | *
516 | *
517 | *
518 | * Because bitwise ops clear this out-of-band data, these arrays can be passed
519 | * to ciphers like AES which want arrays of words.
520 | *
521 | */
522 | sjcl.bitArray = {
523 | /**
524 | * Array slices in units of bits.
525 | * @param {bitArray} a The array to slice.
526 | * @param {Number} bstart The offset to the start of the slice, in bits.
527 | * @param {Number} bend The offset to the end of the slice, in bits. If this is undefined,
528 | * slice until the end of the array.
529 | * @return {bitArray} The requested slice.
530 | */
531 | bitSlice: function (a, bstart, bend) {
532 | a = sjcl.bitArray._shiftRight(a.slice(bstart/32), 32 - (bstart & 31)).slice(1);
533 | return (bend === undefined) ? a : sjcl.bitArray.clamp(a, bend-bstart);
534 | },
535 |
536 | /**
537 | * Extract a number packed into a bit array.
538 | * @param {bitArray} a The array to slice.
539 | * @param {Number} bstart The offset to the start of the slice, in bits.
540 | * @param {Number} length The length of the number to extract.
541 | * @return {Number} The requested slice.
542 | */
543 | extract: function(a, bstart, blength) {
544 | // FIXME: this Math.floor is not necessary at all, but for some reason
545 | // seems to suppress a bug in the Chromium JIT.
546 | var x, sh = Math.floor((-bstart-blength) & 31);
547 | if ((bstart + blength - 1 ^ bstart) & -32) {
548 | // it crosses a boundary
549 | x = (a[bstart/32|0] << (32 - sh)) ^ (a[bstart/32+1|0] >>> sh);
550 | } else {
551 | // within a single word
552 | x = a[bstart/32|0] >>> sh;
553 | }
554 | return x & ((1< 0 && len) {
600 | a[l-1] = sjcl.bitArray.partial(len, a[l-1] & 0x80000000 >> (len-1), 1);
601 | }
602 | return a;
603 | },
604 |
605 | /**
606 | * Make a partial word for a bit array.
607 | * @param {Number} len The number of bits in the word.
608 | * @param {Number} x The bits.
609 | * @param {Number} [0] _end Pass 1 if x has already been shifted to the high side.
610 | * @return {Number} The partial word.
611 | */
612 | partial: function (len, x, _end) {
613 | if (len === 32) { return x; }
614 | return (_end ? x|0 : x << (32-len)) + len * 0x10000000000;
615 | },
616 |
617 | /**
618 | * Get the number of bits used by a partial word.
619 | * @param {Number} x The partial word.
620 | * @return {Number} The number of bits used by the partial word.
621 | */
622 | getPartial: function (x) {
623 | return Math.round(x/0x10000000000) || 32;
624 | },
625 |
626 | /**
627 | * Compare two arrays for equality in a predictable amount of time.
628 | * @param {bitArray} a The first array.
629 | * @param {bitArray} b The second array.
630 | * @return {boolean} true if a == b; false otherwise.
631 | */
632 | equal: function (a, b) {
633 | if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) {
634 | return false;
635 | }
636 | var x = 0, i;
637 | for (i=0; i= 32; shift -= 32) {
655 | out.push(carry);
656 | carry = 0;
657 | }
658 | if (shift === 0) {
659 | return out.concat(a);
660 | }
661 |
662 | for (i=0; i>>shift);
664 | carry = a[i] << (32-shift);
665 | }
666 | last2 = a.length ? a[a.length-1] : 0;
667 | shift2 = sjcl.bitArray.getPartial(last2);
668 | out.push(sjcl.bitArray.partial(shift+shift2 & 31, (shift + shift2 > 32) ? carry : out.pop(),1));
669 | return out;
670 | },
671 |
672 | /** xor a block of 4 words together.
673 | * @private
674 | */
675 | _xor4: function(x,y) {
676 | return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]];
677 | }
678 | };
679 |
680 | // codecBase64.js
681 |
682 | /** @fileOverview Bit array codec implementations.
683 | *
684 | * @author Emily Stark
685 | * @author Mike Hamburg
686 | * @author Dan Boneh
687 | */
688 |
689 | /** @namespace Base64 encoding/decoding */
690 | sjcl.codec.base64 = {
691 | /** The base64 alphabet.
692 | * @private
693 | */
694 | _chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
695 |
696 | /** Convert from a bitArray to a base64 string. */
697 | fromBits: function (arr, _noEquals, _url) {
698 | var out = "", i, bits=0, c = sjcl.codec.base64._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
699 | if (_url) {
700 | c = c.substr(0,62) + '-_';
701 | }
702 | for (i=0; out.length * 6 < bl; ) {
703 | out += c.charAt((ta ^ arr[i]>>>bits) >>> 26);
704 | if (bits < 6) {
705 | ta = arr[i] << (6-bits);
706 | bits += 26;
707 | i++;
708 | } else {
709 | ta <<= 6;
710 | bits -= 6;
711 | }
712 | }
713 | while ((out.length & 3) && !_noEquals) { out += "="; }
714 | return out;
715 | },
716 |
717 | /** Convert from a base64 string to a bitArray */
718 | toBits: function(str, _url) {
719 | str = str.replace(/\s|=/g,'');
720 | var out = [], i, bits=0, c = sjcl.codec.base64._chars, ta=0, x;
721 | if (_url) {
722 | c = c.substr(0,62) + '-_';
723 | }
724 | for (i=0; i 26) {
730 | bits -= 26;
731 | out.push(ta ^ x>>>bits);
732 | ta = x << (32-bits);
733 | } else {
734 | bits += 6;
735 | ta ^= x << (32-bits);
736 | }
737 | }
738 | if (bits&56) {
739 | out.push(sjcl.bitArray.partial(bits&56, ta, 1));
740 | }
741 | return out;
742 | }
743 | };
744 |
745 | sjcl.codec.base64url = {
746 | fromBits: function (arr) { return sjcl.codec.base64.fromBits(arr,1,1); },
747 | toBits: function (str) { return sjcl.codec.base64.toBits(str,1); }
748 | };
749 |
750 | // **** Password generation ****
751 |
752 | function makePassword() {
753 | var secret = document.getElementById('secret').value;
754 | var username = document.getElementById('username').value;
755 | var url = document.getElementById('url').value;
756 | var length = document.getElementById('length').value;
757 | var salt = username + '@' + url;
758 | var binLength = Math.ceil(length/4*3);
759 | return sjcl.codec.base64.fromBits(sjcl.misc.pbkdf2(secret, salt, 5000, binLength * 8)).substring(0, length);
760 | }
761 |
762 |
763 | function toggleDiv(id)
764 | {
765 | var infoStyle = document.getElementById(id).style;
766 | if (infoStyle.display == "block")
767 | infoStyle.display = "none";
768 | else
769 | infoStyle.display = "block";
770 | }
771 |
772 | function showDiv(id)
773 | {
774 | var infoStyle = document.getElementById(id).style;
775 | infoStyle.display = "block";
776 | }
777 |
778 | function hideDiv(id)
779 | {
780 | var infoStyle = document.getElementById(id).style;
781 | infoStyle.display = "none";
782 | }
783 |
784 | function getHostname(str) {
785 | if (str == null || str == undefined)
786 | return "";
787 | var re = new RegExp('^(?:f|ht)tp(?:s)?\://(?:www.)?([^/]+)', 'im');
788 | var match = str.match(re);
789 | if (match != null && match.length > 0)
790 | return match[1].toString();
791 | else
792 | return "";
793 | }
794 |
795 | function showPleaseWait() {
796 | document.querySelector("#result-box").innerHTML = "Please wait...";
797 | showDiv('result-box');
798 | }
799 |
800 | function generatePassword(event)
801 | {
802 | showPleaseWait();
803 | setTimeout(function() {
804 | document.querySelector("#result-box").innerHTML =
805 | '
Your password (copy and paste it): '+
806 | '
'+
807 | '
Secret should contain at least 16 characters for better security.