Compare commits

..

1 Commits

Author SHA1 Message Date
kev
8f7bd620b1 Update readme to reflect whitelist ability 2021-09-28 05:32:18 +00:00
8 changed files with 8 additions and 215 deletions

View File

@ -12,7 +12,7 @@ There are spy libraries that can determine how long keys are held down and the t
This addon was created to skew the key press timing by limiting the speed of presses. The speed is changed randomly on each page load, with a min of 150ms and a max of 300ms. This addon was created to skew the key press timing by limiting the speed of presses. The speed is changed randomly on each page load, with a min of 150ms and a max of 300ms.
It uses a less than ideal thread locking solution, as simply buffering text does not stop event listeners from spies. **Now it also has a non-default setting to use prompt() on non-password single line input elements. This reduces CPU usage and increase typing speed while offering better protection than the delay approach.** It uses a less than ideal thread locking solution, as simply buffering text does not stop event listeners from spies.
You can whitelist domains that you trust on the addon settings page, but it was decided not to support changing the key speed manually as that would add another fingerprinting avenue. You can whitelist domains that you trust on the addon settings page, but it was decided not to support changing the key speed manually as that would add another fingerprinting avenue.
@ -29,7 +29,6 @@ Development Roadmap:
* **This addon may not defeat all types of keyboard biometric surveillance**, however it was tested against the Keytrac and TypingDNA demos and it worked well. * **This addon may not defeat all types of keyboard biometric surveillance**, however it was tested against the Keytrac and TypingDNA demos and it worked well.
* **This addon does not yet deal with stylometry** * **This addon does not yet deal with stylometry**
* Some websites override all key events in which case it is not (yet) possible to protect against keyboarding analysis there
* You may see higher CPU usage while typing. This is due do the unideal locking solution described above * You may see higher CPU usage while typing. This is due do the unideal locking solution described above
* Do not confuse this with spying keyboard apps on mobile devices, this cannot address that. * Do not confuse this with spying keyboard apps on mobile devices, this cannot address that.
* Not tested on Firefox Android * Not tested on Firefox Android
@ -37,9 +36,9 @@ Development Roadmap:
Sources: Sources:
www dot keytrac dot net/en/ https://www.keytrac.net/en/
https://www.typingdna.com/
www dot typingdna dot com/
https://www.whonix.org/wiki/Stylometry https://www.whonix.org/wiki/Stylometry
[https://www.whonix.org/wiki/Keystroke_Deanonymization](https://www.whonix.org/wiki/Keystroke_Deanonymization) [https://www.whonix.org/wiki/Keystroke_Deanonymization](https://www.whonix.org/wiki/Keystroke_Deanonymization)
(Spy companies not linked to avoid helping their SEO)

View File

@ -16,70 +16,13 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
let lastActive = null
const defaultHosts = "<all_urls>"; const defaultHosts = "<all_urls>";
let appCode = function (){ let appCode = function (){
let lastActive = document.createElement("p")
let popupEnabled = false
let popupGetter = browser.storage.sync.get("keyboardprivacyprompt")
popupGetter.then(function(val){
popupEnabled = val['keyboardprivacyprompt']
console.debug(popupEnabled)
}, function(){})
let clearSelect = function()
{
if (window.getSelection) {window.getSelection().removeAllRanges()}
else if
(document.selection) {
document.selection.empty()
}
}
let minValue = 75 let minValue = 75
let maxValue = 150 let maxValue = 150
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)
}
else if (! ["text", "search", "email", "number"].includes(inputType.toLowerCase())){
return
}
else{
text = prompt("[PrivateKeyboard]\n\nEnter text for the " + inputType + " field:", e.target.value)
}
if (text !== null) {
e.target.value = text
}
last = e.target
clearSelect()
time = Date.now()
}
}
let popupMode = function(){
if (popupEnabled){
document.addEventListener('focusin', doPopup, true)
}
}
let mainKeyboardPrivacy = function(){ let mainKeyboardPrivacy = function(){
popupMode()
function getRandNum(){ function getRandNum(){
let buf = new Uint8Array(1) let buf = new Uint8Array(1)
@ -120,24 +63,9 @@ let appCode = function (){
}, ) }, )
document.addEventListener('keydown', function(e){ document.addEventListener('keydown', function(e){
if (e.key.startsWith('Arrow') || e.key.startsWith('Page') || if (e.key.startsWith('Arrow') || e.key.startsWith('Page')){
e.key == "Enter" || e.key == "Control" || e.key == "Tab"
){
return true; return true;
} }
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{
}
if (popupEnabled){
setTimeout(
function(){doPopup(e)}, 10)
}
pausecomp(down); pausecomp(down);
return true; return true;
}) })
@ -198,38 +126,6 @@ let appCode = function (){
let whitelist = browser.storage.sync.get("keyboardprivacywhitelist"); let whitelist = browser.storage.sync.get("keyboardprivacywhitelist");
whitelist.then(shouldRunKeyboardPrivacy, noKeyboardPrivacySettings) whitelist.then(shouldRunKeyboardPrivacy, noKeyboardPrivacySettings)
document.addEventListener('focus', function(e){
let active = document.activeElement;
if (active.tagName == "INPUT" || active.tagName == "TEXTAREA") {
lastActive = active
}
})
browser.runtime.onMessage.addListener(request => {
let active = document.activeElement
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});
}
if (active.tagName != "INPUT" && active.tagName != "TEXTAREA"){
lastActive.value = request.keys
}
else{
document.activeElement.value = request.keys
lastActive = document.activeElement
}
return Promise.resolve({response: "ack"});
});
} }
const dummyStr = '' const dummyStr = ''

