├── LICENSE
├── README.md
├── background.js
├── chat.html
├── chatscript.js
├── config.xml
├── favicon.ico
├── icon.png
├── index.html
├── js-body
├── bodyscript.js
├── filedrop.js
├── initbuttons.js
└── screens.js
├── js-head
├── Chromestuff.js
├── SSSS.js
├── colors.js
├── crypto-extra.js
├── crypto-main.js
├── dictionary_en.js
├── imagestego.js
├── keylock.js
├── license.js
├── localdir.js
├── mail&chat.js
└── textstego.js
├── js-opensrc
├── ed2curve.js
├── isaac.js
├── jscolor.js
├── jssteg-1.0.js
├── jsstegdecoder-1.0.js
├── jsstegencoder-1.0.js
├── lz-string.js
├── nacl-fast.js
├── nacl-util.js
├── nacl.js
├── purify.js
├── qrcode.js
├── scrypt-async.js
└── secrets.js
├── manifest.firefox.json
├── manifest.json
├── passlok-icon128.png
├── passlok-icon48.png
├── passlok-touch-icon.png
├── passlok.appcache
├── phonegap
└── screen
│ ├── android
│ ├── screen-hdpi-portrait.png
│ ├── screen-ldpi-portrait.png
│ ├── screen-mdpi-portrait.png
│ └── screen-xdpi-portrait.png
│ └── ios
│ ├── screen-ipad-landscape-2x.png
│ ├── screen-ipad-landscape.png
│ ├── screen-ipad-portrait-2x.png
│ ├── screen-ipad-portrait.png
│ ├── screen-iphone-portrait-2x.png
│ ├── screen-iphone-portrait-568h-2x.png
│ └── screen-iphone-portrait.png
└── style.css
/README.md:
--------------------------------------------------------------------------------
1 | PassLok Privacy
2 | ===============
3 |
4 | PassLok is a toolkit that implements public key cryptography and steganography to supplement ANY communications program.
5 |
6 | These are the principles guiding the design of PassLok:
7 | * Perfect portability. Runs on any computer or mobile device.
8 | * Completely self-contained so it runs offline. No servers.
9 | * Nothing should be installed. No required secrets saved.
10 | * Highest-level security at every step. No compromises.
11 | * Easy to understand and use by novices. Graphical interface, as clean and simple as possible. No crypto jargon.
12 |
13 | Because of this, PassLok is pure html code consisting mostly of JavaScript instructions. Its cryptography code is based on Tweet NaCl, also on GitHub. It uses XSalsa20 for symmetric encryption and elliptic curves (Curve25519 and Ed25519) for public-key functions.
14 |
15 | PassLok was started as URSA, also by F. Ruiz, and developed privately up to version 1.3.03, made on 8/15/13. Commits on GitHub began seriously with this version. The engine was based on the SJCL library up to version 2.1.03, which has been forked out on this repository in order to preserve it.
16 |
17 | These are the open source libraries used in PassLok, which can be found in the js-opensrc directory:
18 | * Shamir Secret Sharing Scheme. Edited so NaCl RNG is used instead of built-in RNG: https://github.com/amper5and/secrets.js
19 | * DOMpurify XSS filter: https://github.com/cure53/DOMPurify
20 | * Tweet NaCl in JavaScript: https://github.com/dchest/tweetnacl-js
21 | * ed2curve-js conversion of curve coordinates: https://github.com/dchest/ed2curve-js
22 | * SCRYPT key stretching, edited to make it synchronous. https://github.com/dchest/scrypt-async-js
23 | * lz-string compression algorithm: https://github.com/pieroxy/lz-string
24 | * jpeg image steganography: https://github.com/owencm/js-steg
25 | * color picker by Jan Odvarko. Edited so images are included, and to make it smaller: https://github.com/odvarko/JSColor
26 |
27 | The PassLok original code is in directories js-head and js-body:
28 | * this only loads two word arrays: wordlist and blacklist: dictionary_en.js
29 | * Key and Lock functions: keylock.js
30 | * cryptographic functions: crypto-main.js
31 | * signatures and other crypto: crypto-extra.js
32 | * extra functions for mail, etc.: mail&chat.js
33 | * Shamir Secret Sharing Scheme: SSSS.js
34 | * text steganograghy: textstego.js
35 | * image steganography: imagestego.js
36 | * local Directory functions: localdir.js
37 | * functions for switching screens, etc.: screens.js
38 | * special functions that work only with Chrome apps and extensions: Chromestuff.js
39 | * window reformatting, special functions: bodyscript.js
40 | * initialization, button connections: initbuttons.js
41 |
42 | One component run inside and iframes and is served from a different source. It is not included here because we use Phonegap so generate automatically some versions from this repo, and it should not contain that code. Those components are:
43 | * PassLok General Directory: https://github.com/fruiz500/PassLok-GenDir
44 |
45 | Full documentation can be found at: including:
46 | * user manual: http://passlok.weebly.com/uploads/2/4/1/8/24187628/passlok_manual.pdf
47 | * technical design document: http://www.weebly.com/uploads/2/4/1/8/24187628/passlok_technical_document.pdf
48 | * and a number of articles and video tutorials.
49 |
50 | License
51 | -------
52 |
53 | Copyright (C) 2024 Francisco Ruiz
54 |
55 | This program is free software: you can redistribute it and/or modify
56 | it under the terms of the GNU General Public License as published by
57 | the Free Software Foundation, either version 3 of the License, or
58 | (at your option) any later version.
59 |
60 | This program is distributed in the hope that it will be useful,
61 | but WITHOUT ANY WARRANTY; without even the implied warranty of
62 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63 | GNU General Public License for more details.
64 |
65 | You should have received a copy of the GNU General Public License
66 | along with this program. If not, see .
67 |
68 | Acknowledgements
69 | ----------------
70 |
71 | PassLok contains and/or links to code from a number of open source
72 | projects on GitHub, including the Tweet NaCl crypto library, and others.
73 |
74 | Cryptography Notice
75 | -------------------
76 |
77 | This distribution includes cryptographic software. The country in
78 | which you currently reside may have restrictions on the import,
79 | possession, use, and/or re-export to another country, of encryption
80 | software. BEFORE using any encryption software, please check your
81 | country's laws, regulations and policies concerning the import,
82 | possession, or use, and re-export of encryption software, to see if
83 | this is permitted. See for more
84 | information.
85 |
86 | The U.S. Government Department of Commerce, Bureau of Industry and
87 | Security (BIS), has classified this software as Export Commodity
88 | Control Number (ECCN) 5D002.C.1, which includes information security
89 | software using or performing cryptographic functions with asymmetric
90 | algorithms. The form and manner of this distribution makes it
91 | eligible for export under the License Exception ENC Technology
92 | Software Unrestricted (TSU) exception (see the BIS Export
93 | Administration Regulations, Section 740.13) for both object code and
94 | source code.
95 |
--------------------------------------------------------------------------------
/background.js:
--------------------------------------------------------------------------------
1 | //this one opens tabs as directed by the extension
2 | chrome.action.onClicked.addListener(function(){
3 | chrome.tabs.create({url: 'index.html'})
4 | }
5 | );
--------------------------------------------------------------------------------
/chat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PassLok Chat
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/chatscript.js:
--------------------------------------------------------------------------------
1 | window.onload = function() {
2 | chatFrame.src = "https://passlok.com/chat/chat.html" + chatToken; //includes the # sign
3 | chatFrame.height = document.documentElement.clientHeight
4 | }
5 |
6 | //this is extracted from the loading URL, and passed on to the frame
7 | var chatToken = decodeURI(location.hash)
--------------------------------------------------------------------------------
/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | PassLok
10 |
11 |
12 | High strength accessible cryptography.
13 |
14 |
15 |
16 | Francisco Ruiz
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fruiz500/PassLok-Privacy/7a226a966489b4e4e53daa19e34a9b48fe773d2c/favicon.ico
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fruiz500/PassLok-Privacy/7a226a966489b4e4e53daa19e34a9b48fe773d2c/icon.png
--------------------------------------------------------------------------------
/js-body/bodyscript.js:
--------------------------------------------------------------------------------
1 | //this is the part of the javascript code that must be within the body
2 |
3 | //detect browser and device
4 | var isMobile = (typeof window.orientation != 'undefined'),
5 | isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1,
6 | isFirefox = typeof InstallTrigger !== 'undefined', // Firefox 1.0+
7 | isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0, // At least Safari 3+: "[object HTMLElementConstructor]"
8 | isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0, // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
9 | isIE = /*@cc_on!@*/false || !!document.documentMode, // At least IE6
10 | isiPad = (navigator.userAgent.match(/iPad/i) != null),
11 | isiPhone = (navigator.userAgent.match(/iPhone|iPod/i) != null),
12 | isiOS = (isiPhone || isiPad),
13 | isAndroid = (isMobile && !isiOS),
14 | isAndroidTablet = (navigator.userAgent.match(/mobile/i) == null && isAndroid),
15 | isAndroidPhone = (navigator.userAgent.match(/mobile/i) != null && isAndroid),
16 | isFile = (window.location.protocol == 'file:');
17 | textheight();
18 |
19 | // Clear out "sorry, no JavaScript" warning and display the type of source
20 | showGreeting();
21 |
22 | //set global variable indicating if there is a Chrome sync data area. Works for Chrome and Firefox apps and extension
23 | var ChromeSyncOn = false;
24 | if(chrome){
25 | if(chrome.storage){
26 | if(chrome.storage.sync){
27 | ChromeSyncOn = true
28 | }
29 | }
30 | }
31 |
32 | //clears the no JavaScript warning and displays an initial message depending on the type of source
33 | function showGreeting(){
34 | var protocol = window.location.protocol;
35 | var msgStart = "Welcome to PassLok\r\n",
36 | msgEnd = "\r\nSelect your user name and enter your Key. Then click OK";
37 | if(protocol == 'file:'){
38 | pwdMsg.textContent = msgStart + 'running from a saved file' + msgEnd
39 | }else if(protocol == 'https:'){
40 | pwdMsg.textContent = msgStart + 'downloaded from a secure server' + msgEnd
41 | }else if(protocol == 'chrome-extension:' || protocol == 'moz-extension:'){
42 | pwdMsg.textContent = msgStart + 'running as a Chrome or Firefox addon' + msgEnd
43 | }else{
44 | mainTab.style.backgroundColor = '#ffd0ff';
45 | pwdMsg.textContent = msgStart + 'WARNING: running from an insecure source!' + msgEnd
46 | }
47 | }
48 |
49 | //resizes text boxes so they fit within the window
50 | function textheight(){
51 | var fullheight = document.documentElement.clientHeight,
52 | offsetheight = 392,
53 | toolbarheight = 50;
54 | if(isiPhone) offsetheight = offsetheight - 65;
55 | lockBox.style.height = (fullheight - 300) * (isMobile ? 1 : 0.8) + 'px';
56 | if(niceEditor){
57 | mainBox.style.height = fullheight - offsetheight - toolbarheight + 'px'
58 | }else{
59 | if(isMobile){
60 | if(isAndroid && !isFile){
61 | mainBox.style.height = fullheight - offsetheight + 40 + 'px';
62 | }else if(isiPhone){
63 | mainBox.style.height = fullheight - offsetheight - 10 + 'px';
64 | }else{
65 | mainBox.style.height = fullheight - offsetheight + 'px';
66 | }
67 | }else{
68 | mainBox.style.height = fullheight - offsetheight - 10 + 'px';
69 | }
70 | fileImg.style.height = parseInt(mainBox.style.height.slice(0,-2)) - 90 + 'px'
71 | }
72 | }
73 |
74 | //this one is called by window.onload below
75 | function loadFileAsURL(){
76 | var fileToLoad = mainFile.files[0],
77 | fileReader = new FileReader();
78 | fileReader.onload = function(fileLoadedEvent){
79 | var fileName = fileToLoad.name;
80 | var URLFromFileLoaded = fileLoadedEvent.target.result;
81 | if(URLFromFileLoaded.length > 2000000){
82 | var reply = confirm("This file is larger than 1.5MB and Chrome won't save it. Do you want to continue loading it?");
83 | if(!reply){
84 | mainMsg.textContent = 'File load canceled';
85 | return
86 | }
87 | }
88 | if(fileToLoad.type.slice(0,4) == "text"){
89 | if(URLFromFileLoaded.slice(0,2) == '==' && URLFromFileLoaded.slice(-2) == '=='){
90 | var fileLink = document.createElement("a");
91 | fileLink.download = fileName;
92 | fileLink.href = "data:," + decryptSanitizer(URLFromFileLoaded); //filter before adding to the DOM
93 | fileLink.textContent = fileName;
94 | mainBox.appendChild(fileLink)
95 | }else{
96 | var spacer = document.createElement("br"),
97 | textDiv = document.createElement("div");
98 | textDiv.textContent = decryptSanitizer(URLFromFileLoaded).replace(/ /g,' ');
99 | mainBox.appendChild(spacer);
100 | mainBox.appendChild(textDiv)
101 | }
102 | }else{
103 | var fileLink = document.createElement("a");
104 | fileLink.download = fileName;
105 | fileLink.href = decryptSanitizer(URLFromFileLoaded).replace(/=+$/,'');
106 | fileLink.textContent = fileName;
107 | mainBox.appendChild(fileLink)
108 | }
109 | mainFile.type = '';
110 | mainFile.type = 'file' //reset file input
111 | };
112 | if(fileToLoad.type.slice(0,4) == "text"){
113 | fileReader.readAsText(fileToLoad, "UTF-8");
114 | mainMsg.textContent = 'This is the content of file: ' + fileToLoad.name;
115 | }else{
116 | fileReader.readAsDataURL(fileToLoad, "UTF-8");
117 | mainMsg.textContent = 'The file has been loaded in encoded form. It is NOT ENCRYPTED';
118 | }
119 | setTimeout(function(){
120 | updateButtons();
121 | if(decryptBtn.textContent == 'Decrypt') mainMsg.textContent = 'This file may contain an encrypted item';
122 | if(verifyBtn.textContent == 'Unseal') mainMsg.textContent = 'This file may contain a sealed item';
123 | if(secretShareBtn.textContent == 'Join') mainMsg.textContent = 'This file may contain a part. Add more parts if necessary';
124 | },300);
125 | }
126 |
127 | //to load a file into the directory dialog
128 | function loadLockFile(){
129 | var fileToLoad = lockFile.files[0],
130 | fileReader = new FileReader();
131 | fileReader.onload = function(fileLoadedEvent){
132 | var fileName = fileToLoad.name;
133 | URLFromFileLoaded = fileLoadedEvent.target.result,
134 | escapedName = escapeHTML(fileName);
135 | lockBox.textContent = '';
136 | var fileLink = document.createElement('a');
137 | fileLink.download = escapedName;
138 | fileLink.href = decryptSanitizer(URLFromFileLoaded).replace(/=+$/,'');
139 | fileLink.textContent = escapedName;
140 | lockBox.appendChild(fileLink);
141 | lockFile.type = '';
142 | lockFile.type = 'file' //reset file input
143 | };
144 |
145 | fileReader.readAsDataURL(fileToLoad, "UTF-8");
146 | lockMsg.textContent = 'File ' + escapedName + ' has been loaded'
147 | }
148 |
149 | //to load an image into the main box
150 | function loadImage(){
151 | var fileToLoad = imgFile.files[0],
152 | fileReader = new FileReader();
153 | fileReader.onload = function(fileLoadedEvent){
154 | var URLFromFileLoaded = fileLoadedEvent.target.result;
155 | if(URLFromFileLoaded.slice(0,10) != 'data:image'){
156 | mainMsg.textContent = 'This file is not a recognized image type';
157 | return
158 | }
159 | var image = document.createElement("img");
160 | image.src = decryptSanitizer(URLFromFileLoaded).replace(/=+$/,'');
161 | mainBox.appendChild(image)
162 | imgFile.type = '';
163 | imgFile.type = 'file' //reset file input
164 | };
165 |
166 | fileReader.readAsDataURL(fileToLoad, "UTF-8");
167 | }
168 |
169 | //operates when the Save button is clicked
170 | function saveFiles(){
171 | mainBox.contentEditable = 'false';
172 | var files = mainBox.querySelectorAll('a'),
173 | length = files.length; //since files will be loaded as links in the main box
174 | for(var i = 0; i < length; i++){ //download all files
175 | if(files[i].href.includes('data:')) files[i].click()
176 | }
177 | mainBox.contentEditable = 'true'
178 | }
179 |
--------------------------------------------------------------------------------
/js-body/initbuttons.js:
--------------------------------------------------------------------------------
1 | // initialize things
2 | window.onload = function() {
3 |
4 | if(isMobile){
5 | mainMsg.style.height = '20px';
6 | imgSpacer.style.display = 'none';
7 | // partSpacer1.style.display = 'none';
8 | // partSpacer2.style.display = 'none';
9 | // decoyInSpacer1.style.display = 'none';
10 | // decoyInSpacer2.style.display = 'none';
11 | // decoyOutSpacer.style.display = 'none';
12 | qrcodeImg.style.width = '100%';
13 | qrcodeImg.style.left = '0';
14 | var bottomButtons = mainbuttonsbot.children;
15 | for(var i = 0; i < bottomButtons.length; i++) bottomButtons[i].classList.add("narrow")
16 | }else{
17 | previewImg.style.width = "80%" //smaller image on PCs
18 | }
19 | if(isAndroid){ //resize shift buttons on Android
20 | extra2mainBtn.style.padding = '11px';
21 | main2extraBtn.style.padding = '11px'
22 | }
23 | if(isiOS) encodeJPGBtn.style.display = 'none'; //JPG hide does not work on iOS
24 | if(isiPhone || isAndroidPhone){ //to make things fit on narrow screens
25 | anonLabel.textContent = ' Anon. ';
26 | // modeLabel.style.display = 'none';
27 | // otherLabel.style.display = 'none';
28 | greenLabel.textContent = 'Grn';
29 | customLabel.textContent = 'Cust';
30 | backgroundLabel.textContent = 'Bg.';
31 | sentencesLabel.textContent = 'Sentence';
32 | lockScr.style.top = "5%";
33 | lockScr.style.left = "5%";
34 | lockScr.style.width = "90%";
35 | lockScr.style.height = "90%"
36 | }
37 |
38 | //load field icons
39 | pwdIntroIcon.src = eyeImg;
40 | pwdIcon.src = eyeImg;
41 | newKeyIcon.src = eyeImg;
42 | decoyInIcon.src = eyeImg;
43 | decoyOutIcon.src = eyeImg;
44 | imageIcon.src = eyeImg;
45 |
46 | //event listeners for buttons etc.
47 | window.addEventListener('resize',textheight);
48 |
49 | mainFile.addEventListener('change', loadFileAsURL);
50 | mainFile.addEventListener('click', function(){this.value = '';});
51 |
52 | imgFile.addEventListener('change', loadImage);
53 | imgFile.addEventListener('click', function(){this.value = '';});
54 |
55 | imageFile.addEventListener('change', importImage);
56 | imageFile.addEventListener('click', function(){this.value = '';});
57 |
58 | imageFileEmail.addEventListener('change', importImage);
59 | imageFileEmail.addEventListener('click', function(){this.value = '';});
60 |
61 | lockFile.addEventListener('change', loadLockFile);
62 | lockFile.addEventListener('click', function(){this.value = '';});
63 |
64 | encodePNGBtn.addEventListener('click', encodePNG);
65 |
66 | encodeJPGBtn.addEventListener('click', encodeJPG);
67 |
68 | decodeImgBtn.addEventListener('click', decodeImage);
69 |
70 | imageIcon.addEventListener('click',function(){showPwd('image')});
71 |
72 | showLockBtn.addEventListener('click', showLock);
73 |
74 | selectMainBtn.addEventListener('click', selectMain);
75 |
76 | clearMainBtn.addEventListener('click', clearMain);
77 |
78 | showLockBtnBasic.addEventListener('click', showLock);
79 |
80 | decryptBtn.addEventListener('click', lockBtnAction);
81 |
82 | verifyBtn.addEventListener('click', signVerify);
83 |
84 | main2extraBtn.addEventListener('click', main2extra);
85 |
86 | decryptBtnBasic.addEventListener('click', lockBtnAction);
87 |
88 | decryptBtnEmail.addEventListener('click', lockBtnAction);
89 |
90 | extra2mainBtn.addEventListener('click', main2extra);
91 |
92 | niceEditBtn.addEventListener('click', toggleRichText);
93 |
94 | chatBtn.addEventListener('click', Chat);
95 |
96 | sendSMSBtn.addEventListener('click', sendSMS);
97 |
98 | secretShareBtn.addEventListener('click', splitJoin);
99 |
100 | stegoBtn.addEventListener('click', textStego);
101 |
102 | stegoBtnEmail.addEventListener('click', textStego);
103 |
104 | image2mainBtn.addEventListener('click', image2main);
105 |
106 | clearLocksBtn.addEventListener('click', clearLocks);
107 |
108 | addLockBtn.addEventListener('click', function(){addLock(false)});
109 |
110 | renameLockBtn.addEventListener('click', renameLock);
111 |
112 | resetPFSBtn.addEventListener('click', resetPFS);
113 |
114 | showLockDBBtn.addEventListener('click', showLockDB);
115 |
116 | mergeLockDBBtn.addEventListener('click', mergeLockDB);
117 |
118 | moveLockDBBtn.addEventListener('click', moveLockDB);
119 |
120 | acceptKeyBtn.addEventListener('click', acceptpwd);
121 |
122 | cancelKeyBtn.addEventListener('click', cancelKey);
123 |
124 | skipIntroBtn.addEventListener('click', cancelKey);
125 |
126 | changeNameBtn.addEventListener('click', showName);
127 |
128 | changeKeyBtn.addEventListener('click', acceptnewKey);
129 |
130 | changeEmailBtn.addEventListener('click', showEmail);
131 |
132 | backupSettingsBtn.addEventListener('click', moveMyself);
133 |
134 | basicMode.addEventListener('click', mode2basic);
135 |
136 | advancedMode.addEventListener('click', mode2adv);
137 |
138 | emailMode.addEventListener('click', mode2email);
139 |
140 | dropMode.addEventListener('click', mode2drop);
141 |
142 | liteStyle.addEventListener('click', selectStyle);
143 |
144 | darkStyle.addEventListener('click', selectStyle);
145 |
146 | redStyle.addEventListener('click', selectStyle);
147 |
148 | greenStyle.addEventListener('click', selectStyle);
149 |
150 | blueStyle.addEventListener('click', selectStyle);
151 |
152 | customStyle.addEventListener('click', selectStyle);
153 |
154 | colorPicker.addEventListener('change',updateColor);
155 |
156 | rndColors.addEventListener('click', randomColors);
157 |
158 | editTabColor.addEventListener('click', initPicker);
159 |
160 | editBgColor.addEventListener('click', initPicker);
161 |
162 | editBtnColor.addEventListener('click', initPicker);
163 |
164 | editBoxColor.addEventListener('click', initPicker);
165 |
166 | anonMode.addEventListener('click', checkboxStore);
167 |
168 | signedMode.addEventListener('click', checkboxStore);
169 |
170 | onceMode.addEventListener('click', checkboxStore);
171 |
172 | learnMode.addEventListener('click', checkboxStore);
173 |
174 | longMode.addEventListener('click', checkboxStore);
175 |
176 | shortMode.addEventListener('click', checkboxStore);
177 |
178 | compatMode.addEventListener('click', checkboxStore);
179 |
180 | decoyMode.addEventListener('click', checkboxStore);
181 |
182 | wordLockMode.addEventListener('click', checkboxStore);
183 |
184 | ezLokMode.addEventListener('click', checkboxStore);
185 |
186 | normalLockMode.addEventListener('click', checkboxStore);
187 |
188 | fileMode.addEventListener('click', checkboxStore);
189 |
190 | binaryMode.addEventListener('click', checkboxStore);
191 |
192 | textMode.addEventListener('click', checkboxStore);
193 |
194 | qrMode.addEventListener('click', checkboxStore);
195 |
196 | includeMode.addEventListener('click', checkboxStore);
197 |
198 | chromeSyncMode.addEventListener('click', checkboxStore);
199 |
200 | sentenceMode.addEventListener('click', checkboxStore);
201 |
202 | letterMode.addEventListener('click', checkboxStore);
203 |
204 | invisibleMode.addEventListener('click', checkboxStore);
205 |
206 | wordMode.addEventListener('click', checkboxStore);
207 |
208 | spaceMode.addEventListener('click', checkboxStore);
209 |
210 | pwdIcon.addEventListener('click',function(){showPwd('pwd')});
211 |
212 | introRandomBtn.addEventListener('click', randomToken);
213 |
214 | clearIntroRandomBtn.addEventListener('click', clearIntroEmail);
215 |
216 | randomEmailBtn.addEventListener('click', randomToken);
217 |
218 | acceptEmailBtn.addEventListener('click', email2any);
219 |
220 | cancelEmailBtn.addEventListener('click', cancelEmail);
221 |
222 | acceptNameBtn.addEventListener('click', name2any);
223 |
224 | cancelNameBtn.addEventListener('click', cancelName);
225 |
226 | pwdIntroIcon.addEventListener('click', function(){showPwd('pwdIntro')});
227 |
228 | clearIntroBtn.addEventListener('click', clearIntro);
229 |
230 | suggestIntroBtn.addEventListener('click', suggestIntro);
231 |
232 | showlockIntroBtn.addEventListener('click', initUser);
233 |
234 | decoyInIcon.addEventListener('click', function(){showPwd('decoyIn')});
235 |
236 | submitDecoyBtn.addEventListener('click', acceptdecoyIn);
237 |
238 | cancelDecoyBtn.addEventListener('click', cancelDecoy);
239 |
240 | decoyOutIcon.addEventListener('click', function(){showPwd('decoyOut')});
241 |
242 | submitDecoy2Btn.addEventListener('click', acceptdecoyIn);
243 |
244 | cancelDecoy2Btn.addEventListener('click', cancelDecoy);
245 |
246 | submitPartsBtn.addEventListener('click', submitParts);
247 |
248 | cancelPartsBtn.addEventListener('click', cancelPartsIn);
249 |
250 | cancelChatBtn.addEventListener('click', closeBox);
251 |
252 | submitChatBtn.addEventListener('click', makeChat);
253 |
254 | lockList.addEventListener('change', function(){
255 | if(dropMode.checked){
256 | updateUsers() //for File Drop mode
257 | }else{
258 | fillBox()
259 | }
260 | });
261 |
262 | lockList.addEventListener('click', updateButtons);
263 |
264 | makeKeyBtn.addEventListener('click',makeKeyFile);
265 |
266 | resetListBtn.addEventListener('click', resetList);
267 |
268 | main2lockBtn.addEventListener('click', main2lock);
269 |
270 | lock2mainBtn.addEventListener('click', main2lock);
271 |
272 | newUserBtn.addEventListener('click', newUser);
273 |
274 | submitKeyChangeBtn.addEventListener('click', acceptnewKey);
275 |
276 | cancelKeyChangeBtn.addEventListener('click', cancelKeyChange);
277 |
278 | newKeyIcon.addEventListener('click',function(){showPwd('newKey')});
279 |
280 | gotointro2.addEventListener('click', go2intro2);
281 |
282 | backtointro1.addEventListener('click', go2intro2);
283 |
284 | gotointro3.addEventListener('click', go2intro3);
285 |
286 | backtointro2.addEventListener('click', go2intro3);
287 |
288 | gotointro4.addEventListener('click', go2intro4);
289 |
290 | backtointro3.addEventListener('click', go2intro4);
291 |
292 | gotointro5.addEventListener('click', go2intro5);
293 |
294 | backtointro4.addEventListener('click', go2intro5);
295 |
296 | mainBox.addEventListener('keyup', charsLeft);
297 |
298 | mainBox.addEventListener('paste', pasteMain);
299 |
300 | decoyText.addEventListener('keyup', charsLeft);
301 |
302 | chatDate.addEventListener('keyup', charsLeft);
303 |
304 | pwdBox.addEventListener('keyup',function(event){boxKeyup('pwd',event)});
305 |
306 | pwdIntroBox.addEventListener('keyup',function(event){boxKeyup('pwdIntro',event)});
307 |
308 | decoyInBox.addEventListener('keyup', function(event){boxKeyup('decoyIn',event)});
309 |
310 | decoyOutBox.addEventListener('keyup', function(event){boxKeyup('decoyOut',event)});
311 |
312 | partsIn.addEventListener('keyup', function(event) {partsKeyup(event)}, false);
313 |
314 | newKeyBox.addEventListener('keyup', function(event){boxKeyup('newKey',event)});
315 |
316 | newKey2Box.addEventListener('keyup', function(event) {newKey2up(event)}, false);
317 |
318 | lockBox.addEventListener('keyup', function(event) {lockBoxKeyup(event)}, false);
319 |
320 | lockBox.addEventListener('paste', pasteLock);
321 |
322 | userNameBox.addEventListener('keyup', function(event) {nameKeyup(event)}, false);
323 |
324 | emailBox.addEventListener('keyup', function(event) {emailKeyup(event)}, false);
325 |
326 | imageBox.addEventListener('keyup', function(event){boxKeyup('image',event)});
327 |
328 | qrcodeImg.addEventListener('click', function(){qrcodeImg.style.display = 'none';mainMsg.textContent = 'QR code canceled'});
329 |
330 | //for the rich text editor boxes and buttons
331 | formatBlock.addEventListener("change", function() {formatDoc('formatBlock',this[this.selectedIndex].value);this.selectedIndex=0;});
332 | fontName.addEventListener("change", function() {formatDoc('fontName',this[this.selectedIndex].value);this.selectedIndex=0;});
333 | fontSize.addEventListener("change", function() {formatDoc('fontSize',this[this.selectedIndex].value);this.selectedIndex=0;});
334 | foreColor.addEventListener("change", function() {formatDoc('foreColor',this[this.selectedIndex].value);this.selectedIndex=0;});
335 | backColor.addEventListener("change", function() {formatDoc('backColor',this[this.selectedIndex].value);this.selectedIndex=0;});
336 |
337 | document.images[0].addEventListener("click", function() {formatDoc('bold')});
338 | document.images[1].addEventListener("click", function() {formatDoc('italic')});
339 | document.images[2].addEventListener("click", function() {formatDoc('underline')});
340 | document.images[3].addEventListener("click", function() {formatDoc('strikethrough')});
341 | document.images[4].addEventListener("click", function() {formatDoc('subscript')});
342 | document.images[5].addEventListener("click", function() {formatDoc('superscript')});
343 | document.images[6].addEventListener("click", function() {formatDoc('justifyleft')});
344 | document.images[7].addEventListener("click", function() {formatDoc('justifycenter')});
345 | document.images[8].addEventListener("click", function() {formatDoc('justifyright')});
346 | document.images[9].addEventListener("click", function() {formatDoc('justifyfull')});
347 | document.images[10].addEventListener("click", function() {formatDoc('insertorderedlist')});
348 | document.images[11].addEventListener("click", function() {formatDoc('insertunorderedlist')});
349 | document.images[12].addEventListener("click", function() {formatDoc('formatBlock','blockquote')});
350 | document.images[13].addEventListener("click", function() {formatDoc('outdent')});
351 | document.images[14].addEventListener("click", function() {formatDoc('indent')});
352 | document.images[15].addEventListener("click", function() {formatDoc('inserthorizontalrule')});
353 | document.images[16].addEventListener("click", function() {var sLnk=prompt('Write the URL here','http:\/\/');if(sLnk&&sLnk!=''&&sLnk!='http://'){formatDoc('createlink',sLnk)}});
354 | document.images[17].addEventListener("click", function() {formatDoc('unlink')});
355 | document.images[18].addEventListener("click", function() {formatDoc('removeFormat')});
356 | document.images[19].addEventListener("click", function() {formatDoc('undo')});
357 | document.images[20].addEventListener("click", function() {formatDoc('redo')});
358 | document.images[23].addEventListener("click", saveFiles);
359 |
360 | //for the help screens
361 | var helpHeaders = document.getElementsByClassName("helpHeading"); //add listeners to all the help headers
362 |
363 | for (var i = 0; i < helpHeaders.length; i++) {
364 | helpHeaders[i].addEventListener('click', openHelp);
365 | }
366 |
367 | var helpHeaders2 = document.getElementsByClassName("helpHeading2"); //2nd level help
368 |
369 | for (var i = 0; i < helpHeaders2.length; i++) {
370 | helpHeaders2[i].addEventListener('click', openHelp2);
371 | }
372 |
373 | //fixes after inline styles were moved to css file
374 | // lockList.style.padding = '4px';
375 | // lockList.style.width = '30%';
376 | basicBtnsTop.style.display = 'block';
377 | // mainMsg.style.minHeight = '20px';
378 | extraButtonsTop.style.display = 'none'
379 | };
380 |
381 | //fix for iOS Safari upon startup
382 | if(isMobile){
383 | if(window.location.hash.match('#a') && window.location.hash.length < 10) window.location.hash = ''
384 | }
385 |
386 | if(!localStorage){newUser();}else if(localStorage.length == 0){newUser();}else if(localStorage.length == 1 && localStorage['randid']){newUser();};
387 |
388 | //initialize QRcode
389 | var qrcode = new QRCode(document.getElementById("qrcodeImg"), {
390 | width : 290,
391 | height : 290
392 | });
393 |
394 | fillNameList();
395 |
396 | initTabs(); //initialize tabs
397 |
398 | //var time10 = hashTime10(); //get milliseconds for 10 wiseHash at iter = 10
399 | var time10 = 200 //valid for core2 duo. Should be smaller for more recent machines
400 |
401 | //end of body script.
402 |
--------------------------------------------------------------------------------
/js-head/Chromestuff.js:
--------------------------------------------------------------------------------
1 | //to put Lock into sync storage
2 | function syncChromeLock(name,data) {
3 | var syncName = userName + '.' + name,
4 | jsonfile = {};
5 | jsonfile[syncName.toLowerCase()] = data;
6 | chrome.storage.sync.set(jsonfile);
7 |
8 | //now update the list, also in Chrome sync
9 | if(name != 'myself') updateChromeSyncList()
10 | }
11 |
12 | //to update the stored list
13 | function updateChromeSyncList(){
14 | var ChromeSyncList = lockNames.join('|'),
15 | jsonfile2 = {};
16 | jsonfile2[userName + '.ChromeSyncList'] = ChromeSyncList;
17 | chrome.storage.sync.set(jsonfile2)
18 | }
19 |
20 | //to retrieve Lock from sync storage. The code specifying what to do with the item is here because the get operation is asynchronous
21 | function getChromeLock(name) {
22 | var syncName = userName + '.' + name;
23 | chrome.storage.sync.get(syncName.toLowerCase(), function (obj){
24 | var lockdata = obj[syncName.toLowerCase()];
25 | if(lockdata){
26 | storeChromeLock(name,lockdata)
27 | }else if(name.slice(0,2) != '--'){
28 | var name2 = '--' + name.toLowerCase() + '--'; //it may be a List, so change the name and try again
29 | lockdata = obj[name2];
30 | getChromeLock(name2)
31 | }else{
32 | if (name.slice(0,2) == '--') name = name.slice(2,name.length-2);
33 | lockMsg.textContent = name + ' not found in Chrome sync'
34 | }
35 | })
36 | }
37 |
38 | //this one is called by the above function
39 | function storeChromeLock(name,lockdata){
40 | locDir[name] = JSON.parse(lockdata);
41 | lockBox.textContent = removeHTMLtags(locDir[name][0]); //extra precaution, in case something slipped in
42 | lockMsg.textContent = name + ' added from Chrome sync';
43 | locDir = sortObject(locDir);
44 | localStorage[userName] = JSON.stringify(locDir);
45 | lockNames = Object.keys(locDir);
46 | fillList();
47 | updateChromeSyncList()
48 | }
49 |
50 | //to completely remove an entry
51 | function remChromeLock(name){
52 | var syncName = userName + '.' + name;
53 | chrome.storage.sync.remove(syncName.toLowerCase());
54 | updateChromeSyncList()
55 | }
56 |
57 | //this one controls an asynchronous loop
58 | var asyncLoop = function(o){
59 | var i=-1;
60 |
61 | var loop = function(){
62 | i++;
63 | if(i==o.length){o.callback(); return;}
64 | o.functionToLoop(loop, i)
65 | }
66 | loop() //init
67 | }
68 |
69 | //get Lock list from Chrome sync, then call an asynchronous loop to retrieve the data
70 | function retrieveAllSync(){
71 | var syncName = userName + '.ChromeSyncList';
72 | chrome.storage.sync.get(syncName, function(obj){
73 | var lockdata = obj[syncName];
74 | if(lockdata){
75 | var ChromeSyncList = lockdata.split('|');
76 |
77 | //asynchronous loop to fill local directory
78 | asyncLoop({
79 | length : ChromeSyncList.length,
80 |
81 | functionToLoop : function(loop, i){
82 | if (ChromeSyncList[i] != 'myself'){
83 | var syncName2 = userName + '.' + ChromeSyncList[i];
84 | var lockdata2 = {};
85 | chrome.storage.sync.get(syncName2.toLowerCase(), function (obj) {
86 | lockdata2 = obj[syncName2.toLowerCase()];
87 | locDir[ChromeSyncList[i]] = JSON.parse(lockdata2);
88 | locDir = sortObject(locDir);
89 | localStorage[userName] = JSON.stringify(locDir);
90 | lockNames = Object.keys(locDir);
91 | fillList()
92 | })
93 | }
94 | loop()
95 | },
96 |
97 | callback : function(){ //not used here
98 | }
99 | })
100 | //end of asynchronous loop, any code below won't wait for it to be done
101 |
102 | }
103 | })
104 | }
105 |
--------------------------------------------------------------------------------
/js-head/SSSS.js:
--------------------------------------------------------------------------------
1 | //function that starts it all when the Split/Join button is pushed
2 | function splitJoin(){
3 | blinkMsg(mainMsg); //Get blinking message started
4 | setTimeout(function(){ //the rest after a 20 ms delay
5 | secretshare()
6 | },20) //end of timeout
7 | }
8 |
9 | //this function implements the Shamir Secret Sharing Scheme, taking the secret from the main box and putting the result back there, and vice-versa.
10 | function secretshare(){
11 | var main = mainBox.innerHTML.trim(), //innerHTML to preserve links
12 | tags = main.match(/PL\d{2}p\d{3}/);
13 | if(tags){ //main box has parts: join parts
14 | if(main.match('href="data:')){ //parts in links
15 | var shares = main.replace(//g,'
').replace(/<\div>/g,"").replace(/
/g,"").split(" ").filter(Boolean) //go from newline-containing string to array
16 | }else{
17 | var shares = mainBox.innerText.split("\n\n").filter(Boolean) //split when double spaced
18 | }
19 | var n = shares.length,
20 | quorum = parseInt(tags[0].slice(-3)); //quorum in tags is "p" plus 3 digits in a row, first instance
21 | if(n < quorum){ //not enough parts
22 | mainMsg.textContent = 'According to the tags, you need ' + (quorum - n) + ' more parts in the box';
23 | return
24 | }
25 | if(n < quorum){ //not enough parts
26 | mainMsg.textContent = 'According to the tags, you need ' + (quorum - n) + ' more parts in the box';
27 | return
28 | }
29 | //extract shares from links, condition shares for combination
30 | for (var i = 0; i < shares.length; i++) {
31 | if(shares[i].match('href="data:')){ //share is in link
32 | shares[i] = shares[i].match(/,[a-zA-Z0-9\/+]+"/)[0].slice(1,-1)
33 | }else{ //share as text, just remove tags and extra spaces
34 | shares[i] = stripTags(shares[i].replace(/\s/g,''))
35 | }
36 | var shareBin = nacl.util.decodeBase64(shares[i]);
37 | if(!shareBin) return false;
38 | shares[i] = "8" + charArray2hex(shareBin) //convert to hex
39 | }
40 | if(learnMode.checked){
41 | var reply = confirm("The parts in the main box will be joined to retrieve the original item, which will be placed in this box. Please make sure that there are enough parts. Cancel if this is not what you want.");
42 | if(!reply) return
43 | }
44 | if(n === 1){
45 | mainMsg.textContent = 'Only one part in main box';
46 | return
47 | }
48 | try{
49 | var sechex = secrets.combine(shares),
50 | secBin = hex2charArray(sechex);
51 | if(secBin.join().match(",61,34,100,97,116,97,58,")){
52 | var secret = nacl.util.encodeUTF8(secBin)
53 | }else{
54 | var secret = LZString.decompressFromUint8Array(secBin)
55 | }
56 | mainBox.innerHTML = decryptSanitizer(secret); //disable non-whitelisted tags and attributes
57 | mainMsg.textContent = 'Join successful'
58 | }catch(err){
59 | mainMsg.textContent = 'There was an error' //the encodeUTF8 is the likely culprit
60 | }
61 | }else{ //parts not detected, split instead
62 | if(main == "") {
63 | mainMsg.textContent = 'The box is empty';
64 | return
65 | }
66 | if(learnMode.checked){
67 | var reply = confirm("The item in the box will be split into several partial items, which will replace the contents of the box. A popup will ask for the total number of parts, and the minimum needed to reconstruct the original item. Cancel if this is not what you want.");
68 | if(!reply) return
69 | }
70 | var number = partsNumber.value;
71 | if(number.trim() == ""){ //stop to display the entry form if it is empty
72 | partsIn.style.display = "block";
73 | shadow.style.display = "block";
74 | if(!isMobile) partsNumber.focus();
75 | return
76 | }
77 | partsIn.style.display = "none";
78 | shadow.style.display = "none";
79 | var quorum = partsQuorum.value; //this value defaults to the total number if empty
80 | if (quorum.trim() == ""){
81 | quorum = number
82 | }
83 | partsNumber.value = ""; //on re-execution, read the box and reset it
84 | partsQuorum.value = "";
85 | quorum = parseInt(quorum);
86 | number = parseInt(number);
87 | if(number < 2){number = 2} else if(number > 255) {number = 255};
88 | if (quorum > number) quorum = number;
89 | var secret = mainBox.innerHTML.trim();
90 | if(secret.match('="data:')){ //no compression if it includes a file
91 | var secBin = nacl.util.decodeUTF8(secret)
92 | }else{
93 | var secBin = LZString.compressToUint8Array(secret)
94 | }
95 | var sechex = charArray2hex(secBin),
96 | shares = secrets.share(sechex,number,quorum);
97 | displayshare(shares,quorum);
98 | mainMsg.textContent = number + ' parts made. ' + quorum + ' required to reconstruct';
99 | partsInBox = true
100 | }
101 | setTimeout(function(){charsLeft();},20)
102 | }
103 |
104 | function displayshare(shares,quorum){
105 | var length = shares[0].length,
106 | quorumStr = "00" + quorum,
107 | output = "";
108 | quorumStr = quorumStr.substr(quorumStr.length-3);
109 |
110 | mainBox.textContent = '';
111 | var fragment = document.createElement('div');
112 |
113 | for (var i = 0; i < shares.length; i++) {
114 | var dataItem = nacl.util.encodeBase64(hex2charArray(shares[i].slice(1,length))).replace(/=+/g, '');
115 | if(i > 0) output += " ";
116 | if(fileMode.checked){
117 | if(textMode.checked){
118 | output += 'PassLok 2.4 Part out of ' + quorumStr + ' as a text file '
119 | }else{
120 | output += 'PassLok 2.4 Part out of ' + quorumStr + ' as a binary file '
121 | }
122 | }else{
123 | output += "" + ("PL24p" + quorumStr + "==" + dataItem + "==PL24p" + quorumStr).match(/.{1,80}/g).join(" ") + " "
124 | }
125 | };
126 | fragment.innerHTML = output; //must use innerHTML because building the list of links with linefeeds in between with the appendChild method does not work in Chrome (bug?)
127 | mainBox.appendChild(fragment)
128 | }
129 |
130 | //convert an array of 8-bit decimal codes into a hexadecimal string
131 | function charArray2hex(charArray){
132 | var output = '';
133 | for(var i = 0;i < charArray.length; i++){
134 | var newstring = charArray[i].toString(16);
135 | if (newstring.length < 2) newstring = '0' + newstring;
136 | output += newstring
137 | }
138 | return output
139 | }
140 |
141 | //convert a hexadecimal string (two characters per byte) into an array of decimal codes. Wrong codes marked as -1
142 | function hex2charArray(string){
143 | var output = [];
144 | for(var i = 0;i < string.length; i=i+2){
145 | var a = parseInt(string.slice(i,i+2),16);
146 | if(isNaN(a)){
147 | output[i/2] = -1
148 | }else{
149 | output[i/2] = a
150 | }
151 | }
152 | return output
153 | }
154 |
--------------------------------------------------------------------------------
/js-head/colors.js:
--------------------------------------------------------------------------------
1 | //this group creates dummy stylesheets for buttons, tabs, boxes, and background. One sheet for general color and another for text
2 | var btnSheet = (function() {
3 | var style = document.createElement("style");
4 | style.appendChild(document.createTextNode(""));
5 | style.nonce = '4AEemGb0yJptoIGFP3Nd'; //a nonce added because otherwise the CSP of the extension will refuse next statement
6 | document.head.appendChild(style);
7 | return style.sheet
8 | })();
9 | var tabSheet = (function() {
10 | var style = document.createElement("style");
11 | style.appendChild(document.createTextNode(""));
12 | style.nonce = '4AEemGb0yJptoIGFP3Nd';
13 | document.head.appendChild(style);
14 | return style.sheet
15 | })();
16 | var boxSheet = (function() {
17 | var style = document.createElement("style");
18 | style.appendChild(document.createTextNode(""));
19 | style.nonce = '4AEemGb0yJptoIGFP3Nd';
20 | document.head.appendChild(style);
21 | return style.sheet
22 | })();
23 | var bgSheet = (function() {
24 | var style = document.createElement("style");
25 | style.appendChild(document.createTextNode(""));
26 | style.nonce = '4AEemGb0yJptoIGFP3Nd';
27 | document.head.appendChild(style);
28 | return style.sheet
29 | })();
30 | var btnTextSheet = (function() {
31 | var style = document.createElement("style");
32 | style.appendChild(document.createTextNode(""));
33 | style.nonce = '4AEemGb0yJptoIGFP3Nd';
34 | document.head.appendChild(style);
35 | return style.sheet
36 | })();
37 | var tabTextSheet = (function() {
38 | var style = document.createElement("style");
39 | style.appendChild(document.createTextNode(""));
40 | style.nonce = '4AEemGb0yJptoIGFP3Nd';
41 | document.head.appendChild(style);
42 | return style.sheet
43 | })();
44 | var boxTextSheet = (function() {
45 | var style = document.createElement("style");
46 | style.appendChild(document.createTextNode(""));
47 | style.nonce = '4AEemGb0yJptoIGFP3Nd';
48 | document.head.appendChild(style);
49 | return style.sheet
50 | })();
51 | var bgTextSheet = (function() {
52 | var style = document.createElement("style");
53 | style.appendChild(document.createTextNode(""));
54 | style.nonce = '4AEemGb0yJptoIGFP3Nd';
55 | document.head.appendChild(style);
56 | return style.sheet
57 | })();
58 |
59 | //these two are to add a rule (usually a changed color) to a dummy stylesheet, or to remove it
60 | function addCSSRule(sheet, selector, rules, index) {
61 | if("insertRule" in sheet) {
62 | sheet.insertRule(selector + "{" + rules + "}", index)
63 | }
64 | else if("addRule" in sheet) {
65 | sheet.addRule(selector, rules, index)
66 | }
67 | }
68 | function removeCSSRule(sheet, selector, rules, index) {
69 | if("deleteRule" in sheet) {
70 | sheet.deleteRule(selector + "{" + rules + "}", index)
71 | }
72 | else if("removeRule" in sheet) {
73 | sheet.removeRule(selector, rules, index)
74 | }
75 | }
76 |
77 | //stores custom colors
78 | function storeColors(){
79 | if(fullAccess){
80 | var hexCode = '';
81 | for(var i=0; i<4; i++) hexCode += bgColorStore[i];
82 | if(locDir['myself']){
83 | locDir['myself'][2] = hexCode;
84 | localStorage[userName] = JSON.stringify(locDir);
85 |
86 | if(ChromeSyncOn && chromeSyncMode.checked){
87 | syncChromeLock('myself',JSON.stringify(locDir['myself']))
88 | }
89 | }
90 | }
91 | }
92 |
93 | //retrieves custom colors from storage
94 | function getCustomColors(){
95 | if(locDir['myself'][2]){
96 | var hexCode = locDir['myself'][2];
97 | while(hexCode.length < 24) hexCode = '0' + hexCode;
98 | for(var i=0; i<4; i++){
99 | bgColorStore[i] = hexCode.slice(i*6,(i+1)*6);
100 | bgColor[i] = bgColorStore[i];
101 | fgColorStore[i] = foregColor(bgColorStore[i]);
102 | fgColor[i] = fgColorStore[i]
103 | }
104 | }
105 | }
106 |
107 | //deduces foreground color (white or black) from hex background color
108 | function foregColor(bgColor){
109 | var red = parseInt(bgColor.slice(0,2),16),
110 | green = parseInt(bgColor.slice(2,4),16),
111 | blue = parseInt(bgColor.slice(4,6),16);
112 | return 0.213 * red + 0.715 * green + 0.072 * blue < 128 ? 'FFFFFF' : '000000'
113 | }
114 |
115 | //returns a closer to neutral gray version of a hex color, as given by a hex offset (1 byte)
116 | function milder(color, offset){
117 | var red = parseInt(color.slice(0,2),16),
118 | green = parseInt(color.slice(2,4),16),
119 | blue = parseInt(color.slice(4,6),16),
120 | offset10 = parseInt(offset, 16);
121 | if(0.213 * red + 0.715 * green + 0.072 * blue < 128){ //dark or light condition is the same as for foreground color
122 | red = Math.min((red + offset10),255).toString(16);
123 | green = Math.min((green + offset10),255).toString(16);
124 | blue = Math.min((blue + offset10),255).toString(16)
125 | }else{
126 | red = Math.max((red - offset10),0).toString(16);
127 | green = Math.max((green - offset10),0).toString(16);
128 | blue = Math.max((blue - offset10),0).toString(16)
129 | }
130 | if(red.length < 2) red = '0' + red; //take care of single-digit colors
131 | if(green.length < 2) green = '0' + green;
132 | if(blue.length < 2) blue = '0' + blue;
133 | return red + green + blue
134 | }
135 |
136 | function updateColor(){
137 | if(editTabColor.checked) {updateTabColor(true)}
138 | else if(editBgColor.checked) {updateBgColor(true)}
139 | else if(editBtnColor.checked) {updateBtnColor(true)}
140 | else {updateBoxColor(true)}
141 | }
142 |
143 | //global variables containing hex screen colors. Default is Light. Indices: 0 = tabs, 1 = background, 2 = buttons, 3 = boxes
144 | var fgColor = ['202128','000000','666666','000000'],
145 | fgColorStore = ['202128','000000','666666','000000'],
146 | bgColor = ['c6d5c6','ffffff','e6e6e6','fffffd'],
147 | bgColorStore = ['c6d5c6','ffffff','e6e6e6','fffffd'];
148 |
149 | //gets color from picker and puts it into global variables
150 | function getColor(i,isPicker){
151 | if(customStyle.checked){
152 | if(isPicker){
153 | bgColor[i] = colorPicker.value;
154 | fgColor[i] = foregColor(bgColor[i]);
155 | bgColorStore[i] = bgColor[i];
156 | fgColorStore[i] = fgColor[i]
157 | }else{
158 | bgColor[i] = bgColorStore[i];
159 | fgColor[i] = fgColorStore[i]
160 | }
161 | }
162 | }
163 |
164 | //change tab colors (index 0); first delete all rules, then re-make them according to the current color; first background, then text
165 | function updateTabColor(isPicker){
166 | getColor(0,isPicker);
167 | while(tabSheet.cssRules.length) removeCSSRule(tabSheet,"ul#tabs");
168 | addCSSRule(tabSheet, "ul#tabs", "background-color:#" + bgColor[0]);
169 | addCSSRule(tabSheet, "ul#tabs li a", "background-color:#" + bgColor[0]);
170 | addCSSRule(tabSheet, "ul#tabs li a:hover", "background-color:#" + milder(bgColor[0],'22'));
171 | while(tabTextSheet.cssRules.length) removeCSSRule(tabTextSheet,"ul#tabs");
172 | addCSSRule(tabTextSheet, "ul#tabs li a", "color:#" + milder(fgColor[0],'22'))
173 | }
174 |
175 | //same for general background (index 1)
176 | function updateBgColor(isPicker){
177 | getColor(1,isPicker);
178 | while(bgSheet.cssRules.length){
179 | removeCSSRule(bgSheet,"body");
180 | removeCSSRule(bgSheet,".block_page");
181 | removeCSSRule(bgSheet,".white_content");
182 | removeCSSRule(bgSheet,"ul#tabs");
183 | removeCSSRule(bgSheet,"div.tabContent");
184 | removeCSSRule(bgSheet,".helpHeading:hover")
185 | }
186 | addCSSRule(bgSheet, "body", "background-color:#" + bgColor[1]);
187 | addCSSRule(bgSheet, ".block_page", "background-color:#" + bgColor[1]);
188 | addCSSRule(bgSheet, ".white_content", "background-color:#" + bgColor[1]);
189 | addCSSRule(bgSheet, "ul#tabs li a.selected", "background-color:#" + bgColor[1]);
190 | addCSSRule(bgSheet, "div.tabContent", "background-color:#" + bgColor[1]);
191 | addCSSRule(bgSheet, ".helpHeading:hover", "background-color:#" + milder(bgColor[1],'11'));
192 | while(bgTextSheet.cssRules.length){
193 | removeCSSRule(bgTextSheet,"body");
194 | removeCSSRule(bgTextSheet,".block_page");
195 | removeCSSRule(bgTextSheet,".white_content");
196 | removeCSSRule(bgTextSheet,"ul#tabs");
197 | removeCSSRule(bgTextSheet,"div.tabContent")
198 | }
199 | addCSSRule(bgTextSheet, "body", "color:#" + fgColor[1]);
200 | addCSSRule(bgTextSheet, ".block_page", "color:#" + fgColor[1]);
201 | addCSSRule(bgTextSheet, ".white_content", "color:#" + fgColor[1]);
202 | addCSSRule(bgTextSheet, "ul#tabs li a.selected", "color:#" + fgColor[1]);
203 | addCSSRule(bgTextSheet, "div.tabContent", "color:#" + fgColor[1])
204 | }
205 |
206 | //and for buttons (index 2)
207 | function updateBtnColor(isPicker){
208 | getColor(2,isPicker);
209 | while(btnSheet.cssRules.length){
210 | removeCSSRule(btnSheet,".cssbutton");
211 | removeCSSRule(btnSheet,".cssbutton:hover");
212 | removeCSSRule(btnSheet,"#toolBar1")
213 | }
214 | addCSSRule(btnSheet, ".cssbutton", "background-color:#" + bgColor[2]);
215 | addCSSRule(btnSheet, ".cssbutton:hover", "background-color:#" + milder(bgColor[2],'22'));
216 | addCSSRule(btnSheet, "#toolBar1", "background-color:#" + bgColor[2]);
217 | while(btnTextSheet.cssRules.length){
218 | removeCSSRule(btnTextSheet,".cssbutton")
219 | }
220 | addCSSRule(btnTextSheet, ".cssbutton", "color:#" + fgColor[2]);
221 | }
222 |
223 | //and for boxes (index 3)
224 | function updateBoxColor(isPicker){
225 | getColor(3,isPicker);
226 | while(boxSheet.cssRules.length){
227 | removeCSSRule(boxSheet,".cssbox");
228 | removeCSSRule(boxSheet,"select")
229 | }
230 | addCSSRule(boxSheet, ".cssbox", "background-color:#" + bgColor[3]);
231 | addCSSRule(boxSheet, "select", "background-color:#" + bgColor[3]);
232 | while(boxTextSheet.cssRules.length){
233 | removeCSSRule(boxTextSheet,".cssbox");
234 | removeCSSRule(boxTextSheet,"select")
235 | }
236 | addCSSRule(boxTextSheet, ".cssbox", "color:#" + fgColor[3]);
237 | addCSSRule(boxTextSheet, "select", "color:#" + fgColor[3]);
238 | }
239 |
240 | //initialize color picker
241 | function initPicker(){
242 | if(editTabColor.checked){
243 | colorPicker.color.fromString(bgColor[0])
244 | }else if(editBgColor.checked){
245 | colorPicker.color.fromString(bgColor[1])
246 | }else if(editBtnColor.checked){
247 | colorPicker.color.fromString(bgColor[2])
248 | }else {
249 | colorPicker.color.fromString(bgColor[3])
250 | }
251 | }
252 |
253 | //this loads the pre-selected color schemes from Options
254 | function selectStyle(){
255 | if(liteStyle.checked){
256 | customColors.style.display = 'none';
257 | bgColor[0] = 'c6d5c6';
258 | fgColor[0] = '202128';
259 | bgColor[1] = 'ffffff';
260 | fgColor[1] = '000000';
261 | bgColor[2] = 'e6e6e6';
262 | fgColor[2] = '666666';
263 | bgColor[3] = 'fffffd';
264 | fgColor[3] = '000000';
265 | updateTabColor(false);
266 | updateBgColor(false);
267 | updateBtnColor(false);
268 | updateBoxColor(false)
269 | }else if(darkStyle.checked){
270 | customColors.style.display = 'none';
271 | bgColor[0] = '455245';
272 | fgColor[0] = 'e9e9e9';
273 | bgColor[1] = '000000';
274 | fgColor[1] = 'ffffff';
275 | bgColor[2] = '7b7b7b';
276 | fgColor[2] = 'eeeeee';
277 | bgColor[3] = '111111';
278 | fgColor[3] = 'ffffff';
279 | updateTabColor(false);
280 | updateBgColor(false);
281 | updateBtnColor(false);
282 | updateBoxColor(false)
283 | }else if(redStyle.checked){
284 | customColors.style.display = 'none';
285 | bgColor[0] = '000000';
286 | fgColor[0] = 'efefef';
287 | bgColor[1] = 'cc3300';
288 | fgColor[1] = 'ffffff';
289 | bgColor[2] = '663333';
290 | fgColor[2] = 'dddddd';
291 | bgColor[3] = 'ffcc99';
292 | fgColor[3] = '000000';
293 | updateTabColor(false);
294 | updateBgColor(false);
295 | updateBtnColor(false);
296 | updateBoxColor(false)
297 | }else if(greenStyle.checked){
298 | customColors.style.display = 'none';
299 | bgColor[0] = '9999cc';
300 | fgColor[0] = 'ffffff';
301 | bgColor[1] = '99cc99';
302 | fgColor[1] = '000000';
303 | bgColor[2] = 'ddddaa';
304 | fgColor[2] = '666666';
305 | bgColor[3] = 'ffffcc';
306 | fgColor[3] = '000000';
307 | updateTabColor(false);
308 | updateBgColor(false);
309 | updateBtnColor(false);
310 | updateBoxColor(false)
311 | }else if(blueStyle.checked){
312 | customColors.style.display = 'none';
313 | bgColor[0] = '003399';
314 | fgColor[0] = 'efefef';
315 | bgColor[1] = '0099cc';
316 | fgColor[1] = 'ffffff';
317 | bgColor[2] = '1c57cc';
318 | fgColor[2] = 'dddddd';
319 | bgColor[3] = 'd7ffd7';
320 | fgColor[3] = '000000';
321 | updateTabColor(false);
322 | updateBgColor(false);
323 | updateBtnColor(false);
324 | updateBoxColor(false)
325 | }else{
326 | if(customColors.style.display == 'none'){
327 | customColors.style.display = 'block'
328 | }else{
329 | customColors.style.display = 'none'
330 | }
331 | bgColor[0] = bgColorStore[0];
332 | fgColor[0] = fgColorStore[0];
333 | updateTabColor(false);
334 | bgColor[1] = bgColorStore[1];
335 | fgColor[1] = fgColorStore[1];
336 | updateBgColor(false);
337 | bgColor[2] = bgColorStore[2];
338 | fgColor[2] = fgColorStore[2];
339 | updateBtnColor(false);
340 | bgColor[3] = bgColorStore[3];
341 | fgColor[3] = fgColorStore[3];
342 | updateBoxColor(false);
343 | initPicker()
344 | }
345 | checkboxStore()
346 | }
347 |
348 | //selects random colors for the custom setting
349 | function randomColors(){
350 | editBoxColor.checked = true;
351 | for(var i=0; i<4; i++){
352 | bgColor[i] = Math.floor(Math.random()*16777216).toString(16);
353 | while(bgColor[i].length < 6) bgColor[i] = '0' + bgColor[i];
354 | fgColor[i] = foregColor(bgColor[i]);
355 | bgColorStore[i] = bgColor[i];
356 | fgColorStore[i] = fgColor[i]
357 | }
358 | initPicker();
359 | updateTabColor(false);
360 | updateBgColor(false);
361 | updateBtnColor(false);
362 | updateBoxColor(false)
363 | }
364 | //The main script in the head ends here.
365 |
--------------------------------------------------------------------------------
/js-head/license.js:
--------------------------------------------------------------------------------
1 | /*
2 | @source: https://passlok.com/app/index.html
3 |
4 | @licstart The following is the entire license notice for the
5 | JavaScript code in this page.
6 |
7 | Copyright (C) 2025 Francisco Ruiz
8 |
9 | The JavaScript code in this page is free software: you can
10 | redistribute it and/or modify it under the terms of the GNU
11 | General Public License (GNU GPL) as published by the Free Software
12 | Foundation, either version 3 of the License, or (at your option)
13 | any later version. The code is distributed WITHOUT ANY WARRANTY;
14 | without even the implied warranty of MERCHANTABILITY or FITNESS
15 | FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
16 |
17 | As additional permission under GNU GPL version 3 section 7, you
18 | may distribute non-source (e.g., minimized or compacted) forms of
19 | that code without the copy of the GNU GPL normally required by
20 | section 4, provided you include this license notice and a URL
21 | through which recipients can access the Corresponding Source.
22 |
23 |
24 | @licend The above is the entire license notice
25 | for the JavaScript code in this page.
26 | */
27 |
28 | if (window.location.protocol == "http:") { //force SSL/TLS
29 |
30 | var restOfUrl = window.location.href.substr(5);
31 | window.location = "https:" + restOfUrl;
32 | }
33 |
--------------------------------------------------------------------------------
/js-head/mail&chat.js:
--------------------------------------------------------------------------------
1 | //formats results depending on tags present and sends to default email
2 | function sendMail() {
3 | var type = getType(mainBox.innerHTML.trim())[0],
4 | words = mainBox.textContent; //for word Locks
5 | if(words.match('==')) words = words.split('==')[1];
6 | words = words.trim().split(' ');
7 | if(learnMode.checked){
8 | if(type.match(/[lckgdhasoprASO]/)){
9 | var reply = confirm("A new tab will open, including the contents of this box in your default email. You still need to supply the recipient's address and a subject line. Only Locks and encrypted or signed items are allowed. Cancel if this is not what you want.")
10 | }else{
11 | var reply = confirm("An invitation for others to join PassLok and containing your Lock will open in your default email. You still need to supply the recipient's address. Cancel if this is not what you want.")
12 | }
13 | if(!reply) return
14 | }
15 |
16 | if(mainBox.textContent.match("The gibberish link below contains a message from me that has been encrypted with PassLok")){ //invitation message
17 | var link = "mailto:"+ "?subject=Invitation to PassLok" + "&body=" + encodeURIComponent(mainBox.textContent.trim()) + "%0D%0A%0D%0AYou can get PassLok from https://passlok.com/app and other sources, plus the Chrome, Firefox, and Android app stores."
18 | }else{
19 | var hashTag = encodeURIComponent(mainBox.textContent.trim().replace(/ /g,'_')); //item ready for link
20 | var linkText = "Click the link below if you wish to process this automatically using the web app (the app will open in a new tab and ask you for your Key), or simply copy it and paste it into your favorite version of PassLok:%0D%0A%0D%0Ahttps://passlok.com/app#" + hashTag + "%0D%0A%0D%0AYou can get PassLok from https://passlok.com/app and other sources, plus the Chrome, Firefox, and Android app stores.";
21 |
22 | if(words.length == 20){ //20 words, so most likely a word Lock
23 | var link = "mailto:"+ "?subject= My PassLok 2.5 Lock" + "&body=This email contains my PassLok v.2.5 Lock as a list of words. Use it to encrypt text or files for me to decrypt, or to verify my seal.%0D%0A%0D%0A" + linkText
24 | }else if(type=="a" || type=="A"){
25 | if(emailMode.checked){
26 | var link = "mailto:"+ "?subject= " + "&body=" + encodeURIComponent(mainBox.textContent.trim())
27 | }else{
28 | var link = "mailto:"+ "?subject= " + "&body=Anonymous message encrypted with PassLok v.2.5 %0D%0A%0D%0ADecrypt with your secret Key.%0D%0A%0D%0A" + linkText
29 | }
30 | }else if (type=="g" || type=="d" || type=="h"){
31 | if(emailMode.checked){
32 | var link = "mailto:"+ "?subject= " + "&body=" + encodeURIComponent(mainBox.textContent.trim())
33 | }else{
34 | var link = "mailto:"+ "?subject= " + "&body=Message encrypted with PassLok v.2.5 %0D%0A%0D%0ADecrypt with shared Key.%0D%0A%0D%0A" + linkText
35 | }
36 | }else if (type=="s" || type=="S"){
37 | if(emailMode.checked){
38 | var link = "mailto:"+ "?subject= " + "&body=" + encodeURIComponent(mainBox.textContent.trim())
39 | }else{
40 | var link = "mailto:"+ "?subject= " + "&body=Signed message encrypted with PassLok v.2.5 %0D%0A%0D%0ADecrypt with your secret Key and my Lock.%0D%0A%0D%0A" + linkText
41 | }
42 | }else if (type && type.match(/[oprO]/)){
43 | if(emailMode.checked){
44 | var link = "mailto:"+ "?subject= " + "&body=" + encodeURIComponent(mainBox.textContent.trim())
45 | }else{
46 | var link = "mailto:"+ "?subject= " + "&body=Read-once message encrypted with PassLok v.2.5 %0D%0A%0D%0ADecrypt with your secret Key.%0D%0A%0D%0A" + linkText
47 | }
48 | }else if (type=="k"){
49 | var link = "mailto:"+ "?subject=My PassLok database" + "&body=Database encrypted with PassLok v.2.5 %0D%0A%0D%0ADecrypt with my secret Key.%0D%0A%0D%0A" + linkText
50 | }else if (type=="l"){
51 | var link = "mailto:"+ "?subject= " + "&body=Text sealed with PassLok v.2.5. It is not encrypted. Extract it and verify my authorship using my Lock.%0D%0A%0D%0A" + linkText;
52 | }else if (type=='c'){
53 | var link = "mailto:"+ "?subject= My PassLok 2.5 Lock" + "&body=This email contains my PassLok v.2.5 Lock. Use it to encrypt text or files for me to decrypt, or to verify my seal.%0D%0A%0D%0A" + linkText
54 | }
55 | }
56 | if(isMobile){ //new window for PC, same window for mobile
57 | if(isChrome && isAndroid){
58 | mainMsg.textContent = "Android Chrome does not allow communication with the email app"
59 | }else{
60 | window.open(link,"_parent")
61 | }
62 | }else{
63 | window.open(link,"_blank")
64 | }
65 | }
66 |
67 | //encrypt main box with myLock in order to make an invitation
68 | function makeInvitation(){
69 | if(mainBox.textContent.trim() != ''){
70 | var reply = confirm('Do you want the contents of the main box to be encrypted and added to an invitation email? This will encourage the recipients to try PassLok, but be aware that the encrypted contents WILL NOT BE SECURE.');
71 | if (!reply) return;
72 | var text = mainBox.innerHTML.trim(),
73 | nonce = nacl.randomBytes(9),
74 | nonce24 = makeNonce24(nonce),
75 | cipherStr = myezLock + '//////' + nacl.util.encodeBase64(concatUi8([[128],nonce,PLencrypt(text,nonce24,myLock,true)])).replace(/=+$/,''); //this includes compression
76 | mainBox.textContent = '';
77 |
78 | var prefaceMsg = document.createElement('div');
79 | prefaceMsg.innerHTML = " The gibberish link below contains a message from me that has been encrypted with PassLok, a free app that you can get at the Chrome and Firefox web stores. There is also PassLok Privacy, PassLok for Email, and PassLok Universal at the same stores, plus the standalone PassLok web app at https://passlok.com/app .
To decrypt it, install PassLok, reload this page. You will be asked to supply a Master Key, which will not be stored or sent anywhere. You must remember your secret Key, but you can change it later if you want. Then paste in the block of gibberish below. When asked whether to accept my new Lock, go ahead and give it my name, and click OK . Then click Decrypt .
";
80 | if(emailMode.checked){
81 | var initialTag = document.createElement('pre'),
82 | invBody = document.createElement('pre'),
83 | finalTag = document.createElement('pre');
84 | initialTag.textContent = "\r\n\r\n----------begin invitation message encrypted with PassLok--------==\r\n\r\n";
85 | invBody.textContent = cipherStr.match(/.{1,80}/g).join("\r\n");
86 | finalTag.textContent = "\r\n\r\n==---------end invitation message encrypted with PassLok-----------";
87 | mainBox.appendChild(prefaceMsg);
88 | mainBox.appendChild(initialTag);
89 | mainBox.appendChild(invBody);
90 | mainBox.appendChild(finalTag);
91 | }else{
92 | prefaceMsg.textContent = prefaceMsg.textContent + "\r\n\r\nhttps://passlok.com/app#PL24inv==" + cipherStr + "==PL24inv";
93 | mainBox.appendChild(prefaceMsg)
94 | }
95 | updateButtons();
96 | mainMsg.textContent = "Invitation created. Invitations are ";
97 | var blinker = document.createElement('span');
98 | blinker.className = "blink";
99 | blinker.textContent = "NOT SECURELY ENCRYPTED";
100 | mainMsg.appendChild(blinker);
101 | if(emailMode.checked) sendMail()
102 | }else{
103 | mainMsg.textContent = "Write something not confidential to add to the invitation, then click Invite again"
104 | }
105 | }
106 |
107 | //displays QR code with sender's Lock and main URL
108 | function makeQRcode(){
109 | if(!refreshKey()) return;
110 | qrcode.makeCode('passlok.com/app#' + myLockStr);
111 | mainMsg.textContent = "This QR code contains your Lock. Tap it to hide";
112 | qrcodeImg.lastChild.style.margin = "auto"; //center QR code
113 | qrcodeImg.style.display = 'block'
114 | }
115 |
116 | //calls texting app
117 | function sendSMS(){
118 | if(learnMode.checked){
119 | var reply = confirm("The default texting app will now open. You need to have copied your short encrypted message to the clipboard before doing this, if you want to send one. This only works on smartphones. Cancel if this is not what you want.");
120 | if(!reply) return
121 | }
122 | if(sendSMSBtn.textContent == 'Save'){
123 | saveFiles()
124 | }else{
125 | selectMain();
126 | window.open("SMS:","_parent")
127 | }
128 | }
129 |
130 | //decrypts a chat invite if found, then opens chat screen, otherwise makes one
131 | function Chat(){
132 | var text = mainBox.innerHTML.trim();
133 |
134 | if(text.match('==') && text.split('==')[0].slice(-4) == 'chat'){ //there is already a chat invitation, so open it
135 | var msg = text.split('==')[1],
136 | type = msg.charAt(0);
137 | unlock(type,msg,lockBox.innerHTML.replace(/\n/g,'
').trim());
138 | return
139 | }
140 |
141 | var listArray = lockBox.innerHTML.replace(/\n/g,'
').trim().split('
').filter(Boolean);
142 | if(learnMode.checked){
143 | var reply = confirm("A special encrypted item will be made, inviting the selected recipients to a secure chat session. Cancel if this is not what you want.");
144 | if(!reply) return
145 | }
146 |
147 | if(listArray.length == 0 || (listArray.length == 1 && listArray[0] == 'myself')){
148 | mainMsg.textContent = 'Please select those invited to chat';
149 | return
150 | }
151 | if(longMode.checked) listArray = listArray.concat('myself'); //make sure 'myself' is on the list, unless it's not a multi-recipient message
152 | listArray = listArray.filter(function(elem, pos, self) {return self.indexOf(elem) == pos;}); //remove duplicates and nulls
153 | listArray = listArray.filter(function(n){return n});
154 | lockBox.innerText = listArray.join('\n');
155 | openClose('shadow');
156 | openClose('chatDialog'); //stop to get chat type
157 | chatDate.value = mainBox.textContent.trim().slice(0,43);
158 | }
159 |
160 | //continues making a chat invite after the user has chosen the chat type
161 | function makeChat(){
162 | closeBox();
163 | if(dataChat.checked){ //A to C for Muaz Khan's WebRTC chat, D for Jitsi
164 | var type = 'A'
165 | }else if (audioChat.checked){
166 | var type = 'B'
167 | }else if (videoChat.checked){
168 | var type = 'C'
169 | }else{
170 | var type = 'D'
171 | }
172 | var date = chatDate.value.slice(0,43); //can't do encodeURI here because this will be decrypted by decryptList, which doesn't expect it
173 | if(date.trim() == '') date = 'noDate';
174 | while(date.length < 43) date += ' ';
175 | var password = nacl.util.encodeBase64(nacl.randomBytes(32)).replace(/=+$/,''),
176 | chatRoom = makeChatRoom();
177 | lock(lockBox.innerHTML.replace(/\n/g,'
').replace(/
$/,"").trim(),date + type + chatRoom + '?' + password); //date msg + info to be sent to chat page
178 | setTimeout(function(){
179 | if(emailMode.checked) sendMail()
180 | },50)
181 | }
182 |
183 | //makes a mostly anonymous chatRoom name from four words in the wordlist
184 | function makeChatRoom(){
185 | var wordlist = wordListExp.toString().slice(1,-2).split('|'),
186 | name = '';
187 | for(var i = 0; i < 4; i++){
188 | name += capitalizeFirstLetter(replaceVariants(wordlist[randomIndex()]))
189 | }
190 | return name
191 | }
192 |
193 | //capitalizes first letter, the better to blend into Jitsi
194 | function capitalizeFirstLetter(str) {
195 | return str[0].toUpperCase() + str.slice(1);
196 | }
197 |
198 | //returns a random index for wordlist
199 | function randomIndex(){
200 | return Math.floor(Math.random()*wordLength)
201 | }
202 |
203 | //detects if there is a chat link in the main box, and opens the Chat window
204 | function openChat(){
205 | var typetoken = mainBox.textContent.trim();
206 | if (typetoken.slice(-44,-43) == '?' && !typetoken.slice(43).match(/[^A-Za-z0-9+\/?]/)){ //chat data detected, so open chat
207 | mainBox.textContent = '';
208 | var date = typetoken.slice(0,43).trim(), //the first 43 characters are for the date and time etc.
209 | chatToken = decodeURI(typetoken.slice(43));
210 | if(date != 'noDate'){
211 | var msgStart = "This chat invitation says:\n\n " + date + " \n\n"
212 | }else{
213 | var msgStart = ""
214 | }
215 | var reply = confirm(msgStart + "If you go ahead, the chat session will open now.\nWARNING: this involves going online, which might give away your location. If you cancel, a link for the chat will be made.");
216 | if(!reply){
217 | var chatLink = document.createElement('a');
218 | chatLink.href = 'https://passlok.com/chat/chat.html#' + chatToken;
219 | chatLink.textContent = 'Right-click to open the chat';
220 | mainBox.textContent = '';
221 | mainBox.appendChild(chatLink);
222 | return
223 | }
224 | if(isSafari || isIE || isiOS){
225 | mainMsg.textContent = 'Sorry, but chat is not yet supported by your browser or OS';
226 | return
227 | }
228 | main2chat(chatToken)
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/js-head/textstego.js:
--------------------------------------------------------------------------------
1 | //The following code is to convert the contents of main box into fake text, and back. This can be useful against email scanners.
2 |
3 | if (typeof code == 'undefined'){ //default text for base64 to words conversion, global variable
4 | var defaultCoverText = "The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your program, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free softwares (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.";
5 | var coverText = defaultCoverText;
6 | coverText = coverText.replace(/ +/g," ").replace(/ \n/g,"\n") //remove multiple spaces, space before linefeed
7 | }
8 |
9 | //returns true if pure base64
10 | function isBase64(string){
11 | return !string.match(/[^a-zA-Z0-9+\/]/)
12 | }
13 |
14 | //to remove duplicates in array much more quickly than with array.filter(function(elem, pos, self) {return self.indexOf(elem) == pos;});
15 | function uniq(a) {
16 | var seen = {};
17 | return a.filter(function(item) {
18 | return seen.hasOwnProperty(item) ? false : (seen[item] = true);
19 | })
20 | }
21 |
22 | //This function checks for legal (base64 after tags are removed) PassLok output and calls the currently selected encoder. Otherwise it calls the decoder
23 | function textStego(){
24 | var text = mainBox.innerHTML.replace(/&[^;]+;/g,'').replace(/
(.*?)\/a>$/,'').replace(/ /g,'').replace(/[\r\n]/g,'');
25 | if(text.match('==')) text = text.split('==')[1].replace(/-/g,''); //remove tags and dashes
26 | text = text.replace(/<(.*?)>/gi,'');
27 | if(text == ""){
28 | mainMsg.textContent = 'No text in the box';
29 | return
30 | }
31 |
32 | blinkMsg(mainMsg);
33 | setTimeout(function(){ //the rest after a 20 ms delay
34 | if(isBase64(text)){ //pure base64 found: encode it
35 | if(sentenceMode.checked){
36 | toPhrases(text)
37 | }else if(wordMode.checked){
38 | toWords(text)
39 | }else if(spaceMode.checked){
40 | toSpaces(text)
41 | }else if(invisibleMode.checked){
42 | toInvisible(text)
43 | }else{
44 | toLetters(text)
45 | }
46 | }else{ //no legal item found: try to decode
47 | if(text.match('\u00ad')){ //detect soft hyphen
48 | fromInvisible(text);
49 | mainMsg.textContent = 'Message extracted from Invisible encoding'
50 | }else if(text.match('\u200c')){ //detect zero width non-joiner
51 | fromSpaces(text);
52 | mainMsg.textContent = 'Message extracted from Spaces encoding'
53 | }else if(text.match('\u2004') || text.match('\u2005') || text.match('\u2006')){ //detect special characters used in Letters encoding
54 | fromLetters(text);
55 | mainMsg.textContent = 'Message extracted from Letters'
56 | }else if(text.match(':') != null){ //detect colons and if there are any invoke Sentences decoder
57 | fromPhrases(text);
58 | mainMsg.textContent = 'Message extracted from Sentences'
59 | }else{ //no special characters detected: words decoder
60 | fromWords(text);
61 | mainMsg.textContent = 'Message extracted from Words encoding'
62 | }
63 | }
64 | charsLeft()
65 | },20); //end of timeout
66 | }
67 |
68 | //makes array with words of 8 different lengths taken from the cover
69 | function makeWordMatrix(cover){
70 | var wordArray = uniq(cover.replace(/[\.,!?\*;:{}_()\[\]"…“”‘’„‚«»‹›—–―¿¡]|_/g, "").replace(/[\s\n]+/g,' ').slice(0,-1).split(/ /)),
71 | bins = [[],[],[],[],[],[],[],[]];
72 | for(var i = 0; i < wordArray.length; i++){
73 | var lengthMod8 = wordArray[i].length % 8;
74 | bins[lengthMod8] = bins[lengthMod8].concat(wordArray[i])
75 | }
76 | for(var i = 0; i < 8; i++){
77 | if(bins[i].length == 0){
78 | mainMsg.textContent = 'Please use a Cover text with more variation';
79 | return
80 | }
81 | }
82 | return bins
83 | }
84 |
85 | //words encoder: each character is given a 2-digit base9 code, and replaced by two words of those lengths
86 | function toWords(text){
87 | if(learnMode.checked){
88 | var reply = confirm("The contents of the main box will be replaced with encoded text which contains the original text as words of varying length. Cancel if this is not what you want.");
89 | if(!reply) return
90 | }
91 | var wordArray = makeWordMatrix(coverText),
92 | out = new Array(text.length * 2);
93 |
94 | //now get a 2-digit code for each character in the text; each digit goes from 0 to 7, so we have 64 possibilities to encode base64
95 | for(var i = 0; i < text.length; i++){
96 | var index = base64.indexOf(text[i]),
97 | word1Choices = wordArray[Math.floor(index / 8)],
98 | word2Choices = wordArray[index % 8];
99 | out[2*i] = word1Choices[Math.floor(Math.random()*word1Choices.length)] + randompunct();
100 | out[2*i+1] = word2Choices[Math.floor(Math.random()*word2Choices.length)] + randompunct()
101 | }
102 | var outStr = out.join('');
103 |
104 | //capitalize initial and after period, add final period.
105 | outStr = outStr.toLowerCase().replace(/[.][\s\n][a-z]/g,function(a){return a.toUpperCase();});
106 | outStr = outStr.replace(/[a-z]/,function(a){return a.toUpperCase();});
107 | outStr = outStr.slice(0,-1) + '.';
108 | mainBox.textContent = outStr.trim();
109 | mainMsg.textContent = 'Message encoded as words of varying length'
110 | }
111 |
112 | //words decoder. Takes groups of 2 words. Their lengths is the index of each characters, in base9
113 | function fromWords(text){
114 | if(learnMode.checked){
115 | var reply = confirm("The encoded text in the main box will be replaced with the original text from which it came. Cancel if this is not what you want.");
116 | if(!reply) return
117 | }
118 | text = text.replace(/ /g,'').replace(/[.,\n]/g,''); //remove extra spaces and punctuation
119 | var textArray = text.split(/ /),
120 | out = new Array(textArray.length / 2);
121 | for(var i = 0; i < textArray.length; i=i+2){
122 | var index1 = textArray[i].trim().length % 8,
123 | index2 = textArray[i+1].trim().length % 8;
124 | out[i/2] = base64[index1 * 8 + index2]
125 | }
126 | mainBox.textContent = out.join('').trim()
127 | }
128 |
129 | //This is to generate random periods, commans and newlines, per the percentage brackets below, plus spaces when appropriate
130 | function randompunct(){
131 | var percent = Math.ceil(Math.random() * 100);
132 | if (percent < 8) {
133 | return ". "
134 | } else if(percent < 14) {
135 | return ", "
136 | } else {
137 | return " "
138 | }
139 | }
140 |
141 | //the following functions are to hide text into a text cover, as binary double spaces. It needs a little under 6 cover words for each base64 character, 36 for non-ASCII
142 | function spacesEncoder(bin){
143 | var textsplit = coverText.split(" "),
144 | stegospace = [' ', ' \u200c'],
145 | spaces = textsplit.length - 1,
146 | turns = 0;
147 | if (spaces < bin.length){ //repeat cover text if too short
148 | while (spaces < bin.length){
149 | textsplit = textsplit.concat(coverText.split(" "));
150 | spaces = textsplit.length - 1;
151 | turns++
152 | }
153 | mainMsg.textContent = 'Message encoded into spaces of this text. It was repeated ' + turns + ' times.'
154 | }else{
155 | mainMsg.textContent = 'Message encoded into spaces of this text'
156 | }
157 | textsplit = textsplit.slice(0,bin.length + 1); //take a sufficiently long piece of cover text
158 | var newtext = new Array(bin.length + 1);
159 | newtext[0] = textsplit[0];
160 | for(var i = 0; i < bin.length; i++){
161 | newtext[i+1] = stegospace[bin[i]] + textsplit[i+1]
162 | }
163 | return newtext.join('')
164 | }
165 |
166 | function spacesDecoder(text){
167 | var textsplit = text.split(" "),
168 | bin = new Array(textsplit.length - 1);
169 |
170 | for(var i = 0; i < textsplit.length - 1; i++){
171 | if (textsplit[i + 1].match('\u200c')){
172 | bin[i] = 1
173 | }else{
174 | bin[i] = 0
175 | }
176 | }
177 | return bin
178 | }
179 |
180 | //retrieves base64 string from binary array. No error checking
181 | function fromBin(input){
182 | var length = input.length - (input.length % 6),
183 | output = new Array(length / 6),
184 | index = 0;
185 |
186 | for(var i = 0; i < length; i = i+6) {
187 | index = 0;
188 | for(var j = 0; j < 6; j++){
189 | index = 2 * index + input[i+j]
190 | }
191 | output[i / 6] = base64.charAt(index)
192 | }
193 | return output.join('')
194 | }
195 |
196 | //makes the binary equivalent (array) of a base64 string. No error checking
197 | function toBin(input){
198 | var output = new Array(input.length * 6),
199 | code = 0,
200 | digit = 0,
201 | divider = 32;
202 |
203 | for(var i = 0; i < input.length; i++) {
204 | code = base64.indexOf(input.charAt(i));
205 | divider = 32;
206 | for(var j = 0; j < 5; j++){
207 | digit = code >= divider ? 1 : 0;
208 | code -= digit * divider;
209 | divider = divider / 2;
210 | output[6 * i + j] = digit
211 | }
212 | output[6 * i + 5] = code;
213 | }
214 | return output
215 | }
216 |
217 | function toSpaces(text) {
218 | if(learnMode.checked){
219 | var reply = confirm("The contents of the main box will be replaced with encoded text which contains the original text as formatted spacing. Cancel if this is not what you want.");
220 | if(!reply) return
221 | }
222 | mainBox.textContent = spacesEncoder(toBin(text))
223 | }
224 |
225 | function fromSpaces(text) {
226 | if(learnMode.checked){
227 | var reply = confirm("The encoded text in the main box will be replaced with the original text from which it came. Cancel if this is not what you want.");
228 | if(!reply) return
229 | }
230 | mainBox.textContent = fromBin(spacesDecoder(text)).replace(/\x00/g,'').trim(); //take out nulls, in case text was added to finish the last sentence.
231 | }
232 |
233 | //the following functions are to hide text between two letters, as a binary string made of invisible characters.
234 | function toInvisible(text) {
235 | if(learnMode.checked){
236 | var reply = confirm("The contents of the main box will be invisibly encoded (to a human) at the end of a sentence. Cancel if this is not what you want.");
237 | if(!reply) return
238 | }
239 | mainBox.textContent = 'Dear friend,' + invisibleEncoder(toBin(text)) + '\r\n\r\nBody of the message.'
240 | }
241 |
242 | function fromInvisible(text) {
243 | if(learnMode.checked){
244 | var reply = confirm("The encoded text in the main box will be replaced with the original text from which it came. Cancel if this is not what you want.");
245 | if(!reply) return
246 | }
247 | mainBox.textContent = fromBin(invisibleDecoder(text)).trim()
248 | }
249 |
250 | function invisibleEncoder(bin){
251 | var stegospace = ['\u00ad', '\u200c'],
252 | newtext = new Array(bin.length);
253 |
254 | for(var i = 0; i < bin.length; i++){
255 | newtext[i+1] = stegospace[bin[i]]
256 | }
257 | mainMsg.textContent = 'Message invisibly encoded at the end of the introduction in the box. Edit as needed';
258 | return newtext.join('')
259 | }
260 |
261 | function invisibleDecoder(text){
262 | var binStr = text.replace(/\u00ad/g,'0').replace(/\u200c/g,'1');
263 | binStr = binStr.match(/[01]+/)[0]; //keep binary part
264 | var length = binStr.length,
265 | bin = new Array(length);
266 | for(var i = 0; i < length; i++){
267 | if (binStr.charAt(i) == '0'){
268 | bin[i] = 0
269 | }else{
270 | bin[i] = 1
271 | }
272 | }
273 | return bin
274 | }
275 |
276 | //makes phrase matrix for a given cover text, where sentences are catalogued by length mod 11
277 | function makePhraseMatrix(cover){
278 | var phraseArray = cover.replace(/["…“”‘’„‚«»‹›¿¡\_\*\^\(\)]+/g,'').slice(0,-1).split(/[.,;:?!][ \-\—]/),
279 | bins = [[],[],[],[],[],[],[],[],[],[],[]];
280 | for(var i = 0; i < phraseArray.length; i++){
281 | var lengthMod12 = phraseArray[i].length % 11;
282 | bins[lengthMod12] = bins[lengthMod12].concat(phraseArray[i])
283 | }
284 | for(var i = 0; i < 11; i++){
285 | if(bins[i].length == 0){
286 | mainMsg.textContent = 'Please use a Cover text with more variation.';
287 | return
288 | }
289 | }
290 | return bins
291 | }
292 |
293 | //encodes text as sentences of varying length (11 different values)
294 | function toPhrases(text){
295 | if(learnMode.checked){
296 | var reply = confirm("The contents of the main box will be replaced with encoded text which contains the original text as sentences of varying length. Cancel if this is not what you want.");
297 | if(!reply) return
298 | }
299 | var phraseArray = makePhraseMatrix(coverText),
300 | punct = '.,;:!?',
301 | outArray = new Array(text.length);
302 |
303 | //now split the base64-encoded string into a base11 code to select sentence length, and a base6 code to select punctuation
304 | for(var i = 0; i < text.length; i++){
305 | var index = base64.indexOf(text[i]),
306 | phraseChoices = phraseArray[index % 11];
307 | outArray[i] = phraseChoices[Math.floor(Math.random()*phraseChoices.length)] + punct[Math.floor(index/11)] + ' '
308 | }
309 | var out = outArray.join('');
310 | out = out.replace(/[.!?][\s\n][a-z]/g,function(a){return a.toUpperCase();}).replace(/[,;:][\s\n][A-Z]/g,function(a){return a.toLowerCase();}).trim(); //capitalization
311 | out = out.charAt(0).toUpperCase() + out.slice(1);
312 | mainBox.textContent = out.trim();
313 | mainMsg.textContent = 'Message encoded as sentences of varying length'
314 | }
315 |
316 | //decodes text encoded as sentences of varying length
317 | function fromPhrases(text){
318 | if(learnMode.checked){
319 | var reply = confirm("The encoded text in the main box will be replaced with the original text from which it came. Cancel if this is not what you want.");
320 | if(!reply) return
321 | }
322 | text = text.replace(/ /g,'').replace(/\n/g,'').trim() + ' ';
323 | var textArray = text.split(/[.,;:!?] /g).slice(0,-1),
324 | punctArray = text.match(/[.,;:!?]/g),
325 | punct = ".,;:!?",
326 | outArray = new Array(textArray.length);
327 |
328 | for(var i = 0; i < textArray.length; i++){
329 | var index11 = textArray[i].trim().length % 11,
330 | index6 = punct.indexOf(punctArray[i]);
331 | outArray[i] = base64[index6 * 11 + index11]
332 | }
333 | mainBox.textContent = outArray.join('').trim()
334 | }
335 |
336 | //Letters encoding is based on code at: http://www.irongeek.com/i.php?page=security/unicode-steganography-homoglyph-encoder, by Adrian Crenshaw, 2013
337 | //first the object containing the Unicode character substitutions
338 | var charMappings = {//Aa
339 | "a":"0", "a0":"a", "\u0430":"1", "a1":"\u0430",
340 | "A":"0", "A0":"A", "\u0391":"1", "A1":"\u0391",
341 | //Bb
342 | "B":"0", "B0":"B", "\u0392":"1", "B1":"\u0392",
343 | //Cc
344 | "c":"0", "c0":"c", "\u0441":"1", "c1":"\u0441",
345 | "C":"0", "C0":"C", "\u0421":"1", "C1":"\u0421",
346 | //Ee
347 | "e":"0", "e0":"e", "\u0435":"1", "e1":"\u0435",
348 | "E":"0", "E0":"E", "\u0415":"1", "E1":"\u0415",
349 | //Gg
350 | "g":"0", "g0":"g", "\u0261":"1", "g1":"\u0261",
351 | //Hh
352 | "H":"0", "H0":"H", "\u041D":"1", "H1":"\u041D",
353 | //Ii
354 | "i":"0", "i0":"i", "\u0456":"1", "i1":"\u0456",
355 | "I":"0", "I0":"I", "\u0406":"1", "I1":"\u0406",
356 | //Jj
357 | "j":"0", "j0":"j", "\u03F3":"1", "j1":"\u03F3",
358 | "J":"0", "J0":"J", "\u0408":"1", "J1":"\u0408",
359 | //Kk
360 | "K":"0", "K0":"K", "\u039A":"1", "K1":"\u039A",
361 | //Mm
362 | "M":"0", "M0":"M", "\u039C":"1", "M1":"\u039C",
363 | //Nn
364 | "N":"0", "N0":"N", "\u039D":"1", "N1":"\u039D",
365 | //Oo
366 | "o":"0", "o0":"o", "\u03BF":"1", "o1":"\u03BF",
367 | "O":"0", "O0":"O", "\u039F":"1", "O1":"\u039F",
368 | //Pp
369 | "p":"0", "p0":"p", "\u0440":"1", "p1":"\u0440",
370 | "P":"0", "P0":"P", "\u03A1":"1", "P1":"\u03A1",
371 | //Ss
372 | "s":"0", "s0":"s", "\u0455":"1", "s1":"\u0455",
373 | "S":"0", "S0":"S", "\u0405":"1", "S1":"\u0405",
374 | //Tt
375 | "T":"0", "T0":"T", "\u03A4":"1", "T1":"\u03A4",
376 | //Xx
377 | "x":"0", "x0":"x", "\u0445":"1", "x1":"\u0445",
378 | "X":"0", "X0":"X", "\u03A7":"1", "X1":"\u03A7",
379 | //Yy
380 | "y":"0", "y0":"y", "\u0443":"1", "y1":"\u0443",
381 | "Y":"0", "Y0":"Y", "\u03A5":"1", "Y1":"\u03A5",
382 | //Zz
383 | "Z":"0", "Z0":"Z", "\u0396":"1", "Z1":"\u0396",
384 | //Spaces
385 | " ":"000",
386 | " 000":" ",
387 | "\u2004":"001",
388 | " 001":"\u2004",
389 | "\u2005":"010",
390 | " 010":"\u2005",
391 | "\u2006":"011",
392 | " 011":"\u2006",
393 | "\u2008":"100",
394 | " 100":"\u2008",
395 | "\u2009":"101",
396 | " 101":"\u2009",
397 | "\u202f":"110",
398 | " 110":"\u202F",
399 | "\u205F":"111",
400 | " 111":"\u205F"
401 | };
402 |
403 | //counts the number of encodable bits in the cover text
404 | function encodableBits(cover){
405 | var bitcount = 0;
406 | for (var i = 0; i < cover.length; i++){
407 | if (charMappings[cover[i]] !== undefined){
408 | bitcount = bitcount + charMappings[cover[i]].length
409 | }
410 | }
411 | return bitcount
412 | }
413 |
414 | //encodes text as special letters and spaces in the cover text, which replace the original ones
415 | function toLetters(text){
416 | if(learnMode.checked){
417 | var reply = confirm("The contents of the main box will be replaced with encoded text which contains the original text as formatted special characters and spaces. Cancel if this is not what you want.");
418 | if(!reply) return
419 | }
420 | var textBin = toBin(text).join(''), //string containing 1's and 0's
421 | cover = coverText,
422 | capacity = encodableBits(cover);
423 | if (capacity < textBin.length){ //repeat the cover text if it's too short
424 | var turns = Math.ceil(textBin.length / capacity),
425 | index = 0;
426 | while (index < turns){
427 | cover += ' ' + coverText;
428 | index++
429 | }
430 | mainMsg.textContent = 'Message encoded into letters of this text. It was repeated ' + turns + ' times.'
431 | }
432 | var finalString = "",
433 | bitsIndex = 0,
434 | i = 0,
435 | doneBits = '';
436 | while(doneBits.length < textBin.length){
437 | if (charMappings[cover[i]] === undefined){
438 | finalString = finalString + cover[i]
439 | }else{
440 | var tempBits = textBin.substring(bitsIndex,bitsIndex + charMappings[cover[i]].length);
441 | while(tempBits.length < charMappings[cover[i]].length){tempBits = tempBits + "0";} //Got to pad it out
442 | finalString += charMappings[cover[i] + tempBits];
443 | bitsIndex += charMappings[cover[i]].length;
444 | doneBits += tempBits
445 | }
446 | i++
447 | }
448 | mainBox.textContent = finalString + '.'; //period needed because there could be spaces at the end
449 | mainMsg.textContent = 'Message encoded into letters of this text.'
450 | }
451 |
452 | //gets the original text from Letters encoded text
453 | function fromLetters(text){
454 | if(learnMode.checked){
455 | var reply = confirm("The encoded text in the main box will be replaced with the original text from which it came. Cancel if this is not what you want.");
456 | if(!reply) return
457 | }
458 | var bintemp = [],
459 | tempchar = "";
460 | for (var i = 0; i < text.length; i++){
461 | if (charMappings[text[i]] === undefined ){
462 | }else{
463 | tempchar = charMappings[text[i]];
464 | bintemp.push(tempchar)
465 | }
466 | }
467 | var binStr = bintemp.join(''),
468 | bin = new Array(binStr.length);
469 | for(var i = 0; i < binStr.length; i++) bin[i] = parseInt(binStr.charAt(i));
470 | mainBox.textContent = fromBin(bin.slice(0,bin.length-(bin.length % 6)))
471 | }
472 |
473 | //this one is to display the cover text or change it as requested
474 | function newCover(string){
475 | coverText = addSpaces(string.replace(/[\n\s-]+/g,' ')); //newlines, dashes, and multiple spaces do weird things, so remove them
476 | mainMsg.textContent = 'Cover text changed'
477 | }
478 |
479 | //adds spaces that can be encoded if Chinese, Korean, or Japanese
480 | function addSpaces(string){
481 | if (string.match(/[\u3400-\u9FBF]/) != null) string = string.split('').join(' ').replace(/\s+/g, ' ');
482 | return string
483 | }
484 |
--------------------------------------------------------------------------------
/js-opensrc/ed2curve.js:
--------------------------------------------------------------------------------
1 | /*
2 | * ed2curve: convert Ed25519 signing key pair into Curve25519
3 | * key pair suitable for Diffie-Hellman key exchange.
4 | *
5 | * Written by Dmitry Chestnykh in 2014. Public domain.
6 | */
7 | /* jshint newcap: false */
8 | (function(root, f) {
9 | 'use strict';
10 | if (typeof module !== 'undefined' && module.exports) module.exports = f(require('tweetnacl/nacl-fast'));
11 | else root.ed2curve = f(root.nacl);
12 | }(this, function(nacl) {
13 | 'use strict';
14 | if (!nacl) throw new Error('tweetnacl not loaded');
15 |
16 | // -- Operations copied from TweetNaCl.js. --
17 |
18 | var gf = function(init) {
19 | var i, r = new Float64Array(16);
20 | if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
21 | return r;
22 | };
23 |
24 | var gf0 = gf(),
25 | gf1 = gf([1]),
26 | D = gf([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]),
27 | I = gf([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);
28 |
29 |
30 |
31 | function car25519(o) {
32 | var c;
33 | var i;
34 | for (i = 0; i < 16; i++) {
35 | o[i] += 65536;
36 | c = Math.floor(o[i] / 65536);
37 | o[(i+1)*(i<15?1:0)] += c - 1 + 37 * (c-1) * (i===15?1:0);
38 | o[i] -= (c * 65536);
39 | }
40 | }
41 |
42 | function sel25519(p, q, b) {
43 | var t, c = ~(b-1);
44 | for (var i = 0; i < 16; i++) {
45 | t = c & (p[i] ^ q[i]);
46 | p[i] ^= t;
47 | q[i] ^= t;
48 | }
49 | }
50 |
51 | function unpack25519(o, n) {
52 | var i;
53 | for (i = 0; i < 16; i++) o[i] = n[2*i] + (n[2*i+1] << 8);
54 | o[15] &= 0x7fff;
55 | }
56 |
57 | // addition
58 | function A(o, a, b) {
59 | var i;
60 | for (i = 0; i < 16; i++) o[i] = (a[i] + b[i])|0;
61 | }
62 |
63 | // subtraction
64 | function Z(o, a, b) {
65 | var i;
66 | for (i = 0; i < 16; i++) o[i] = (a[i] - b[i])|0;
67 | }
68 |
69 | // multiplication
70 | function M(o, a, b) {
71 | var i, j, t = new Float64Array(31);
72 | for (i = 0; i < 31; i++) t[i] = 0;
73 | for (i = 0; i < 16; i++) {
74 | for (j = 0; j < 16; j++) {
75 | t[i+j] += a[i] * b[j];
76 | }
77 | }
78 | for (i = 0; i < 15; i++) {
79 | t[i] += 38 * t[i+16];
80 | }
81 | for (i = 0; i < 16; i++) o[i] = t[i];
82 | car25519(o);
83 | car25519(o);
84 | }
85 |
86 | // squaring
87 | function S(o, a) {
88 | M(o, a, a);
89 | }
90 |
91 | // inversion
92 | function inv25519(o, i) {
93 | var c = gf();
94 | var a;
95 | for (a = 0; a < 16; a++) c[a] = i[a];
96 | for (a = 253; a >= 0; a--) {
97 | S(c, c);
98 | if(a !== 2 && a !== 4) M(c, c, i);
99 | }
100 | for (a = 0; a < 16; a++) o[a] = c[a];
101 | }
102 |
103 | function pack25519(o, n) {
104 | var i, j, b;
105 | var m = gf(), t = gf();
106 | for (i = 0; i < 16; i++) t[i] = n[i];
107 | car25519(t);
108 | car25519(t);
109 | car25519(t);
110 | for (j = 0; j < 2; j++) {
111 | m[0] = t[0] - 0xffed;
112 | for (i = 1; i < 15; i++) {
113 | m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1);
114 | m[i-1] &= 0xffff;
115 | }
116 | m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1);
117 | b = (m[15]>>16) & 1;
118 | m[14] &= 0xffff;
119 | sel25519(t, m, 1-b);
120 | }
121 | for (i = 0; i < 16; i++) {
122 | o[2*i] = t[i] & 0xff;
123 | o[2*i+1] = t[i] >> 8;
124 | }
125 | }
126 |
127 |
128 | function par25519(a) {
129 | var d = new Uint8Array(32);
130 | pack25519(d, a);
131 | return d[0] & 1;
132 | }
133 |
134 |
135 |
136 | function vn(x, xi, y, yi, n) {
137 | var i, d = 0;
138 | for (i = 0; i < n; i++) d |= x[xi + i] ^ y[yi + i];
139 | return (1 & ((d - 1) >>> 8)) - 1;
140 | }
141 |
142 |
143 | function crypto_verify_32(x, xi, y, yi) {
144 | return vn(x, xi, y, yi, 32);
145 | }
146 |
147 | function neq25519(a, b) {
148 | var c = new Uint8Array(32), d = new Uint8Array(32);
149 | pack25519(c, a);
150 | pack25519(d, b);
151 | return crypto_verify_32(c, 0, d, 0);
152 | }
153 |
154 |
155 | function pow2523(o, i) {
156 | var c = gf();
157 | var a;
158 | for (a = 0; a < 16; a++) c[a] = i[a];
159 | for (a = 250; a >= 0; a--) {
160 | S(c, c);
161 | if (a !== 1) M(c, c, i);
162 | }
163 | for (a = 0; a < 16; a++) o[a] = c[a];
164 | }
165 |
166 | function set25519(r, a) {
167 | var i;
168 | for (i = 0; i < 16; i++) r[i] = a[i] | 0;
169 | }
170 |
171 | function unpackneg(r, p) {
172 | var t = gf(), chk = gf(), num = gf(),
173 | den = gf(), den2 = gf(), den4 = gf(),
174 | den6 = gf();
175 |
176 | set25519(r[2], gf1);
177 | unpack25519(r[1], p);
178 | S(num, r[1]);
179 | M(den, num, D);
180 | Z(num, num, r[2]);
181 | A(den, r[2], den);
182 |
183 | S(den2, den);
184 | S(den4, den2);
185 | M(den6, den4, den2);
186 | M(t, den6, num);
187 | M(t, t, den);
188 |
189 | pow2523(t, t);
190 | M(t, t, num);
191 | M(t, t, den);
192 | M(t, t, den);
193 | M(r[0], t, den);
194 |
195 | S(chk, r[0]);
196 | M(chk, chk, den);
197 | if (neq25519(chk, num)) M(r[0], r[0], I);
198 |
199 | S(chk, r[0]);
200 | M(chk, chk, den);
201 | if (neq25519(chk, num)) return -1;
202 |
203 | if (par25519(r[0]) === (p[31] >> 7)) Z(r[0], gf0, r[0]);
204 |
205 | M(r[3], r[0], r[1]);
206 | return 0;
207 | }
208 |
209 | // ----
210 |
211 | // Converts Ed25519 public key to Curve25519 public key.
212 | // montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p
213 | function convertPublicKey(pk) {
214 | var z = new Uint8Array(32),
215 | q = [gf(), gf(), gf(), gf()],
216 | a = gf(), b = gf();
217 |
218 | if (unpackneg(q, pk)) return null; // reject invalid key
219 |
220 | var y = q[1];
221 |
222 | A(a, gf1, y);
223 | Z(b, gf1, y);
224 | inv25519(b, b);
225 | M(a, a, b);
226 |
227 | pack25519(z, a);
228 | return z;
229 | }
230 |
231 | // Converts Ed25519 secret key to Curve25519 secret key.
232 | function convertSecretKey(sk) {
233 | var d = new Uint8Array(64), o = new Uint8Array(32), i;
234 | nacl.lowlevel.crypto_hash(d, sk, 32);
235 | d[0] &= 248;
236 | d[31] &= 127;
237 | d[31] |= 64;
238 | for (i = 0; i < 32; i++) o[i] = d[i];
239 | for (i = 0; i < 64; i++) d[i] = 0;
240 | return o;
241 | }
242 |
243 | function convertKeyPair(edKeyPair) {
244 | var publicKey = convertPublicKey(edKeyPair.publicKey);
245 | if (!publicKey) return null;
246 | return {
247 | publicKey: publicKey,
248 | secretKey: convertSecretKey(edKeyPair.secretKey)
249 | };
250 | }
251 |
252 | return {
253 | convertPublicKey: convertPublicKey,
254 | convertSecretKey: convertSecretKey,
255 | convertKeyPair: convertKeyPair,
256 | };
257 |
258 | }));
259 |
--------------------------------------------------------------------------------
/js-opensrc/isaac.js:
--------------------------------------------------------------------------------
1 | /* ----------------------------------------------------------------------
2 | * Copyright (c) 2012 Yves-Marie K. Rinquin
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining
5 | * a copy of this software and associated documentation files (the
6 | * "Software"), to deal in the Software without restriction, including
7 | * without limitation the rights to use, copy, modify, merge, publish,
8 | * distribute, sublicense, and/or sell copies of the Software, and to
9 | * permit persons to whom the Software is furnished to do so, subject to
10 | * the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be
13 | * included in all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | *
23 | * ----------------------------------------------------------------------
24 | *
25 | * ISAAC is a cryptographically secure pseudo-random number generator
26 | * (or CSPRNG for short) designed by Robert J. Jenkins Jr. in 1996 and
27 | * based on RC4. It is designed for speed and security.
28 | *
29 | * ISAAC's informations & analysis:
30 | * http://burtleburtle.net/bob/rand/isaac.html
31 | * ISAAC's implementation details:
32 | * http://burtleburtle.net/bob/rand/isaacafa.html
33 | *
34 | * ISAAC succesfully passed TestU01
35 | *
36 | * ----------------------------------------------------------------------
37 | *
38 | * Usage:
39 | * var random_number = isaac.random();
40 | *
41 | * Output: [ 0x00000000; 0xffffffff]
42 | * [-2147483648; 2147483647]
43 | *
44 | */
45 |
46 | /* js string (ucs-2/utf16) to a 32-bit integer (utf-8 chars, little-endian) array */
47 | String.prototype.toIntArray = function() {
48 | var w1, w2, u, r4 = [], r = [], i = 0;
49 | var s = this + '\0\0\0'; // pad string to avoid discarding last chars
50 | var l = s.length - 1;
51 |
52 | while(i < l) {
53 | w1 = s.charCodeAt(i++);
54 | w2 = s.charCodeAt(i+1);
55 | if (w1 < 0x0080) {
56 | // 0x0000 - 0x007f code point: basic ascii
57 | r4.push(w1);
58 | } else if(w1 < 0x0800) {
59 | // 0x0080 - 0x07ff code point
60 | r4.push(((w1 >>> 6) & 0x1f) | 0xc0);
61 | r4.push(((w1 >>> 0) & 0x3f) | 0x80);
62 | } else if((w1 & 0xf800) != 0xd800) {
63 | // 0x0800 - 0xd7ff / 0xe000 - 0xffff code point
64 | r4.push(((w1 >>> 12) & 0x0f) | 0xe0);
65 | r4.push(((w1 >>> 6) & 0x3f) | 0x80);
66 | r4.push(((w1 >>> 0) & 0x3f) | 0x80);
67 | } else if(((w1 & 0xfc00) == 0xd800)
68 | && ((w2 & 0xfc00) == 0xdc00)) {
69 | // 0xd800 - 0xdfff surrogate / 0x10ffff - 0x10000 code point
70 | u = ((w2 & 0x3f) | ((w1 & 0x3f) << 10)) + 0x10000;
71 | r4.push(((u >>> 18) & 0x07) | 0xf0);
72 | r4.push(((u >>> 12) & 0x3f) | 0x80);
73 | r4.push(((u >>> 6) & 0x3f) | 0x80);
74 | r4.push(((u >>> 0) & 0x3f) | 0x80);
75 | i++;
76 | } else {
77 | // invalid char
78 | }
79 | /* add integer (four utf-8 value) to array */
80 | if(r4.length > 3) {
81 | // little endian
82 | r.push((r4.shift() << 0) | (r4.shift() << 8) |
83 | (r4.shift() << 16) | (r4.shift() << 24));
84 | }
85 | }
86 |
87 | return r;
88 | }
89 |
90 | /* isaac module pattern */
91 | var isaac = (function(){
92 |
93 | /* private: internal states */
94 | var m = Array(256), // internal memory
95 | acc = 0, // accumulator
96 | brs = 0, // last result
97 | cnt = 0, // counter
98 | r = Array(256), // result array
99 | gnt = 0; // generation counter
100 |
101 | seed(Math.random() * 0xffffffff);
102 |
103 | /* private: 32-bit integer safe adder */
104 | function add(x, y) {
105 | var lsb = (x & 0xffff) + (y & 0xffff);
106 | var msb = (x >>> 16) + (y >>> 16) + (lsb >>> 16);
107 | return (msb << 16) | (lsb & 0xffff);
108 | }
109 |
110 | /* public: initialisation */
111 | function reset() {
112 | acc = brs = cnt = 0;
113 | for(var i = 0; i < 256; ++i)
114 | m[i] = r[i] = 0;
115 | gnt = 0;
116 | }
117 |
118 | /* public: seeding function */
119 | function seed(s) {
120 | var a, b, c, d, e, f, g, h, i;
121 |
122 | /* seeding the seeds of love */
123 | a = b = c = d =
124 | e = f = g = h = 0x9e3779b9; /* the golden ratio */
125 |
126 | if(s && typeof(s) === 'string')
127 | s = s.toIntArray();
128 |
129 | if(s && typeof(s) === 'number') {
130 | s = [s];
131 | }
132 |
133 | if(s instanceof Array) {
134 | reset();
135 | for(i = 0; i < s.length; i++)
136 | r[i & 0xff] += (typeof(s[i]) === 'number') ? s[i] : 0;
137 | }
138 |
139 | /* private: seed mixer */
140 | function seed_mix() {
141 | a ^= b << 11; d = add(d, a); b = add(b, c);
142 | b ^= c >>> 2; e = add(e, b); c = add(c, d);
143 | c ^= d << 8; f = add(f, c); d = add(d, e);
144 | d ^= e >>> 16; g = add(g, d); e = add(e, f);
145 | e ^= f << 10; h = add(h, e); f = add(f, g);
146 | f ^= g >>> 4; a = add(a, f); g = add(g, h);
147 | g ^= h << 8; b = add(b, g); h = add(h, a);
148 | h ^= a >>> 9; c = add(c, h); a = add(a, b);
149 | }
150 |
151 | for(i = 0; i < 4; i++) /* scramble it */
152 | seed_mix();
153 |
154 | for(i = 0; i < 256; i += 8) {
155 | if(s) { /* use all the information in the seed */
156 | a = add(a, r[i + 0]); b = add(b, r[i + 1]);
157 | c = add(c, r[i + 2]); d = add(d, r[i + 3]);
158 | e = add(e, r[i + 4]); f = add(f, r[i + 5]);
159 | g = add(g, r[i + 6]); h = add(h, r[i + 7]);
160 | }
161 | seed_mix();
162 | /* fill in m[] with messy stuff */
163 | m[i + 0] = a; m[i + 1] = b; m[i + 2] = c; m[i + 3] = d;
164 | m[i + 4] = e; m[i + 5] = f; m[i + 6] = g; m[i + 7] = h;
165 | }
166 | if(s) {
167 | /* do a second pass to make all of the seed affect all of m[] */
168 | for(i = 0; i < 256; i += 8) {
169 | a = add(a, m[i + 0]); b = add(b, m[i + 1]);
170 | c = add(c, m[i + 2]); d = add(d, m[i + 3]);
171 | e = add(e, m[i + 4]); f = add(f, m[i + 5]);
172 | g = add(g, m[i + 6]); h = add(h, m[i + 7]);
173 | seed_mix();
174 | /* fill in m[] with messy stuff (again) */
175 | m[i + 0] = a; m[i + 1] = b; m[i + 2] = c; m[i + 3] = d;
176 | m[i + 4] = e; m[i + 5] = f; m[i + 6] = g; m[i + 7] = h;
177 | }
178 | }
179 |
180 | prng(); /* fill in the first set of results */
181 | gnt = 256; /* prepare to use the first set of results */;
182 | }
183 |
184 | /* public: isaac generator, n = number of run */
185 | function prng(n){
186 | var i, x, y;
187 |
188 | n = (n && typeof(n) === 'number')
189 | ? Math.abs(Math.floor(n)) : 1;
190 |
191 | while(n--) {
192 | cnt = add(cnt, 1);
193 | brs = add(brs, cnt);
194 |
195 | for(i = 0; i < 256; i++) {
196 | switch(i & 3) {
197 | case 0: acc ^= acc << 13; break;
198 | case 1: acc ^= acc >>> 6; break;
199 | case 2: acc ^= acc << 2; break;
200 | case 3: acc ^= acc >>> 16; break;
201 | }
202 | acc = add(m[(i + 128) & 0xff], acc); x = m[i];
203 | m[i] = y = add(m[(x >>> 2) & 0xff], add(acc, brs));
204 | r[i] = brs = add(m[(y >>> 10) & 0xff], x);
205 | }
206 | }
207 | }
208 |
209 | /* public: return a random number between */
210 | function rand() {
211 | if(!gnt--) {
212 | prng(); gnt = 255;
213 | }
214 | return r[gnt];
215 | }
216 |
217 | /* public: return internals in an object*/
218 | function internals(){
219 | return {a: acc, b: brs, c: cnt, m: m, r: r};
220 | }
221 |
222 | /* return class object */
223 | return {
224 | 'reset': reset,
225 | 'seed': seed,
226 | 'prng': prng,
227 | 'rand': rand,
228 | 'internals': internals
229 | };
230 | })(); /* declare and execute */
231 |
232 | /* public: output*/
233 | isaac.random = function() {
234 | return 0.5 + this.rand() * 2.3283064365386963e-10; // 2^-32
235 | }
--------------------------------------------------------------------------------
/js-opensrc/jssteg-1.0.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jsSteg Javascript Library v1.0
3 | * https://github.com/owencm/js-steg
4 | * Copyright 2014, Owen Campbell-Moore and other contributors
5 | * Released under the MIT license
6 | *
7 | * Usage:
8 | * jsSteg provides two public functions, getCoefficients and reEncodeWithModifications.
9 | * Refer to their documentation below to understand their usage.
10 | *
11 | * Note:
12 | * This library depends on jsstegdecoder-1.0.js and jsstegencoder-1.0.js which have different
13 | * licences and must be included before this library.
14 | */
15 | var jsSteg = (function() {
16 | /**
17 | * Use the JPEG decoding library and pass on the coefficients to coeffReader
18 | * - url: the blob URL from which to read the image
19 | * - coeffReader: a function which will be called with the coefficients as an argument
20 | */
21 | var getCoefficients = function(url, coeffReader) {
22 | var image;
23 | image = new JpegImage();
24 | image.onload = function(coefficients) {
25 | return coeffReader(coefficients);
26 | };
27 | return image.load(url, true);
28 | };
29 |
30 | /**
31 | * Convert an image in any format to bmp data for encoding
32 | * - url: the blob URL to convert to bmp
33 | * - callback: called with the resulting data
34 | */
35 | var getImageDataFromURL = function(url, callback) {
36 | var img;
37 | img = document.createElement("img");
38 | img.onload = function() {
39 | var ctx, cvs;
40 | cvs = document.createElement("canvas");
41 | cvs.width = img.width;
42 | cvs.height = img.height;
43 | ctx = cvs.getContext("2d");
44 | ctx.drawImage(img, 0, 0);
45 | return callback(ctx.getImageData(0, 0, cvs.width, cvs.height));
46 | };
47 | return img.src = url;
48 | };
49 |
50 | /**
51 | * Decode the provided JPEG to raw data and then re-encode it with the JPEG encoding library,
52 | * running coefficientModifier on the coefficients while encoding
53 | * - url: the blob URL from which to 're-encode'
54 | * - coefficientModifier: this will be called with the coefficients as an argument which it can
55 | * modify before the encoding is completed
56 | */
57 | var reEncodeWithModifications = function(url, coefficientModifier, callback) {
58 | getImageDataFromURL(url, function(data) {
59 | var encoder = new JPEGEncoder();
60 | var jpegURI = encoder.encodeAndModifyCoefficients(data, 75, modifyCoefficients);
61 | callback(jpegURI);
62 | });
63 | }
64 |
65 | return {
66 | getCoefficients: getCoefficients,
67 | reEncodeWithModifications: reEncodeWithModifications
68 | };
69 | })();
--------------------------------------------------------------------------------
/js-opensrc/jsstegencoder-1.0.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Based on Basic Blocking JPEG Encoder v 0.9a, ported and optimised by
4 | Andreas Ritter (www.bytestrom.eu, 11/2009).
5 |
6 | Modified by Owen Campbell-Moore (www.owencampbellmoore.com, 03/13) for
7 | easy DCT modification.
8 |
9 | Released by Andreas Ritter carrying both of the following licences:
10 |
11 | Copyright (c) 2008, Adobe Systems Incorporated
12 | All rights reserved.
13 |
14 | Redistribution and use in source and binary forms, with or without
15 | modification, are permitted provided that the following conditions are
16 | met:
17 |
18 | * Redistributions of source code must retain the above copyright notice,
19 | this list of conditions and the following disclaimer.
20 |
21 | * Redistributions in binary form must reproduce the above copyright
22 | notice, this list of conditions and the following disclaimer in the
23 | documentation and/or other materials provided with the distribution.
24 |
25 | * Neither the name of Adobe Systems Incorporated nor the names of its
26 | contributors may be used to endorse or promote products derived from
27 | this software without specific prior written permission.
28 |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
31 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
33 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 |
41 | -------------
42 |
43 | Licensed under the MIT License
44 |
45 | Copyright (c) 2009 Andreas Ritter
46 |
47 | Permission is hereby granted, free of charge, to any person obtaining a copy
48 | of this software and associated documentation files (the "Software"), to deal
49 | in the Software without restriction, including without limitation the rights
50 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
51 | copies of the Software, and to permit persons to whom the Software is
52 | furnished to do so, subject to the following conditions:
53 |
54 | The above copyright notice and this permission notice shall be included in
55 | all copies or substantial portions of the Software.
56 |
57 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
60 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
61 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
62 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
63 | THE SOFTWARE.
64 |
65 | */
66 | function JPEGEncoder() {
67 | var self = this;
68 | var fround = Math.round;
69 | var ffloor = Math.floor;
70 | var YTable = new Array(64);
71 | var UVTable = new Array(64);
72 | var fdtbl_Y = new Array(64);
73 | var fdtbl_UV = new Array(64);
74 | var YDC_HT;
75 | var UVDC_HT;
76 | var YAC_HT;
77 | var UVAC_HT;
78 |
79 | var bitcode = new Array(65535);
80 | var category = new Array(65535);
81 | var outputfDCTQuant = new Array(64);
82 | var DU = new Array(64);
83 | var byteout = [];
84 | var bytenew = 0;
85 | var bytepos = 7;
86 |
87 | var YDU = new Array(64);
88 | var UDU = new Array(64);
89 | var VDU = new Array(64);
90 | var clt = new Array(256);
91 | var RGB_YUV_TABLE = new Array(2048);
92 | var currentQuality;
93 |
94 | var ZigZag = [
95 | 0, 1, 5, 6,14,15,27,28,
96 | 2, 4, 7,13,16,26,29,42,
97 | 3, 8,12,17,25,30,41,43,
98 | 9,11,18,24,31,40,44,53,
99 | 10,19,23,32,39,45,52,54,
100 | 20,22,33,38,46,51,55,60,
101 | 21,34,37,47,50,56,59,61,
102 | 35,36,48,49,57,58,62,63
103 | ];
104 |
105 | var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
106 | var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
107 | var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
108 | var std_ac_luminance_values = [
109 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,
110 | 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
111 | 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
112 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
113 | 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,
114 | 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
115 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,
116 | 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
117 | 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
118 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
119 | 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
120 | 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
121 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,
122 | 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
123 | 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
124 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
125 | 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,
126 | 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
127 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
128 | 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
129 | 0xf9,0xfa
130 | ];
131 |
132 | var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
133 | var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
134 | var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
135 | var std_ac_chrominance_values = [
136 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,
137 | 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
138 | 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
139 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
140 | 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,
141 | 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
142 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,
143 | 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
144 | 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
145 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
146 | 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,
147 | 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
148 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,
149 | 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
150 | 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
151 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
152 | 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,
153 | 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
154 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
155 | 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
156 | 0xf9,0xfa
157 | ];
158 |
159 | function initQuantTables(sf){
160 | var YQT = [
161 | 16, 11, 10, 16, 24, 40, 51, 61,
162 | 12, 12, 14, 19, 26, 58, 60, 55,
163 | 14, 13, 16, 24, 40, 57, 69, 56,
164 | 14, 17, 22, 29, 51, 87, 80, 62,
165 | 18, 22, 37, 56, 68,109,103, 77,
166 | 24, 35, 55, 64, 81,104,113, 92,
167 | 49, 64, 78, 87,103,121,120,101,
168 | 72, 92, 95, 98,112,100,103, 99
169 | ];
170 |
171 | for (var i = 0; i < 64; i++) {
172 | var t = ffloor((YQT[i]*sf+50)/100);
173 | if (t < 1) {
174 | t = 1;
175 | } else if (t > 255) {
176 | t = 255;
177 | }
178 | YTable[ZigZag[i]] = t;
179 | }
180 |
181 | var UVQT = [
182 | 17, 18, 24, 47, 99, 99, 99, 99,
183 | 18, 21, 26, 66, 99, 99, 99, 99,
184 | 24, 26, 56, 99, 99, 99, 99, 99,
185 | 47, 66, 99, 99, 99, 99, 99, 99,
186 | 99, 99, 99, 99, 99, 99, 99, 99,
187 | 99, 99, 99, 99, 99, 99, 99, 99,
188 | 99, 99, 99, 99, 99, 99, 99, 99,
189 | 99, 99, 99, 99, 99, 99, 99, 99
190 | ];
191 | for (var j = 0; j < 64; j++) {
192 | var u = ffloor((UVQT[j]*sf+50)/100);
193 | if (u < 1) {
194 | u = 1;
195 | } else if (u > 255) {
196 | u = 255;
197 | }
198 | UVTable[ZigZag[j]] = u;
199 | }
200 | var aasf = [
201 | 1.0, 1.387039845, 1.306562965, 1.175875602,
202 | 1.0, 0.785694958, 0.541196100, 0.275899379
203 | ];
204 | var k = 0;
205 | for (var row = 0; row < 8; row++)
206 | {
207 | for (var col = 0; col < 8; col++)
208 | {
209 | fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
210 | fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
211 | k++;
212 | }
213 | }
214 | }
215 |
216 | function computeHuffmanTbl(nrcodes, std_table){
217 | var codevalue = 0;
218 | var pos_in_table = 0;
219 | var HT = new Array();
220 | for (var k = 1; k <= 16; k++) {
221 | for (var j = 1; j <= nrcodes[k]; j++) {
222 | HT[std_table[pos_in_table]] = [];
223 | HT[std_table[pos_in_table]][0] = codevalue;
224 | HT[std_table[pos_in_table]][1] = k;
225 | pos_in_table++;
226 | codevalue++;
227 | }
228 | codevalue*=2;
229 | }
230 | return HT;
231 | }
232 |
233 | function initHuffmanTbl()
234 | {
235 | YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);
236 | UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);
237 | YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);
238 | UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);
239 | }
240 |
241 | function initCategoryNumber()
242 | {
243 | var nrlower = 1;
244 | var nrupper = 2;
245 | for (var cat = 1; cat <= 15; cat++) {
246 | //Positive numbers
247 | for (var nr = nrlower; nr>0] = 38470 * i;
269 | RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000;
270 | RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i;
271 | RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i;
272 | RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF;
273 | RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i;
274 | RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i;
275 | }
276 | }
277 |
278 | // IO functions
279 | function writeBits(bs)
280 | {
281 | var value = bs[0];
282 | var posval = bs[1]-1;
283 | while ( posval >= 0 ) {
284 | if (value & (1 << posval) ) {
285 | bytenew |= (1 << bytepos);
286 | }
287 | posval--;
288 | bytepos--;
289 | if (bytepos < 0) {
290 | if (bytenew == 0xFF) {
291 | writeByte(0xFF);
292 | writeByte(0);
293 | }
294 | else {
295 | writeByte(bytenew);
296 | }
297 | bytepos=7;
298 | bytenew=0;
299 | }
300 | }
301 | }
302 |
303 | function writeByte(value)
304 | {
305 | byteout.push(clt[value]); // write char directly instead of converting later
306 | }
307 |
308 | function writeWord(value)
309 | {
310 | writeByte((value>>8)&0xFF);
311 | writeByte((value )&0xFF);
312 | }
313 |
314 | // DCT & quantization core
315 | function fDCTQuant(data, fdtbl)
316 | {
317 | var d0, d1, d2, d3, d4, d5, d6, d7;
318 | /* Pass 1: process rows. */
319 | var dataOff=0;
320 | var i;
321 | const I8 = 8;
322 | const I64 = 64;
323 | for (i=0; i 0.0) ? ((fDCTQuantVar + 0.5)|0) : ((fDCTQuantVar - 0.5)|0);
443 | //outputfDCTQuant[i] = fround(fDCTQuant);
444 |
445 | }
446 |
447 | return outputfDCTQuant;
448 | }
449 |
450 | function writeAPP0()
451 | {
452 | writeWord(0xFFE0); // marker
453 | writeWord(16); // length
454 | writeByte(0x4A); // J
455 | writeByte(0x46); // F
456 | writeByte(0x49); // I
457 | writeByte(0x46); // F
458 | writeByte(0); // = "JFIF",'\0'
459 | writeByte(1); // versionhi
460 | writeByte(1); // versionlo
461 | writeByte(0); // xyunits
462 | writeWord(1); // xdensity
463 | writeWord(1); // ydensity
464 | writeByte(0); // thumbnwidth
465 | writeByte(0); // thumbnheight
466 | }
467 |
468 | function writeSOF0(width, height)
469 | {
470 | writeWord(0xFFC0); // marker
471 | writeWord(17); // length, truecolor YUV JPG
472 | writeByte(8); // precision
473 | writeWord(height);
474 | writeWord(width);
475 | writeByte(3); // nrofcomponents
476 | writeByte(1); // IdY
477 | writeByte(0x11); // HVY
478 | writeByte(0); // QTY
479 | writeByte(2); // IdU
480 | writeByte(0x11); // HVU
481 | writeByte(1); // QTU
482 | writeByte(3); // IdV
483 | writeByte(0x11); // HVV
484 | writeByte(1); // QTV
485 | }
486 |
487 | function writeDQT()
488 | {
489 | writeWord(0xFFDB); // marker
490 | writeWord(132); // length
491 | writeByte(0);
492 | for (var i=0; i<64; i++) {
493 | writeByte(YTable[i]);
494 | }
495 | writeByte(1);
496 | for (var j=0; j<64; j++) {
497 | writeByte(UVTable[j]);
498 | }
499 | }
500 |
501 | function writeDHT()
502 | {
503 | writeWord(0xFFC4); // marker
504 | writeWord(0x01A2); // length
505 |
506 | writeByte(0); // HTYDCinfo
507 | for (var i=0; i<16; i++) {
508 | writeByte(std_dc_luminance_nrcodes[i+1]);
509 | }
510 | for (var j=0; j<=11; j++) {
511 | writeByte(std_dc_luminance_values[j]);
512 | }
513 |
514 | writeByte(0x10); // HTYACinfo
515 | for (var k=0; k<16; k++) {
516 | writeByte(std_ac_luminance_nrcodes[k+1]);
517 | }
518 | for (var l=0; l<=161; l++) {
519 | writeByte(std_ac_luminance_values[l]);
520 | }
521 |
522 | writeByte(1); // HTUDCinfo
523 | for (var m=0; m<16; m++) {
524 | writeByte(std_dc_chrominance_nrcodes[m+1]);
525 | }
526 | for (var n=0; n<=11; n++) {
527 | writeByte(std_dc_chrominance_values[n]);
528 | }
529 |
530 | writeByte(0x11); // HTUACinfo
531 | for (var o=0; o<16; o++) {
532 | writeByte(std_ac_chrominance_nrcodes[o+1]);
533 | }
534 | for (var p=0; p<=161; p++) {
535 | writeByte(std_ac_chrominance_values[p]);
536 | }
537 | }
538 |
539 | function writeSOS()
540 | {
541 | writeWord(0xFFDA); // marker
542 | writeWord(12); // length
543 | writeByte(3); // nrofcomponents
544 | writeByte(1); // IdY
545 | writeByte(0); // HTY
546 | writeByte(2); // IdU
547 | writeByte(0x11); // HTU
548 | writeByte(3); // IdV
549 | writeByte(0x11); // HTV
550 | writeByte(0); // Ss
551 | writeByte(0x3f); // Se
552 | writeByte(0); // Bf
553 | }
554 |
555 | function processDU(DU_DCT, DC, HTDC, HTAC){
556 | var EOB = HTAC[0x00];
557 | var M16zeroes = HTAC[0xF0];
558 | var pos;
559 | const I16 = 16;
560 | const I63 = 63;
561 | const I64 = 64;
562 |
563 | //ZigZag reorder
564 | for (var j=0;j0)&&(DU[end0pos]==0); end0pos--) {};
579 | //end0pos = first element in reverse order !=0
580 | if ( end0pos == 0) {
581 | writeBits(EOB);
582 | return DC;
583 | }
584 | var i = 1;
585 | var lng;
586 | while ( i <= end0pos ) {
587 | var startpos = i;
588 | for (; (DU[i]==0) && (i<=end0pos); ++i) {}
589 | var nrzeroes = i-startpos;
590 | if ( nrzeroes >= I16 ) {
591 | lng = nrzeroes>>4;
592 | for (var nrmarker=1; nrmarker <= lng; ++nrmarker)
593 | writeBits(M16zeroes);
594 | nrzeroes = nrzeroes&0xF;
595 | }
596 | pos = 32767+DU[i];
597 | writeBits(HTAC[(nrzeroes<<4)+category[pos]]);
598 | writeBits(bitcode[pos]);
599 | i++;
600 | }
601 | if ( end0pos != I63 ) {
602 | writeBits(EOB);
603 | }
604 | return DC;
605 | }
606 |
607 | function initCharLookupTable(){
608 | var sfcc = String.fromCharCode;
609 | for(var i=0; i < 256; i++){ ///// ACHTUNG // 255
610 | clt[i] = sfcc(i);
611 | }
612 | }
613 |
614 | this.encodeAndModifyCoefficients = function(image, quality, coefficientModifier) // image data object
615 | {
616 | var time_start = new Date().getTime();
617 |
618 | setQuality(quality);
619 |
620 | // Initialize bit writer
621 | byteout = new Array();
622 | bytenew=0;
623 | bytepos=7;
624 |
625 | // Add JPEG headers
626 | writeWord(0xFFD8); // SOI
627 | writeAPP0();
628 | writeDQT();
629 | writeSOF0(image.width,image.height);
630 | writeDHT();
631 | writeSOS();
632 |
633 |
634 | // Encode 8x8 macroblocks
635 | var DCY=0;
636 | var DCU=0;
637 | var DCV=0;
638 |
639 | bytenew=0;
640 | bytepos=7;
641 |
642 | var imageData = image.data;
643 | var width = image.width;
644 | var height = image.height;
645 |
646 | var quadWidth = width*4;
647 | var tripleWidth = width*3;
648 |
649 | var x, y = 0;
650 | var j = 0;
651 | var r, g, b;
652 | var start,p, col,row,pos;
653 | var DU_DCT_ARRAY = new Array();
654 | DU_DCT_ARRAY[0] = new Array();
655 | DU_DCT_ARRAY[1] = new Array();
656 | DU_DCT_ARRAY[2] = new Array();
657 | while(y < height){
658 | x = 0;
659 | while(x < quadWidth){
660 | start = quadWidth * y + x;
661 | p = start;
662 | col = -1;
663 | row = 0;
664 |
665 | for(pos=0; pos < 64; pos++){
666 | row = pos >> 3;// /8
667 | col = ( pos & 7 ) * 4; // %8
668 | p = start + ( row * quadWidth ) + col;
669 |
670 | if(y+row >= height){ // padding bottom
671 | p-= (quadWidth*(y+1+row-height));
672 | }
673 |
674 | if(x+col >= quadWidth){ // padding right
675 | p-= ((x+col) - quadWidth +4)
676 | }
677 |
678 | r = imageData[ p++ ];
679 | g = imageData[ p++ ];
680 | b = imageData[ p++ ];
681 |
682 |
683 | /* // calculate YUV values dynamically
684 | YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80
685 | UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));
686 | VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));
687 | */
688 |
689 | // use lookup table (slightly faster)
690 | YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128;
691 | UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;
692 | VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;
693 | }
694 |
695 | DU_DCT_ARRAY[0][j] = fDCTQuant(YDU, fdtbl_Y);
696 | DU_DCT_ARRAY[1][j] = fDCTQuant(UDU, fdtbl_UV);
697 | DU_DCT_ARRAY[2][j] = fDCTQuant(VDU, fdtbl_UV);
698 |
699 | x+=32;
700 | j++;
701 | }
702 | y+=8;
703 | }
704 |
705 | //This is where the passed in function gets to fiddle with the coefficients.
706 | coefficientModifier(DU_DCT_ARRAY);
707 |
708 | for (var i = 0; i < j; i++){
709 | DCY = processDU(DU_DCT_ARRAY[0][i], DCY, YDC_HT, YAC_HT);
710 | DCU = processDU(DU_DCT_ARRAY[1][i], DCU, UVDC_HT, UVAC_HT);
711 | DCV = processDU(DU_DCT_ARRAY[2][i], DCV, UVDC_HT, UVAC_HT);
712 | }
713 |
714 | // Do the bit alignment of the EOI marker
715 | if ( bytepos >= 0 ) {
716 | var fillbits = [];
717 | fillbits[1] = bytepos+1;
718 | fillbits[0] = (1<<(bytepos+1))-1;
719 | writeBits(fillbits);
720 | }
721 |
722 | writeWord(0xFFD9); //EOI
723 |
724 | var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));
725 |
726 | byteout = [];
727 |
728 | // benchmarking
729 | var duration = new Date().getTime() - time_start;
730 | console.log('Encoding time: '+ duration + 'ms');
731 | //
732 |
733 | return jpegDataUri
734 | }
735 |
736 | function setQuality(quality){
737 | if (quality <= 0) {
738 | quality = 1;
739 | }
740 | if (quality > 100) {
741 | quality = 100;
742 | }
743 |
744 | if(currentQuality == quality) return // don't recalc if unchanged
745 |
746 | var sf = 0;
747 | if (quality < 50) {
748 | sf = Math.floor(5000 / quality);
749 | } else {
750 | sf = Math.floor(200 - quality*2);
751 | }
752 |
753 | initQuantTables(sf);
754 | currentQuality = quality;
755 | console.log('Quality set to: '+quality +'%');
756 | }
757 |
758 | function init(){
759 | var time_start = new Date().getTime();
760 | // Create tables
761 | initCharLookupTable()
762 | initHuffmanTbl();
763 | initCategoryNumber();
764 | initRGBYUVTable();
765 |
766 | var duration = new Date().getTime() - time_start;
767 | console.log('Initialization '+ duration + 'ms');
768 | }
769 |
770 | init();
771 |
772 | };
--------------------------------------------------------------------------------
/js-opensrc/lz-string.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2013 Pieroxy
2 | // This work is free. You can redistribute it and/or modify it
3 | // under the terms of the WTFPL, Version 2
4 | // For more information see LICENSE.txt or http://www.wtfpl.net/
5 | //
6 | // For more information, the home page:
7 | // http://pieroxy.net/blog/pages/lz-string/testing.html
8 | //
9 | // LZ-based compression algorithm, version 1.4.4
10 | var LZString = (function() {
11 |
12 | // private property
13 | var f = String.fromCharCode;
14 | var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
15 | var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";
16 | var baseReverseDic = {};
17 |
18 | function getBaseValue(alphabet, character) {
19 | if (!baseReverseDic[alphabet]) {
20 | baseReverseDic[alphabet] = {};
21 | for (var i=0 ; i>> 8;
66 | buf[i*2+1] = current_value % 256;
67 | }
68 | return buf;
69 | },
70 |
71 | //decompress from uint8array (UCS-2 big endian format)
72 | decompressFromUint8Array:function (compressed) {
73 | if (compressed===null || compressed===undefined){
74 | return LZString.decompress(compressed);
75 | } else {
76 | var buf=new Array(compressed.length/2); // 2 bytes per character
77 | for (var i=0, TotalLen=buf.length; i> 1;
159 | }
160 | } else {
161 | value = 1;
162 | for (i=0 ; i> 1;
184 | }
185 | }
186 | context_enlargeIn--;
187 | if (context_enlargeIn == 0) {
188 | context_enlargeIn = Math.pow(2, context_numBits);
189 | context_numBits++;
190 | }
191 | delete context_dictionaryToCreate[context_w];
192 | } else {
193 | value = context_dictionary[context_w];
194 | for (i=0 ; i> 1;
204 | }
205 |
206 |
207 | }
208 | context_enlargeIn--;
209 | if (context_enlargeIn == 0) {
210 | context_enlargeIn = Math.pow(2, context_numBits);
211 | context_numBits++;
212 | }
213 | // Add wc to the dictionary.
214 | context_dictionary[context_wc] = context_dictSize++;
215 | context_w = String(context_c);
216 | }
217 | }
218 |
219 | // Output the code for w.
220 | if (context_w !== "") {
221 | if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
222 | if (context_w.charCodeAt(0)<256) {
223 | for (i=0 ; i> 1;
244 | }
245 | } else {
246 | value = 1;
247 | for (i=0 ; i> 1;
269 | }
270 | }
271 | context_enlargeIn--;
272 | if (context_enlargeIn == 0) {
273 | context_enlargeIn = Math.pow(2, context_numBits);
274 | context_numBits++;
275 | }
276 | delete context_dictionaryToCreate[context_w];
277 | } else {
278 | value = context_dictionary[context_w];
279 | for (i=0 ; i> 1;
289 | }
290 |
291 |
292 | }
293 | context_enlargeIn--;
294 | if (context_enlargeIn == 0) {
295 | context_enlargeIn = Math.pow(2, context_numBits);
296 | context_numBits++;
297 | }
298 | }
299 |
300 | // Mark the end of the stream
301 | value = 2;
302 | for (i=0 ; i> 1;
312 | }
313 |
314 | // Flush the last char
315 | while (true) {
316 | context_data_val = (context_data_val << 1);
317 | if (context_data_position == bitsPerChar-1) {
318 | context_data.push(getCharFromInt(context_data_val));
319 | break;
320 | }
321 | else context_data_position++;
322 | }
323 | return context_data.join('');
324 | },
325 |
326 | decompress: function (compressed) {
327 | if (compressed == null) return "";
328 | if (compressed == "") return null;
329 | return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); });
330 | },
331 |
332 | _decompress: function (length, resetValue, getNextValue) {
333 | var dictionary = [],
334 | next,
335 | enlargeIn = 4,
336 | dictSize = 4,
337 | numBits = 3,
338 | entry = "",
339 | result = [],
340 | i,
341 | w,
342 | bits, resb, maxpower, power,
343 | c,
344 | data = {val:getNextValue(0), position:resetValue, index:1};
345 |
346 | for (i = 0; i < 3; i += 1) {
347 | dictionary[i] = i;
348 | }
349 |
350 | bits = 0;
351 | maxpower = Math.pow(2,2);
352 | power=1;
353 | while (power!=maxpower) {
354 | resb = data.val & data.position;
355 | data.position >>= 1;
356 | if (data.position == 0) {
357 | data.position = resetValue;
358 | data.val = getNextValue(data.index++);
359 | }
360 | bits |= (resb>0 ? 1 : 0) * power;
361 | power <<= 1;
362 | }
363 |
364 | switch (next = bits) {
365 | case 0:
366 | bits = 0;
367 | maxpower = Math.pow(2,8);
368 | power=1;
369 | while (power!=maxpower) {
370 | resb = data.val & data.position;
371 | data.position >>= 1;
372 | if (data.position == 0) {
373 | data.position = resetValue;
374 | data.val = getNextValue(data.index++);
375 | }
376 | bits |= (resb>0 ? 1 : 0) * power;
377 | power <<= 1;
378 | }
379 | c = f(bits);
380 | break;
381 | case 1:
382 | bits = 0;
383 | maxpower = Math.pow(2,16);
384 | power=1;
385 | while (power!=maxpower) {
386 | resb = data.val & data.position;
387 | data.position >>= 1;
388 | if (data.position == 0) {
389 | data.position = resetValue;
390 | data.val = getNextValue(data.index++);
391 | }
392 | bits |= (resb>0 ? 1 : 0) * power;
393 | power <<= 1;
394 | }
395 | c = f(bits);
396 | break;
397 | case 2:
398 | return "";
399 | }
400 | dictionary[3] = c;
401 | w = c;
402 | result.push(c);
403 | while (true) {
404 | if (data.index > length) {
405 | return "";
406 | }
407 |
408 | bits = 0;
409 | maxpower = Math.pow(2,numBits);
410 | power=1;
411 | while (power!=maxpower) {
412 | resb = data.val & data.position;
413 | data.position >>= 1;
414 | if (data.position == 0) {
415 | data.position = resetValue;
416 | data.val = getNextValue(data.index++);
417 | }
418 | bits |= (resb>0 ? 1 : 0) * power;
419 | power <<= 1;
420 | }
421 |
422 | switch (c = bits) {
423 | case 0:
424 | bits = 0;
425 | maxpower = Math.pow(2,8);
426 | power=1;
427 | while (power!=maxpower) {
428 | resb = data.val & data.position;
429 | data.position >>= 1;
430 | if (data.position == 0) {
431 | data.position = resetValue;
432 | data.val = getNextValue(data.index++);
433 | }
434 | bits |= (resb>0 ? 1 : 0) * power;
435 | power <<= 1;
436 | }
437 |
438 | dictionary[dictSize++] = f(bits);
439 | c = dictSize-1;
440 | enlargeIn--;
441 | break;
442 | case 1:
443 | bits = 0;
444 | maxpower = Math.pow(2,16);
445 | power=1;
446 | while (power!=maxpower) {
447 | resb = data.val & data.position;
448 | data.position >>= 1;
449 | if (data.position == 0) {
450 | data.position = resetValue;
451 | data.val = getNextValue(data.index++);
452 | }
453 | bits |= (resb>0 ? 1 : 0) * power;
454 | power <<= 1;
455 | }
456 | dictionary[dictSize++] = f(bits);
457 | c = dictSize-1;
458 | enlargeIn--;
459 | break;
460 | case 2:
461 | return result.join('');
462 | }
463 |
464 | if (enlargeIn == 0) {
465 | enlargeIn = Math.pow(2, numBits);
466 | numBits++;
467 | }
468 |
469 | if (dictionary[c]) {
470 | entry = dictionary[c];
471 | } else {
472 | if (c === dictSize) {
473 | entry = w + w.charAt(0);
474 | } else {
475 | return null;
476 | }
477 | }
478 | result.push(entry);
479 |
480 | // Add w+entry[0] to the dictionary.
481 | dictionary[dictSize++] = w + entry.charAt(0);
482 | enlargeIn--;
483 |
484 | w = entry;
485 |
486 | if (enlargeIn == 0) {
487 | enlargeIn = Math.pow(2, numBits);
488 | numBits++;
489 | }
490 |
491 | }
492 | }
493 | };
494 | return LZString;
495 | })();
496 |
497 | if (typeof define === 'function' && define.amd) {
498 | define(function () { return LZString; });
499 | } else if( typeof module !== 'undefined' && module != null ) {
500 | module.exports = LZString
501 | }
502 |
--------------------------------------------------------------------------------
/js-opensrc/nacl-util.js:
--------------------------------------------------------------------------------
1 | // taken from an old version of TweetNaCl, which dropped this code eventually
2 |
3 | nacl.util = {};
4 |
5 | nacl.util.decodeUTF8 = function(s) {
6 | var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
7 | for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
8 | return b;
9 | };
10 |
11 | nacl.util.encodeUTF8 = function(arr) {
12 | var i, s = [];
13 | for (i = 0; i < arr.length; i++) s.push(String.fromCharCode(arr[i]));
14 | return decodeURIComponent(escape(s.join('')));
15 | };
16 |
17 | nacl.util.encodeBase64 = function(arr) {
18 | if (typeof btoa === 'undefined') {
19 | return (new Buffer(arr)).toString('base64');
20 | } else {
21 | var i, s = [], len = arr.length;
22 | for (i = 0; i < len; i++) s.push(String.fromCharCode(arr[i]));
23 | return btoa(s.join(''));
24 | }
25 | };
26 |
27 | nacl.util.decodeBase64 = function(s) {
28 | if (typeof atob === 'undefined') {
29 | return new Uint8Array(Array.prototype.slice.call(new Buffer(s, 'base64'), 0));
30 | } else {
31 | try{ //added for PassLok because atob may fail
32 | var i, d = atob(s), b = new Uint8Array(d.length);
33 | }catch(error){
34 | return false
35 | }
36 | for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
37 | return b;
38 | }
39 | };
--------------------------------------------------------------------------------
/js-opensrc/scrypt-async.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Fast "async" scrypt implementation in JavaScript.
3 | * Copyright (c) 2013-2016 Dmitry Chestnykh | BSD License
4 | * https://github.com/dchest/scrypt-async-js
5 | */
6 |
7 | /**
8 | * scrypt(password, salt, options, callback)
9 | *
10 | * where
11 | *
12 | * password and salt are strings or arrays of bytes (Array of Uint8Array)
13 | * options is
14 | *
15 | * {
16 | * N: // CPU/memory cost parameter, must be power of two
17 | * // (alternatively, you can specify logN)
18 | * r: // block size
19 | * p: // parallelization parameter
20 | * dkLen: // length of derived key, default = 32
21 | * encoding: // optional encoding:
22 | * "base64" - standard Base64 encoding
23 | * "hex" — hex encoding,
24 | * "binary" — Uint8Array,
25 | * undefined/null - Array of bytes
26 | * interruptStep: // optional, steps to split calculations (default is 0)
27 | * }
28 | *
29 | * Derives a key from password and salt and calls callback
30 | * with derived key as the only argument.
31 | *
32 | * Calculations are interrupted with setImmediate (or zero setTimeout) at the
33 | * given interruptSteps to avoid freezing the browser. If it's undefined or zero,
34 | * the callback is called immediately after the calculation, avoiding setImmediate.
35 | *
36 | * Legacy way (only supports p = 1) to call this function is:
37 | *
38 | * scrypt(password, salt, logN, r, dkLen, [interruptStep], callback, [encoding])
39 | *
40 | * In legacy API, if interruptStep is not given, it defaults to 1000.
41 | * Pass 0 to have callback called immediately.
42 | *
43 | */
44 | function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encoding) {
45 | 'use strict';
46 |
47 | function SHA256(m) {
48 | /** @const */ var K = [
49 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
50 | 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
51 | 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
52 | 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
53 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
54 | 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
55 | 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
56 | 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
57 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
58 | 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
59 | 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
60 | 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
61 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
62 | ];
63 |
64 | var h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a,
65 | h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19,
66 | w = new Array(64);
67 |
68 | function blocks(p) {
69 | var off = 0, len = p.length;
70 | while (len >= 64) {
71 | var a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7,
72 | u, i, j, t1, t2;
73 |
74 | for (i = 0; i < 16; i++) {
75 | j = off + i*4;
76 | w[i] = ((p[j] & 0xff)<<24) | ((p[j+1] & 0xff)<<16) |
77 | ((p[j+2] & 0xff)<<8) | (p[j+3] & 0xff);
78 | }
79 |
80 | for (i = 16; i < 64; i++) {
81 | u = w[i-2];
82 | t1 = ((u>>>17) | (u<<(32-17))) ^ ((u>>>19) | (u<<(32-19))) ^ (u>>>10);
83 |
84 | u = w[i-15];
85 | t2 = ((u>>>7) | (u<<(32-7))) ^ ((u>>>18) | (u<<(32-18))) ^ (u>>>3);
86 |
87 | w[i] = (((t1 + w[i-7]) | 0) + ((t2 + w[i-16]) | 0)) | 0;
88 | }
89 |
90 | for (i = 0; i < 64; i++) {
91 | t1 = ((((((e>>>6) | (e<<(32-6))) ^ ((e>>>11) | (e<<(32-11))) ^
92 | ((e>>>25) | (e<<(32-25)))) + ((e & f) ^ (~e & g))) | 0) +
93 | ((h + ((K[i] + w[i]) | 0)) | 0)) | 0;
94 |
95 | t2 = ((((a>>>2) | (a<<(32-2))) ^ ((a>>>13) | (a<<(32-13))) ^
96 | ((a>>>22) | (a<<(32-22)))) + ((a & b) ^ (a & c) ^ (b & c))) | 0;
97 |
98 | h = g;
99 | g = f;
100 | f = e;
101 | e = (d + t1) | 0;
102 | d = c;
103 | c = b;
104 | b = a;
105 | a = (t1 + t2) | 0;
106 | }
107 |
108 | h0 = (h0 + a) | 0;
109 | h1 = (h1 + b) | 0;
110 | h2 = (h2 + c) | 0;
111 | h3 = (h3 + d) | 0;
112 | h4 = (h4 + e) | 0;
113 | h5 = (h5 + f) | 0;
114 | h6 = (h6 + g) | 0;
115 | h7 = (h7 + h) | 0;
116 |
117 | off += 64;
118 | len -= 64;
119 | }
120 | }
121 |
122 | blocks(m);
123 |
124 | var i, bytesLeft = m.length % 64,
125 | bitLenHi = (m.length / 0x20000000) | 0,
126 | bitLenLo = m.length << 3,
127 | numZeros = (bytesLeft < 56) ? 56 : 120,
128 | p = m.slice(m.length - bytesLeft, m.length);
129 |
130 | p.push(0x80);
131 | for (i = bytesLeft + 1; i < numZeros; i++) p.push(0);
132 | p.push((bitLenHi>>>24) & 0xff);
133 | p.push((bitLenHi>>>16) & 0xff);
134 | p.push((bitLenHi>>>8) & 0xff);
135 | p.push((bitLenHi>>>0) & 0xff);
136 | p.push((bitLenLo>>>24) & 0xff);
137 | p.push((bitLenLo>>>16) & 0xff);
138 | p.push((bitLenLo>>>8) & 0xff);
139 | p.push((bitLenLo>>>0) & 0xff);
140 |
141 | blocks(p);
142 |
143 | return [
144 | (h0>>>24) & 0xff, (h0>>>16) & 0xff, (h0>>>8) & 0xff, (h0>>>0) & 0xff,
145 | (h1>>>24) & 0xff, (h1>>>16) & 0xff, (h1>>>8) & 0xff, (h1>>>0) & 0xff,
146 | (h2>>>24) & 0xff, (h2>>>16) & 0xff, (h2>>>8) & 0xff, (h2>>>0) & 0xff,
147 | (h3>>>24) & 0xff, (h3>>>16) & 0xff, (h3>>>8) & 0xff, (h3>>>0) & 0xff,
148 | (h4>>>24) & 0xff, (h4>>>16) & 0xff, (h4>>>8) & 0xff, (h4>>>0) & 0xff,
149 | (h5>>>24) & 0xff, (h5>>>16) & 0xff, (h5>>>8) & 0xff, (h5>>>0) & 0xff,
150 | (h6>>>24) & 0xff, (h6>>>16) & 0xff, (h6>>>8) & 0xff, (h6>>>0) & 0xff,
151 | (h7>>>24) & 0xff, (h7>>>16) & 0xff, (h7>>>8) & 0xff, (h7>>>0) & 0xff
152 | ];
153 | }
154 |
155 | function PBKDF2_HMAC_SHA256_OneIter(password, salt, dkLen) {
156 | // compress password if it's longer than hash block length
157 | if(password.length > 64) {
158 | // SHA256 expects password to be an Array. If it's not
159 | // (i.e. it doesn't have .push method), convert it to one.
160 | password = SHA256(password.push ? password : Array.prototype.slice.call(password, 0));
161 | }
162 |
163 | var i, innerLen = 64 + salt.length + 4,
164 | inner = new Array(innerLen),
165 | outerKey = new Array(64),
166 | dk = [];
167 |
168 | // inner = (password ^ ipad) || salt || counter
169 | for (i = 0; i < 64; i++) inner[i] = 0x36;
170 | for (i = 0; i < password.length; i++) inner[i] ^= password[i];
171 | for (i = 0; i < salt.length; i++) inner[64+i] = salt[i];
172 | for (i = innerLen - 4; i < innerLen; i++) inner[i] = 0;
173 |
174 | // outerKey = password ^ opad
175 | for (i = 0; i < 64; i++) outerKey[i] = 0x5c;
176 | for (i = 0; i < password.length; i++) outerKey[i] ^= password[i];
177 |
178 | // increments counter inside inner
179 | function incrementCounter() {
180 | for (var i = innerLen-1; i >= innerLen-4; i--) {
181 | inner[i]++;
182 | if (inner[i] <= 0xff) return;
183 | inner[i] = 0;
184 | }
185 | }
186 |
187 | // output blocks = SHA256(outerKey || SHA256(inner)) ...
188 | while (dkLen >= 32) {
189 | incrementCounter();
190 | dk = dk.concat(SHA256(outerKey.concat(SHA256(inner))));
191 | dkLen -= 32;
192 | }
193 | if (dkLen > 0) {
194 | incrementCounter();
195 | dk = dk.concat(SHA256(outerKey.concat(SHA256(inner))).slice(0, dkLen));
196 | }
197 | return dk;
198 | }
199 |
200 | function salsaXOR(tmp, B, bin, bout) {
201 | var j0 = tmp[0] ^ B[bin++],
202 | j1 = tmp[1] ^ B[bin++],
203 | j2 = tmp[2] ^ B[bin++],
204 | j3 = tmp[3] ^ B[bin++],
205 | j4 = tmp[4] ^ B[bin++],
206 | j5 = tmp[5] ^ B[bin++],
207 | j6 = tmp[6] ^ B[bin++],
208 | j7 = tmp[7] ^ B[bin++],
209 | j8 = tmp[8] ^ B[bin++],
210 | j9 = tmp[9] ^ B[bin++],
211 | j10 = tmp[10] ^ B[bin++],
212 | j11 = tmp[11] ^ B[bin++],
213 | j12 = tmp[12] ^ B[bin++],
214 | j13 = tmp[13] ^ B[bin++],
215 | j14 = tmp[14] ^ B[bin++],
216 | j15 = tmp[15] ^ B[bin++],
217 | u, i;
218 |
219 | var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7,
220 | x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14,
221 | x15 = j15;
222 |
223 | for (i = 0; i < 8; i += 2) {
224 | u = x0 + x12; x4 ^= u<<7 | u>>>(32-7);
225 | u = x4 + x0; x8 ^= u<<9 | u>>>(32-9);
226 | u = x8 + x4; x12 ^= u<<13 | u>>>(32-13);
227 | u = x12 + x8; x0 ^= u<<18 | u>>>(32-18);
228 |
229 | u = x5 + x1; x9 ^= u<<7 | u>>>(32-7);
230 | u = x9 + x5; x13 ^= u<<9 | u>>>(32-9);
231 | u = x13 + x9; x1 ^= u<<13 | u>>>(32-13);
232 | u = x1 + x13; x5 ^= u<<18 | u>>>(32-18);
233 |
234 | u = x10 + x6; x14 ^= u<<7 | u>>>(32-7);
235 | u = x14 + x10; x2 ^= u<<9 | u>>>(32-9);
236 | u = x2 + x14; x6 ^= u<<13 | u>>>(32-13);
237 | u = x6 + x2; x10 ^= u<<18 | u>>>(32-18);
238 |
239 | u = x15 + x11; x3 ^= u<<7 | u>>>(32-7);
240 | u = x3 + x15; x7 ^= u<<9 | u>>>(32-9);
241 | u = x7 + x3; x11 ^= u<<13 | u>>>(32-13);
242 | u = x11 + x7; x15 ^= u<<18 | u>>>(32-18);
243 |
244 | u = x0 + x3; x1 ^= u<<7 | u>>>(32-7);
245 | u = x1 + x0; x2 ^= u<<9 | u>>>(32-9);
246 | u = x2 + x1; x3 ^= u<<13 | u>>>(32-13);
247 | u = x3 + x2; x0 ^= u<<18 | u>>>(32-18);
248 |
249 | u = x5 + x4; x6 ^= u<<7 | u>>>(32-7);
250 | u = x6 + x5; x7 ^= u<<9 | u>>>(32-9);
251 | u = x7 + x6; x4 ^= u<<13 | u>>>(32-13);
252 | u = x4 + x7; x5 ^= u<<18 | u>>>(32-18);
253 |
254 | u = x10 + x9; x11 ^= u<<7 | u>>>(32-7);
255 | u = x11 + x10; x8 ^= u<<9 | u>>>(32-9);
256 | u = x8 + x11; x9 ^= u<<13 | u>>>(32-13);
257 | u = x9 + x8; x10 ^= u<<18 | u>>>(32-18);
258 |
259 | u = x15 + x14; x12 ^= u<<7 | u>>>(32-7);
260 | u = x12 + x15; x13 ^= u<<9 | u>>>(32-9);
261 | u = x13 + x12; x14 ^= u<<13 | u>>>(32-13);
262 | u = x14 + x13; x15 ^= u<<18 | u>>>(32-18);
263 | }
264 |
265 | B[bout++] = tmp[0] = (x0 + j0) | 0;
266 | B[bout++] = tmp[1] = (x1 + j1) | 0;
267 | B[bout++] = tmp[2] = (x2 + j2) | 0;
268 | B[bout++] = tmp[3] = (x3 + j3) | 0;
269 | B[bout++] = tmp[4] = (x4 + j4) | 0;
270 | B[bout++] = tmp[5] = (x5 + j5) | 0;
271 | B[bout++] = tmp[6] = (x6 + j6) | 0;
272 | B[bout++] = tmp[7] = (x7 + j7) | 0;
273 | B[bout++] = tmp[8] = (x8 + j8) | 0;
274 | B[bout++] = tmp[9] = (x9 + j9) | 0;
275 | B[bout++] = tmp[10] = (x10 + j10) | 0;
276 | B[bout++] = tmp[11] = (x11 + j11) | 0;
277 | B[bout++] = tmp[12] = (x12 + j12) | 0;
278 | B[bout++] = tmp[13] = (x13 + j13) | 0;
279 | B[bout++] = tmp[14] = (x14 + j14) | 0;
280 | B[bout++] = tmp[15] = (x15 + j15) | 0;
281 | }
282 |
283 | function blockCopy(dst, di, src, si, len) {
284 | while (len--) dst[di++] = src[si++];
285 | }
286 |
287 | function blockXOR(dst, di, src, si, len) {
288 | while (len--) dst[di++] ^= src[si++];
289 | }
290 |
291 | function blockMix(tmp, B, bin, bout, r) {
292 | blockCopy(tmp, 0, B, bin + (2*r-1)*16, 16);
293 | for (var i = 0; i < 2*r; i += 2) {
294 | salsaXOR(tmp, B, bin + i*16, bout + i*8);
295 | salsaXOR(tmp, B, bin + i*16 + 16, bout + i*8 + r*16);
296 | }
297 | }
298 |
299 | function integerify(B, bi, r) {
300 | return B[bi+(2*r-1)*16];
301 | }
302 |
303 | function stringToUTF8Bytes(s) {
304 | var arr = [];
305 | for (var i = 0; i < s.length; i++) {
306 | var c = s.charCodeAt(i);
307 | if (c < 0x80) {
308 | arr.push(c);
309 | } else if (c < 0x800) {
310 | arr.push(0xc0 | c >> 6);
311 | arr.push(0x80 | c & 0x3f);
312 | } else if (c < 0xd800) {
313 | arr.push(0xe0 | c >> 12);
314 | arr.push(0x80 | (c >> 6) & 0x3f);
315 | arr.push(0x80 | c & 0x3f);
316 | } else {
317 | if (i >= s.length - 1) {
318 | throw new Error('invalid string');
319 | }
320 | i++; // get one more character
321 | c = (c & 0x3ff) << 10;
322 | c |= s.charCodeAt(i) & 0x3ff;
323 | c += 0x10000;
324 |
325 | arr.push(0xf0 | c >> 18);
326 | arr.push(0x80 | (c >> 12) & 0x3f);
327 | arr.push(0x80 | (c >> 6) & 0x3f);
328 | arr.push(0x80 | c & 0x3f);
329 | }
330 | }
331 | return arr;
332 | }
333 |
334 | function bytesToHex(p) {
335 | /** @const */
336 | var enc = '0123456789abcdef'.split('');
337 |
338 | var len = p.length,
339 | arr = [],
340 | i = 0;
341 |
342 | for (; i < len; i++) {
343 | arr.push(enc[(p[i]>>>4) & 15]);
344 | arr.push(enc[(p[i]>>>0) & 15]);
345 | }
346 | return arr.join('');
347 | }
348 |
349 | function bytesToBase64(p) {
350 | /** @const */
351 | var enc = ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
352 | '0123456789+/').split('');
353 |
354 | var len = p.length,
355 | arr = [],
356 | i = 0,
357 | a, b, c, t;
358 |
359 | while (i < len) {
360 | a = i < len ? p[i++] : 0;
361 | b = i < len ? p[i++] : 0;
362 | c = i < len ? p[i++] : 0;
363 | t = (a << 16) + (b << 8) + c;
364 | arr.push(enc[(t >>> 3 * 6) & 63]);
365 | arr.push(enc[(t >>> 2 * 6) & 63]);
366 | arr.push(enc[(t >>> 1 * 6) & 63]);
367 | arr.push(enc[(t >>> 0 * 6) & 63]);
368 | }
369 | if (len % 3 > 0) {
370 | arr[arr.length-1] = '=';
371 | if (len % 3 === 1) arr[arr.length-2] = '=';
372 | }
373 | return arr.join('');
374 | }
375 |
376 |
377 | // Generate key.
378 |
379 | var MAX_UINT = (-1)>>>0,
380 | p = 1;
381 |
382 | if (typeof logN === "object") {
383 | // Called as: scrypt(password, salt, opts, callback)
384 | if (arguments.length > 4) {
385 | throw new Error('scrypt: incorrect number of arguments');
386 | }
387 |
388 | var opts = logN;
389 |
390 | callback = r;
391 | logN = opts.logN;
392 | if (typeof logN === 'undefined') {
393 | if (typeof opts.N !== 'undefined') {
394 | if (opts.N < 2 || opts.N > MAX_UINT)
395 | throw new Error('scrypt: N is out of range');
396 |
397 | if ((opts.N & (opts.N - 1)) !== 0)
398 | throw new Error('scrypt: N is not a power of 2');
399 |
400 | logN = Math.log(opts.N) / Math.LN2;
401 | } else {
402 | throw new Error('scrypt: missing N parameter');
403 | }
404 | }
405 |
406 | // XXX: If opts.p or opts.dkLen is 0, it will be set to the default value
407 | // instead of throwing due to incorrect value. To avoid breaking
408 | // compatibility, this will only be changed in the next major version.
409 | p = opts.p || 1;
410 | r = opts.r;
411 | dkLen = opts.dkLen || 32;
412 | interruptStep = opts.interruptStep || 0;
413 | encoding = opts.encoding;
414 | }
415 |
416 | if (p < 1)
417 | throw new Error('scrypt: invalid p');
418 |
419 | if (r <= 0)
420 | throw new Error('scrypt: invalid r');
421 |
422 | if (logN < 1 || logN > 31)
423 | throw new Error('scrypt: logN must be between 1 and 31');
424 |
425 |
426 | var N = (1<>>0,
427 | XY, V, B, tmp;
428 |
429 | if (r*p >= 1<<30 || r > MAX_UINT/128/p || r > MAX_UINT/256 || N > MAX_UINT/128/r)
430 | throw new Error('scrypt: parameters are too large');
431 |
432 | // Decode strings.
433 | if (typeof password === 'string')
434 | password = stringToUTF8Bytes(password);
435 | if (typeof salt === 'string')
436 | salt = stringToUTF8Bytes(salt);
437 |
438 | if (typeof Int32Array !== 'undefined') {
439 | //XXX We can use Uint32Array, but Int32Array is faster in Safari.
440 | XY = new Int32Array(64*r);
441 | V = new Int32Array(32*N*r);
442 | tmp = new Int32Array(16);
443 | } else {
444 | XY = [];
445 | V = [];
446 | tmp = new Array(16);
447 | }
448 | B = PBKDF2_HMAC_SHA256_OneIter(password, salt, p*128*r);
449 |
450 | var xi = 0, yi = 32 * r;
451 |
452 | function smixStart(pos) {
453 | for (var i = 0; i < 32*r; i++) {
454 | var j = pos + i*4;
455 | XY[xi+i] = ((B[j+3] & 0xff)<<24) | ((B[j+2] & 0xff)<<16) |
456 | ((B[j+1] & 0xff)<<8) | ((B[j+0] & 0xff)<<0);
457 | }
458 | }
459 |
460 | function smixStep1(start, end) {
461 | for (var i = start; i < end; i += 2) {
462 | blockCopy(V, i*(32*r), XY, xi, 32*r);
463 | blockMix(tmp, XY, xi, yi, r);
464 |
465 | blockCopy(V, (i+1)*(32*r), XY, yi, 32*r);
466 | blockMix(tmp, XY, yi, xi, r);
467 | }
468 | }
469 |
470 | function smixStep2(start, end) {
471 | for (var i = start; i < end; i += 2) {
472 | var j = integerify(XY, xi, r) & (N-1);
473 | blockXOR(XY, xi, V, j*(32*r), 32*r);
474 | blockMix(tmp, XY, xi, yi, r);
475 |
476 | j = integerify(XY, yi, r) & (N-1);
477 | blockXOR(XY, yi, V, j*(32*r), 32*r);
478 | blockMix(tmp, XY, yi, xi, r);
479 | }
480 | }
481 |
482 | function smixFinish(pos) {
483 | for (var i = 0; i < 32*r; i++) {
484 | var j = XY[xi+i];
485 | B[pos + i*4 + 0] = (j>>>0) & 0xff;
486 | B[pos + i*4 + 1] = (j>>>8) & 0xff;
487 | B[pos + i*4 + 2] = (j>>>16) & 0xff;
488 | B[pos + i*4 + 3] = (j>>>24) & 0xff;
489 | }
490 | }
491 |
492 | var nextTick = (typeof setImmediate !== 'undefined') ? setImmediate : setTimeout;
493 |
494 | function interruptedFor(start, end, step, fn, donefn) {
495 | (function performStep() {
496 | nextTick(function() {
497 | fn(start, start + step < end ? start + step : end);
498 | start += step;
499 | if (start < end)
500 | performStep();
501 | else
502 | donefn();
503 | });
504 | })();
505 | }
506 |
507 | function getResult(enc) {
508 | var result = PBKDF2_HMAC_SHA256_OneIter(password, B, dkLen);
509 | if (enc === 'base64')
510 | return bytesToBase64(result);
511 | else if (enc === 'hex')
512 | return bytesToHex(result);
513 | else if (enc === 'binary')
514 | return new Uint8Array(result);
515 | else
516 | return result;
517 | }
518 |
519 | // Blocking variant.
520 | function calculateSync() {
521 | for (var i = 0; i < p; i++) {
522 | smixStart(i*128*r);
523 | smixStep1(0, N);
524 | smixStep2(0, N);
525 | smixFinish(i*128*r);
526 | }
527 | callback(getResult(encoding));
528 | }
529 |
530 | // Async variant.
531 | function calculateAsync(i) {
532 | smixStart(i*128*r);
533 | interruptedFor(0, N, interruptStep*2, smixStep1, function() {
534 | interruptedFor(0, N, interruptStep*2, smixStep2, function () {
535 | smixFinish(i*128*r);
536 | if (i + 1 < p) {
537 | nextTick(function() { calculateAsync(i + 1); });
538 | } else {
539 | callback(getResult(encoding));
540 | }
541 | });
542 | });
543 | }
544 |
545 | if (typeof interruptStep === 'function') {
546 | // Called as: scrypt(..., callback, [encoding])
547 | // shifting: scrypt(..., interruptStep, callback, [encoding])
548 | encoding = callback;
549 | callback = interruptStep;
550 | interruptStep = 1000;
551 | }
552 |
553 | if (interruptStep <= 0) {
554 | calculateSync();
555 | } else {
556 | calculateAsync(0);
557 | }
558 | }
559 |
560 | if (typeof module !== 'undefined') module.exports = scrypt;
561 |
--------------------------------------------------------------------------------
/js-opensrc/secrets.js:
--------------------------------------------------------------------------------
1 | //edit in lines 153-156 by F. Ruiz in order to use NaCl RNG function rather than the built-in RNG. Chunks of code commented out as a consequence. Another edit in 341-346 because the original padLeft function didn't work on iPad
2 | // secrets.js - by Alexander Stetsyuk - released under MIT License
3 | (function(exports, global){
4 | var defaults = {
5 | bits: 8, // default number of bits
6 | radix: 16, // work with HEX by default
7 | minBits: 3,
8 | maxBits: 20, // this permits 1,048,575 shares, though going this high is NOT recommended in JS!
9 |
10 | bytesPerChar: 2,
11 | maxBytesPerChar: 6, // Math.pow(256,7) > Math.pow(2,53)
12 |
13 | // Primitive polynomials (in decimal form) for Galois Fields GF(2^n), for 2 <= n <= 30
14 | // The index of each term in the array corresponds to the n for that polynomial
15 | // i.e. to get the polynomial for n=16, use primitivePolynomials[16]
16 | primitivePolynomials: [null,null,1,3,3,5,3,3,29,17,9,5,83,27,43,3,45,9,39,39,9,5,3,33,27,9,71,39,9,5,83],
17 |
18 | // warning for insecure PRNG
19 | warning: 'WARNING:\nA secure random number generator was not found.\nUsing Math.random(), which is NOT cryptographically strong!'
20 | };
21 |
22 | // Protected settings object
23 | var config = {};
24 |
25 | /** @expose **/
26 | /*edit by F. Ruiz. This function is turned off since it does not get called
27 | exports.getConfig = function(){
28 | return {
29 | 'bits': config.bits,
30 | 'unsafePRNG': config.unsafePRNG
31 | };
32 | };
33 | */
34 |
35 | function init(bits){
36 | if(bits && (typeof bits !== 'number' || bits%1 !== 0 || bitsdefaults.maxBits)){
37 | throw new Error('Number of bits must be an integer between ' + defaults.minBits + ' and ' + defaults.maxBits + ', inclusive.')
38 | }
39 |
40 | config.radix = defaults.radix;
41 | config.bits = bits || defaults.bits;
42 | config.size = Math.pow(2, config.bits);
43 | config.max = config.size - 1;
44 |
45 | // Construct the exp and log tables for multiplication.
46 | var logs = [], exps = [], x = 1, primitive = defaults.primitivePolynomials[config.bits];
47 | for(var i=0; i= config.size){
52 | x ^= primitive;
53 | x &= config.max;
54 | }
55 | }
56 |
57 | config.logs = logs;
58 | config.exps = exps;
59 | };
60 |
61 | /** @expose **/
62 | exports.init = init;
63 |
64 | function isInited(){
65 | if(!config.bits || !config.size || !config.max || !config.logs || !config.exps || config.logs.length !== config.size || config.exps.length !== config.size){
66 | return false;
67 | }
68 | return true;
69 | };
70 |
71 | //Edit by F. Ruiz. The RNG code in this library is removed and the one in SJCL is used instead
72 |
73 | //about 100 lines removed here
74 |
75 | //End of F. Ruiz edits for this block. There are three more edits: 1. a RNG check that is removed. 2. built-in RNG call replaced by SJCL RNG call. 3. new version of padLeft function
76 |
77 |
78 | // Divides a `secret` number String str expressed in radix `inputRadix` (optional, default 16)
79 | // into `numShares` shares, each expressed in radix `outputRadix` (optional, default to `inputRadix`),
80 | // requiring `threshold` number of shares to reconstruct the secret.
81 | // Optionally, zero-pads the secret to a length that is a multiple of padLength before sharing.
82 | /** @expose **/
83 | exports.share = function(secret, numShares, threshold, padLength, withoutPrefix){
84 | if(!isInited()){
85 | this.init();
86 | }
87 | /* if(!isSetRNG()){ RNG check removed by F. Ruiz
88 | this.setRNG();
89 | }
90 | */
91 | padLength = padLength || 0;
92 |
93 | if(typeof secret !== 'string'){
94 | throw new Error('Secret must be a string.');
95 | }
96 | if(typeof numShares !== 'number' || numShares%1 !== 0 || numShares < 2){
97 | throw new Error('Number of shares must be an integer between 2 and 2^bits-1 (' + config.max + '), inclusive.')
98 | }
99 | if(numShares > config.max){
100 | var neededBits = Math.ceil(Math.log(numShares +1)/Math.LN2);
101 | throw new Error('Number of shares must be an integer between 2 and 2^bits-1 (' + config.max + '), inclusive. To create ' + numShares + ' shares, use at least ' + neededBits + ' bits.')
102 | }
103 | if(typeof threshold !== 'number' || threshold%1 !== 0 || threshold < 2){
104 | throw new Error('Threshold number of shares must be an integer between 2 and 2^bits-1 (' + config.max + '), inclusive.');
105 | }
106 | if(threshold > config.max){
107 | var neededBits = Math.ceil(Math.log(threshold +1)/Math.LN2);
108 | throw new Error('Threshold number of shares must be an integer between 2 and 2^bits-1 (' + config.max + '), inclusive. To use a threshold of ' + threshold + ', use at least ' + neededBits + ' bits.');
109 | }
110 | if(typeof padLength !== 'number' || padLength%1 !== 0 ){
111 | throw new Error('Zero-pad length must be an integer greater than 1.');
112 | }
113 |
114 | if(config.unsafePRNG){
115 | warn();
116 | }
117 |
118 | secret = '1' + hex2bin(secret); // append a 1 so that we can preserve the correct number of leading zeros in our secret
119 | secret = split(secret, padLength);
120 | var x = new Array(numShares), y = new Array(numShares);
121 | for(var i=0, len = secret.length; i exp(log(fx) + log(x)) + coeff[i],
169 | // so if fx===0, just set fx to coeff[i] because
170 | // using the exp/log form will result in incorrect value
171 | function horner(x, coeffs){
172 | var logx = config.logs[x];
173 | var fx = 0;
174 | for(var i=coeffs.length-1; i>=0; i--){
175 | if(fx === 0){
176 | fx = coeffs[i];
177 | continue;
178 | }
179 | fx = config.exps[ (logx + config.logs[fx]) % config.max ] ^ coeffs[i];
180 | }
181 | return fx;
182 | };
183 |
184 | function inArray(arr,val){
185 | for(var i = 0,len=arr.length; i < len; i++) {
186 | if(arr[i] === val){
187 | return true;
188 | }
189 | }
190 | return false;
191 | };
192 |
193 | function processShare(share){
194 |
195 | var bits = parseInt(share[0], 36);
196 | if(bits && (typeof bits !== 'number' || bits%1 !== 0 || bitsdefaults.maxBits)){
197 | throw new Error('Number of bits must be an integer between ' + defaults.minBits + ' and ' + defaults.maxBits + ', inclusive.')
198 | }
199 |
200 | var max = Math.pow(2, bits) - 1;
201 | var idLength = max.toString(config.radix).length;
202 |
203 | var id = parseInt(share.substr(1, idLength), config.radix);
204 | if(typeof id !== 'number' || id%1 !== 0 || id<1 || id>max){
205 | throw new Error('Share id must be an integer between 1 and ' + config.max + ', inclusive.');
206 | }
207 | share = share.substr(idLength + 1);
208 | if(!share.length){
209 | throw new Error('Invalid share: zero-length share.')
210 | }
211 | return {
212 | 'bits': bits,
213 | 'id': id,
214 | 'value': share
215 | };
216 | };
217 |
218 | /** @expose **/
219 | exports._processShare = processShare;
220 |
221 | // Protected method that evaluates the Lagrange interpolation
222 | // polynomial at x=`at` for individual config.bits-length
223 | // segments of each share in the `shares` Array.
224 | // Each share is expressed in base `inputRadix`. The output
225 | // is expressed in base `outputRadix'
226 | function combine(at, shares){
227 | var setBits, share, x = [], y = [], result = '', idx;
228 |
229 | for(var i=0, len = shares.length; imax){
283 | throw new Error('Share id must be an integer between 1 and ' + config.max + ', inclusive.');
284 | }
285 |
286 | var padding = max.toString(config.radix).length;
287 | return config.bits.toString(36).toUpperCase() + padLeft(id.toString(config.radix), padding) + combine(id, shares);
288 | };
289 |
290 | // Evaluate the Lagrange interpolation polynomial at x = `at`
291 | // using x and y Arrays that are of the same length, with
292 | // corresponding elements constituting points on the polynomial.
293 | function lagrange(at, x, y){
294 | var sum = 0,
295 | product,
296 | i, j;
297 |
298 | for(var i=0, len = x.length; iconfig.bits; i-=config.bits){
332 | parts.push(parseInt(str.slice(i-config.bits, i), 2));
333 | }
334 | parts.push(parseInt(str.slice(0, i), 2));
335 | return parts;
336 | };
337 |
338 | // Pads a string `str` with zeros on the left so that its length is a multiple of `bits`
339 | function padLeft(str, bits){
340 | bits = bits || config.bits
341 | //edit by F. Ruiz. This function didn't work well on iPad, so changed it with a different algorithm
342 | // var missing = str.length % bits;
343 | // return (missing ? new Array(bits - missing + 1).join('0') : '') + str;
344 | while(str.length % bits) str = '0' + str;
345 | return str;
346 | //end of F. Ruiz edit
347 | };
348 |
349 | function hex2bin(str){
350 | var bin = '', num;
351 | for(var i=str.length - 1; i>=0; i--){
352 | num = parseInt(str[i], 16)
353 | if(isNaN(num)){
354 | throw new Error('Invalid hex character.')
355 | }
356 | bin = padLeft(num.toString(2), 4) + bin;
357 | }
358 | return bin;
359 | }
360 |
361 | function bin2hex(str){
362 | var hex = '', num;
363 | str = padLeft(str, 4);
364 | for(var i=str.length; i>=4; i-=4){
365 | num = parseInt(str.slice(i-4, i), 2);
366 | if(isNaN(num)){
367 | throw new Error('Invalid binary character.')
368 | }
369 | hex = num.toString(16) + hex;
370 | }
371 | return hex;
372 | }
373 |
374 | // Converts a given UTF16 character string to the HEX representation.
375 | // Each character of the input string is represented by
376 | // `bytesPerChar` bytes in the output string.
377 | /** @expose **/
378 | exports.str2hex = function(str, bytesPerChar){
379 | if(typeof str !== 'string'){
380 | throw new Error('Input must be a character string.');
381 | }
382 | bytesPerChar = bytesPerChar || defaults.bytesPerChar;
383 |
384 | if(typeof bytesPerChar !== 'number' || bytesPerChar%1 !== 0 || bytesPerChar<1 || bytesPerChar > defaults.maxBytesPerChar){
385 | throw new Error('Bytes per character must be an integer between 1 and ' + defaults.maxBytesPerChar + ', inclusive.')
386 | }
387 |
388 | var hexChars = 2*bytesPerChar;
389 | var max = Math.pow(16, hexChars) - 1;
390 | var out = '', num;
391 | for(var i=0, len=str.length; i max){
396 | var neededBytes = Math.ceil(Math.log(num+1)/Math.log(256));
397 | throw new Error('Invalid character code (' + num +'). Maximum allowable is 256^bytes-1 (' + max + '). To convert this character, use at least ' + neededBytes + ' bytes.')
398 | }else{
399 | out = padLeft(num.toString(16), hexChars) + out;
400 | }
401 | }
402 | return out;
403 | };
404 |
405 | // Converts a given HEX number string to a UTF16 character string.
406 | /** @expose **/
407 | exports.hex2str = function(str, bytesPerChar){
408 | if(typeof str !== 'string'){
409 | throw new Error('Input must be a hexadecimal string.');
410 | }
411 | bytesPerChar = bytesPerChar || defaults.bytesPerChar;
412 |
413 | if(typeof bytesPerChar !== 'number' || bytesPerChar%1 !== 0 || bytesPerChar<1 || bytesPerChar > defaults.maxBytesPerChar){
414 | throw new Error('Bytes per character must be an integer between 1 and ' + defaults.maxBytesPerChar + ', inclusive.')
415 | }
416 |
417 | var hexChars = 2*bytesPerChar;
418 | var out = '';
419 | str = padLeft(str, hexChars);
420 | for(var i=0, len = str.length; i
396 | #mainMsg {
397 | min-height: 20px;
398 | }
399 | #lockList {
400 | padding: 4px;
401 | width: 100%;
402 | }
403 | #resetListBtn {
404 | font-size: 14px;
405 | padding: 4px;
406 | }
407 | #main2lockBtn {
408 | font-size: 14px;
409 | padding: 4px;
410 | padding-left: 10px;
411 | padding-right: 12px;
412 | }
413 | #basicBtnsTop {
414 | display: block;
415 | }
416 | #decryptBtnBasic {
417 | font-size: 22px;
418 | padding: 14px;
419 | }
420 | #showLockBtnBasic {
421 | font-size: 22px;
422 | padding: 14px;
423 | }
424 | #emailBtnsTop {
425 | display: none;
426 | }
427 | #decryptBtnEmail {
428 | padding-right: 7px;
429 | padding-left: 7px;
430 | }
431 | #stegoBtnEmail {
432 | padding-right: 6px;
433 | padding-left: 6px;
434 | }
435 | #imageFileEmailBtn {
436 | padding-right: 6px;
437 | padding-left: 6px;
438 | padding-bottom: 13px;
439 | }
440 | #imageFileEmail {
441 | display: none;
442 | }
443 | #mainBtnsTop {
444 | display: none;
445 | }
446 | #decryptBtn {
447 | padding-right: 7px;
448 | padding-left: 7px;
449 | }
450 | #verifyBtn {
451 | padding-right: 7px;
452 | padding-left: 7px;
453 | }
454 | #showLockBtn {
455 | padding-right: 7px;
456 | padding-left: 7px;
457 | }
458 | #main2extraBtn {
459 | color: orange;
460 | padding-right: 9px;
461 | padding-left: 9px;
462 | }
463 | #extraButtonsTop {
464 | display: none;
465 | }
466 | #stegoBtn {
467 | padding-right: 9px;
468 | padding-left: 9px;
469 | }
470 | #imageFileBtn {
471 | padding-bottom: 13px;
472 | }
473 | #imageFile {
474 | display: none;
475 | }
476 | #secretShareBtn {
477 | padding-right: 9px;
478 | padding-left: 9px;
479 | }
480 | #extra2mainBtn {
481 | color: orange;
482 | padding-right: 9px;
483 | padding-left: 9px;
484 | }
485 | #imgFile {
486 | display: none;
487 | }
488 | #mainFile {
489 | display: none;
490 | }
491 |
492 | #optionsTab {
493 | max-width: 500px;
494 | }
495 | #customColors {
496 | display: block;
497 | }
498 | #colorPicker {
499 | width: 50px;
500 | padding: 12px;
501 | }
502 | #rndColors {
503 | font-size: medium;
504 | padding: 9px;
505 | }
506 | #specialEncryptModes {
507 | display: none;
508 | }
509 | #syncCheck {
510 | display: none;
511 | }
512 | #basicHideModes {
513 | display: none;
514 | }
515 | #advancedModes {
516 | display: none;
517 | }
518 | #advancedBtns {
519 | display: none;
520 | }
521 |
522 | #advancedHelp {
523 | display: none;
524 | }
525 | #canary {
526 | float: left;
527 | }
528 |
529 | #lockScr {
530 | display: none;
531 | }
532 | #lock2mainBtn {
533 | float: left;
534 | }
535 | #lockMsg {
536 | height: 25px;
537 | }
538 | #lockNameBox {
539 | width: 95%;
540 | }
541 | #lockBox {
542 | width: 95%;
543 | }
544 | #lockBtnsBottom {
545 | display: none;
546 | }
547 | #lockFile{
548 | display: none;
549 | }
550 |
551 | #keyScr {
552 | display: block;
553 | }
554 | #pwdMsg {
555 | height: 40px;
556 | }
557 | #javascriptOff {
558 | color: red;
559 | font-weight: bold;
560 | }
561 | #nameList {
562 | padding: 4px;
563 | width: 30%;
564 | }
565 | #newUserBtn {
566 | font-size: 14px;
567 | padding: 4px;
568 | }
569 | #pwdBox {
570 | width: 95%;
571 | }
572 |
573 | #introscr {
574 | display: none;
575 | }
576 | #introWelcome {
577 | color: green;
578 | }
579 | #intro1 {
580 | width: 98%;
581 | max-width: 500px;
582 | }
583 | #skipIntroBtn {
584 | float: right;
585 | }
586 | #introscr2 {
587 | display: none;
588 | }
589 | #intro2 {
590 | width: 98%;
591 | max-width: 500px;
592 | }
593 | #nameIntro {
594 | width: 90%;
595 | }
596 | #pwdIntroMsg {
597 | color: green;
598 | }
599 | #introscr3 {
600 | display: none;
601 | }
602 | #intro3 {
603 | width: 98%;
604 | max-width: 500px;
605 | }
606 | #pwdIntroBox {
607 | width: 90%;
608 | }
609 | #introscr4 {
610 | display: none;
611 | }
612 | #intro4 {
613 | width: 98%;
614 | max-width: 500px;
615 | }
616 | #emailIntro {
617 | width: 90%;
618 | }
619 | #introscr5 {
620 | display: none;
621 | }
622 | #intro5 {
623 | width: 98%;
624 | max-width: 500px;
625 | }
626 | #showlockIntroBtn {
627 | color: blue;
628 | }
629 | #intromsg2 {
630 | color: red;
631 | }
632 |
633 | #nameScr {
634 | display: none;
635 | }
636 | #namechangemsg {
637 | height: 30px;
638 | }
639 | #userNameBox {
640 | width: 95%;
641 | }
642 | #newKeyMsg {
643 | height: 30px;
644 | }
645 | #newKeyBox {
646 | width: 95%;
647 | }
648 | #newKey2Box {
649 | width: 95%;
650 | }
651 | #emailScr {
652 | display: none;
653 | }
654 | #emailBox {
655 | width: 95%;
656 | }
657 |
658 | #decoyText {
659 | width: 95%;
660 | }
661 | #decoyInBox {
662 | width: 95%;
663 | }
664 | #decoyOutBox {
665 | width: 95%;
666 | }
667 |
668 | #partsNumber {
669 | width: 30%;
670 | }
671 | #partsQuorum {
672 | width: 30%;
673 | }
674 |
675 | #chatDate {
676 | width: 95%;
677 | }
678 |
679 | #imageScr {
680 | display: none;
681 | }
682 | #image2mainBtn {
683 | float: left;
684 | }
685 | #imageBox {
686 | width: 25%;
687 | padding: 5px;
688 | }
689 | #imageMsg {
690 | height: 40px;
691 | }
692 |
693 | #lockdirContent {
694 | max-width: 1200px;
695 | margin-left: auto;
696 | margin-right: auto;
697 | }
698 |
699 | #qrcodeImg {
700 | top: 100px;
701 | }
702 |
--------------------------------------------------------------------------------