2021-08-08 19:49:22 +00:00
|
|
|
/*
|
|
|
|
Private Keyboard
|
|
|
|
Copyright (C) 2021 Kevin Froman VoidNetwork LLC
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
*/
|
2022-02-19 18:34:08 +00:00
|
|
|
let lastActive = null
|
2021-08-08 19:49:22 +00:00
|
|
|
const defaultHosts = "<all_urls>";
|
|
|
|
|
|
|
|
let appCode = function (){
|
2022-02-20 06:50:15 +00:00
|
|
|
let lastActive = document.createElement("p")
|
2021-10-12 18:36:09 +00:00
|
|
|
let popupEnabled = false
|
|
|
|
let popupGetter = browser.storage.sync.get("keyboardprivacyprompt")
|
|
|
|
popupGetter.then(function(val){
|
|
|
|
popupEnabled = val['keyboardprivacyprompt']
|
|
|
|
console.debug(popupEnabled)
|
|
|
|
}, function(){})
|
|
|
|
|
2021-10-12 07:34:19 +00:00
|
|
|
let clearSelect = function()
|
|
|
|
{
|
|
|
|
if (window.getSelection) {window.getSelection().removeAllRanges()}
|
|
|
|
else if
|
|
|
|
(document.selection) {
|
|
|
|
document.selection.empty()
|
|
|
|
}
|
|
|
|
}
|
2021-08-10 05:57:27 +00:00
|
|
|
let minValue = 75
|
|
|
|
let maxValue = 150
|
2021-10-11 23:56:11 +00:00
|
|
|
let time = 0
|
|
|
|
let last = null
|
|
|
|
|
|
|
|
let doPopup = function(e) {
|
|
|
|
let text = ""
|
|
|
|
let inputType = ""
|
|
|
|
|
|
|
|
if (e.target.tagName === "INPUT"){
|
|
|
|
inputType = e.target.getAttribute("type")
|
|
|
|
|
|
|
|
if (Date.now() - time < 1000 && e.target === last){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (! inputType){
|
|
|
|
text = prompt("[PrivateKeyboard]\n\nEnter text for the text field:", e.target.value)
|
|
|
|
}
|
2021-10-12 18:36:09 +00:00
|
|
|
else if (! ["text", "search", "email", "number"].includes(inputType.toLowerCase())){
|
2021-10-11 23:56:11 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
text = prompt("[PrivateKeyboard]\n\nEnter text for the " + inputType + " field:", e.target.value)
|
|
|
|
}
|
2021-10-12 07:34:19 +00:00
|
|
|
if (text !== null) {
|
|
|
|
e.target.value = text
|
|
|
|
}
|
2021-10-11 23:56:11 +00:00
|
|
|
last = e.target
|
2021-10-12 07:34:19 +00:00
|
|
|
clearSelect()
|
2021-10-11 23:56:11 +00:00
|
|
|
time = Date.now()
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
let popupMode = function(){
|
|
|
|
|
2021-10-12 18:36:09 +00:00
|
|
|
if (popupEnabled){
|
|
|
|
document.addEventListener('focusin', doPopup, true)
|
|
|
|
}
|
2021-10-11 23:56:11 +00:00
|
|
|
|
|
|
|
}
|
2021-08-08 19:49:22 +00:00
|
|
|
|
|
|
|
let mainKeyboardPrivacy = function(){
|
2021-10-11 23:56:11 +00:00
|
|
|
popupMode()
|
2021-08-08 19:49:22 +00:00
|
|
|
|
|
|
|
function getRandNum(){
|
|
|
|
let buf = new Uint8Array(1)
|
|
|
|
while (true){
|
|
|
|
window.crypto.getRandomValues(buf);
|
|
|
|
|
2021-08-10 05:02:49 +00:00
|
|
|
if (buf[0] <= maxValue && buf[0] >= minValue){
|
|
|
|
break;
|
2021-08-08 19:49:22 +00:00
|
|
|
}
|
|
|
|
buf = new Uint8Array(1);
|
|
|
|
}
|
|
|
|
return buf[0];
|
|
|
|
}
|
2021-08-10 05:57:27 +00:00
|
|
|
let up = getRandNum()
|
|
|
|
let down = getRandNum()
|
2021-08-08 19:49:22 +00:00
|
|
|
|
|
|
|
console.debug('Protecting keyboard biometrics on ' + document.location.href)
|
|
|
|
|
|
|
|
function pausecomp(millis)
|
|
|
|
{
|
|
|
|
// Yes i know this wastes cpu. i don't like it either, but it seems a blocking
|
|
|
|
// approach is needed to prevent spying event listeners from reading key events in *real time*
|
|
|
|
// Might use an off-page buffer solution in the future
|
|
|
|
var date = new Date();
|
|
|
|
var curDate = null;
|
|
|
|
do { curDate = new Date(); }
|
|
|
|
while(curDate-date < millis);
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout(function(){
|
|
|
|
|
|
|
|
document.addEventListener('keyup', function(e){
|
2021-08-09 02:46:55 +00:00
|
|
|
if (e.key.startsWith('Arrow') || e.key.startsWith('Page')){
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-10 05:57:27 +00:00
|
|
|
pausecomp(up);
|
2021-08-08 19:49:22 +00:00
|
|
|
return true;
|
|
|
|
}, )
|
|
|
|
|
|
|
|
document.addEventListener('keydown', function(e){
|
2021-10-11 23:56:11 +00:00
|
|
|
if (e.key.startsWith('Arrow') || e.key.startsWith('Page') ||
|
|
|
|
|
|
|
|
e.key == "Enter" || e.key == "Control" || e.key == "Tab"
|
|
|
|
){
|
2021-08-09 02:46:55 +00:00
|
|
|
return true;
|
|
|
|
}
|
2021-10-11 23:56:11 +00:00
|
|
|
else if (e.key.length > 1 && e.key.startsWith("F")){
|
|
|
|
// Ignore function keys (unicode can also be >1 length so check for f)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
}
|
|
|
|
|
2021-10-12 18:36:09 +00:00
|
|
|
if (popupEnabled){
|
|
|
|
setTimeout(
|
|
|
|
function(){doPopup(e)}, 10)
|
|
|
|
}
|
2021-10-11 23:56:11 +00:00
|
|
|
|
2021-09-21 04:47:40 +00:00
|
|
|
pausecomp(down);
|
2021-08-08 19:49:22 +00:00
|
|
|
return true;
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
}, 100)
|
|
|
|
}
|
2021-09-21 04:47:40 +00:00
|
|
|
function checkForLANThenRun(){
|
|
|
|
|
|
|
|
let lan = browser.storage.sync.get("keyboardprivacylan");
|
|
|
|
lan.then(function(val){
|
|
|
|
|
|
|
|
if (! val['keyboardprivacylan']){
|
|
|
|
mainKeyboardPrivacy()
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
let hostname = document.location.hostname
|
|
|
|
if (/^(10)\.(.*)\.(.*)\.(.*)$/.test(hostname)){
|
|
|
|
//10.x.x.x
|
|
|
|
}else if (/^(172)\.(1[6-9]|2[0-9]|3[0-1])\.(.*)\.(.*)$/.test(hostname)){
|
|
|
|
//172.16.x.x - 172.31.255.255
|
|
|
|
}else if (/^(192)\.(168)\.(.*)\.(.*)$/.test(hostname)){
|
|
|
|
//192.168.x.x
|
|
|
|
}else if (/^(127)\.(.*)\.(.*)\.(.*)$/.test(hostname)){
|
|
|
|
}
|
|
|
|
else if (hostname == '[::1]'){
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mainKeyboardPrivacy()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
console.debug("Not running private keyboard because lan/loopback hostname")
|
|
|
|
}
|
|
|
|
|
|
|
|
}, function(val){mainKeyboardPrivacy()})
|
|
|
|
|
|
|
|
}
|
2021-08-08 19:49:22 +00:00
|
|
|
function shouldRunKeyboardPrivacy(value){
|
2021-08-10 05:57:27 +00:00
|
|
|
if (typeof value.keyboardprivacywhitelist === 'undefined'){
|
2021-08-09 00:06:39 +00:00
|
|
|
mainKeyboardPrivacy()
|
|
|
|
return
|
|
|
|
}
|
2021-08-08 19:49:22 +00:00
|
|
|
let vals = value.keyboardprivacywhitelist.split(',')
|
|
|
|
for (i = 0; i < vals.length; i++){
|
2021-08-10 05:02:49 +00:00
|
|
|
if (vals[i] === document.location.hostname || 'www.' + vals[i] === document.location.hostname){
|
2021-08-08 19:49:22 +00:00
|
|
|
console.debug(document.location.hostname + ' whitelisted for no keyboard privacy')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2021-09-21 04:47:40 +00:00
|
|
|
checkForLANThenRun()
|
|
|
|
|
2021-08-08 19:49:22 +00:00
|
|
|
}
|
2021-08-10 05:57:27 +00:00
|
|
|
function noKeyboardPrivacySettings(value){
|
2021-08-08 19:49:22 +00:00
|
|
|
mainKeyboardPrivacy()
|
|
|
|
}
|
|
|
|
|
|
|
|
let whitelist = browser.storage.sync.get("keyboardprivacywhitelist");
|
|
|
|
whitelist.then(shouldRunKeyboardPrivacy, noKeyboardPrivacySettings)
|
|
|
|
|
2022-02-19 07:04:13 +00:00
|
|
|
|
2022-02-20 06:50:15 +00:00
|
|
|
document.addEventListener('focus', function(e){
|
|
|
|
let active = document.activeElement;
|
|
|
|
if (active.tagName == "INPUT" || active.tagName == "TEXTAREA") {
|
|
|
|
lastActive = active
|
2022-02-19 18:34:08 +00:00
|
|
|
}
|
2022-02-20 06:50:15 +00:00
|
|
|
})
|
2022-02-19 18:34:08 +00:00
|
|
|
|
2022-02-20 06:50:15 +00:00
|
|
|
browser.runtime.onMessage.addListener(request => {
|
|
|
|
let active = document.activeElement
|
2022-02-19 18:34:08 +00:00
|
|
|
|
2022-02-20 06:50:15 +00:00
|
|
|
if (request.getCurrent){
|
|
|
|
if (active.tagName != "INPUT" && active.tagName != "TEXTAREA") {
|
|
|
|
if (lastActive.tagName != "INPUT" && lastActive.tagName != "TEXTAREA"){
|
|
|
|
console.debug("no current active or last active")
|
|
|
|
return Promise.resolve({response: false});
|
|
|
|
}
|
|
|
|
return Promise.resolve({response: lastActive.value});
|
|
|
|
}
|
|
|
|
return Promise.resolve({response: active.value});
|
2022-02-20 00:45:20 +00:00
|
|
|
}
|
2022-02-20 06:50:15 +00:00
|
|
|
if (active.tagName != "INPUT" && active.tagName != "TEXTAREA"){
|
|
|
|
|
|
|
|
lastActive.value = request.keys
|
2022-02-19 18:34:08 +00:00
|
|
|
}
|
2022-02-19 07:04:13 +00:00
|
|
|
else{
|
2022-02-20 06:50:15 +00:00
|
|
|
document.activeElement.value = request.keys
|
|
|
|
lastActive = document.activeElement
|
2022-02-19 18:34:08 +00:00
|
|
|
}
|
2022-02-19 07:04:13 +00:00
|
|
|
return Promise.resolve({response: "ack"});
|
|
|
|
});
|
|
|
|
|
2021-08-08 19:49:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const dummyStr = ''
|
|
|
|
|
|
|
|
let defaultCode = dummyStr + appCode;
|
|
|
|
defaultCode = defaultCode.replace('function (){', 'function keyboardPrivacy(){')
|
|
|
|
|
|
|
|
async function register(hosts, code) {
|
|
|
|
|
|
|
|
return await browser.contentScripts.register({
|
|
|
|
matches: [hosts],
|
|
|
|
js: [{code}],
|
|
|
|
runAt: "document_idle",
|
|
|
|
"allFrames": true
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
var registered = register(defaultHosts, defaultCode + ' keyboardPrivacy()');
|