View File

@ -2,7 +2,7 @@
"manifest_version": 2, "manifest_version": 2,
"name": "Private Keyboard", "name": "Private Keyboard",
"version": "2.0", "version": "1.8",
"description": "Protect against keyboard biometrics", "description": "Protect against keyboard biometrics",
@ -32,15 +32,6 @@
"default_popup": "settings/button.html" "default_popup": "settings/button.html"
}, },
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Ctrl+1"
}
}
},
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {

View File

@ -5,14 +5,10 @@
<title>Private Keyboard</title> <title>Private Keyboard</title>
<link rel="stylesheet" href="./button.css"> <link rel="stylesheet" href="./button.css">
<script src="./button.js" defer></script> <script src="./button.js" defer></script>
<script src="./keybuffer2.js" defer></script>
</head> </head>
<body> <body>
<header id="siteDomain"></header> <header id="siteDomain"></header>
<button id="toggleSite">Disable Keyboard Privacy</button> <button id="toggleSite">Disable Keyboard Privacy</button>
<p id="reloadPage">Refresh the page to apply</p> <p id="reloadPage">Refresh the page to apply</p>
<br>
<textarea tabindex="0" id="keyBuffer" cols="20" rows="10" placeholder="Text typed here will be sent to active input/textarea elements. Breaks on many sites, but is faster than typing directly on the page"></textarea>
</body> </body>
</html> </html>

View File

@ -1,76 +0,0 @@
let started = false
function onError(error) {
console.error(`Error: ${error}`);
}
function doSendMsg(msg, tabs) {
for (let tab of tabs) {
browser.tabs.sendMessage(
tab.id,
{keys: msg, getCurrent: false}
).then(response => {
console.log("Message from the content script:");
console.log(response.response);
}).catch(onError);
}
}
let sender = async function(e){
if (e.key == 'Tab') {
e.preventDefault();
let start = this.selectionStart;
let end = this.selectionEnd;
// set textarea value to: text before caret + tab + text after caret
this.value = this.value.substring(0, start) +
"\t" + this.value.substring(end);
// put caret at right position again
this.selectionStart =
this.selectionEnd = start + 1;
}
let sendMessageToTabs = function(tabs){
doSendMsg(document.getElementById('keyBuffer').value, tabs)
}
browser.tabs.query({
currentWindow: true,
active: true
}).then(sendMessageToTabs).catch(onError);
}
function getCurrent(){
browser.tabs.query({
currentWindow: true,
active: true
}).then(function(tabs){
for (let tab of tabs) {
browser.tabs.sendMessage(
tab.id,
{keys: "", getCurrent: true}
).then(response => {
console.debug(response)
if (response.response === undefined || response.response === false){
//document.getElementById('keyBuffer').value = "No element in focus. Focus an element and reopen this."
return
}
document.getElementById('keyBuffer').value = response.response
}).catch(onError);
}
}).catch(onError);
}
getCurrent()
document.getElementById("keyBuffer").focus()
document.getElementById('keyBuffer').onkeydown = sender
document.getElementById('keyBuffer').onpaste = sender

View File

@ -9,9 +9,6 @@
<body> <body>
<form> <form>
<input type="checkbox" id="usePrompt">
<label for="usePrompt">Use prompt() dialogs on single-line inputs. Faster typing and less CPU usage, but can break some websites and features like autocomplete.</label>
<br>
<input type="checkbox" id="whitelistLAN"> <input type="checkbox" id="whitelistLAN">
<label for="whitelistLAN">Whitelist private IP + localhost range hostnames (e.g. routers)</label> <label for="whitelistLAN">Whitelist private IP + localhost range hostnames (e.g. routers)</label>
<h1>Trusted domains (comma delimited)</h1> <h1>Trusted domains (comma delimited)</h1>

View File

@ -24,9 +24,6 @@ function saveOptions(e) {
browser.storage.sync.set({ browser.storage.sync.set({
keyboardprivacylan: document.querySelector("#whitelistLAN").checked keyboardprivacylan: document.querySelector("#whitelistLAN").checked
}) })
browser.storage.sync.set({
keyboardprivacyprompt: document.querySelector("#usePrompt").checked
})
document.getElementById('saved').innerHTML = '<br><b>Saved</b>' document.getElementById('saved').innerHTML = '<br><b>Saved</b>'
setTimeout(function(){ setTimeout(function(){
document.getElementById('saved').innerHTML = '<br>' document.getElementById('saved').innerHTML = '<br>'
@ -45,10 +42,6 @@ function saveOptions(e) {
document.querySelector("#whitelistLAN").checked = result['keyboardprivacylan'] document.querySelector("#whitelistLAN").checked = result['keyboardprivacylan']
} }
function setCurrentPrompt(result){
document.querySelector("#usePrompt").checked = result['keyboardprivacyprompt']
}
function onError(error) { function onError(error) {
console.log(`Error: ${error}`); console.log(`Error: ${error}`);
} }
@ -58,9 +51,6 @@ function saveOptions(e) {
let gettingLAN = browser.storage.sync.get("keyboardprivacylan"); let gettingLAN = browser.storage.sync.get("keyboardprivacylan");
gettingLAN.then(setCurrentLAN, onError); gettingLAN.then(setCurrentLAN, onError);
let gettingPrompt = browser.storage.sync.get("keyboardprivacyprompt");
gettingPrompt.then(setCurrentPrompt, onError);
} }
document.addEventListener("DOMContentLoaded", restoreOptions); document.addEventListener("DOMContentLoaded", restoreOptions);