Skip to content

Commit 7706c48

Browse files
committed
keytap3-gui : new stable version of the GUI
1 parent df87502 commit 7706c48

File tree

7 files changed

+401
-94
lines changed

7 files changed

+401
-94
lines changed

CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,20 @@ if (EMSCRIPTEN)
230230

231231
configure_file(${CMAKE_SOURCE_DIR}/index-${TARGET}-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/index.html @ONLY)
232232
configure_file(${CMAKE_SOURCE_DIR}/style.css ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/style.css @ONLY)
233+
234+
# keytap3-gui
235+
set(TARGET keytap3-gui)
236+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${TARGET})
237+
238+
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \
239+
-s TOTAL_MEMORY=536870912 \
240+
-s FORCE_FILESYSTEM=1 \
241+
-s LZ4=1 \
242+
--preload-file ${PROJECT_SOURCE_DIR}/data/ggwords-6-gram.dat.binary@/data/ \
243+
")
244+
245+
configure_file(${CMAKE_SOURCE_DIR}/index-${TARGET}-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/index.html @ONLY)
246+
configure_file(${CMAKE_SOURCE_DIR}/style.css ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/style.css @ONLY)
233247
endif()
234248

235249
if (NOT EMSCRIPTEN)

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ A more detailed description of the tool is available here: [Keytap2 discussion](
2929
[CTF: can you guess the text being typed?](https://ggerganov.github.io/keytap-challenge/)
3030

3131
[Try it online:](https://keytap2.ggerganov.com)
32-
32+
3333
<a href="https://keytap2.ggerganov.com" target="_blank"><img src="https://i.imgur.com/nPlLEDN.jpg" style="display: inline-block; overflow: hidden; width: 99%;"></img></a>
3434

3535
### Keytap3
@@ -39,6 +39,8 @@ automated and does not require any manual intervation during the text recovery p
3939

4040
[Video: short demo of using Keytap3](https://youtu.be/5aphvxpSt3o)
4141

42+
[GUI for Keytap3](https://keytap3.ggerganov.com)
43+
4244
[Check if your keyboard is vulnerable to Keytap:](https://keytap3.ggerganov.com)
4345

4446
<a href="https://keytap3.ggerganov.com" target="_blank"><img src="https://user-images.githubusercontent.com/1991296/166096331-ab26f7f8-08e0-48d6-abd7-57017ebf1866.JPEG" style="display: inline-block; overflow: hidden; width: 99%;"></img></a>
@@ -99,6 +101,7 @@ Short summary of the available tools. If the status of the tool is not **stable*
99101
| **keytap-gui** | gui | **stable** |
100102
| **keytap2-gui** | gui | **stable** |
101103
| **keytap3** | text | **stable** |
104+
| **keytap3-gui** | gui | **stable** |
102105
| - | *extra* | - |
103106
| **guess-qp** | text | experiment |
104107
| **guess-qp2** | text | experiment |
@@ -107,7 +110,6 @@ Short summary of the available tools. If the status of the tool is not **stable*
107110
| **subreak** | text | experiment |
108111
| **key-average-gui** | gui | experiment |
109112
| **keytap2** | text | experiment |
110-
| **keytap3-gui** | gui | experiment |
111113

112114
## Tool details
113115

@@ -157,6 +159,8 @@ Short summary of the available tools. If the status of the tool is not **stable*
157159

158160
./keytap-gui input0.kbd [input1.kbd] [input2.kbd] ... [-cN] [-CN]
159161

162+
Online demo: https://keytap.ggerganov.com
163+
160164
---
161165

162166
* **keytap2-gui** record.kbd n-gram-dir [-pN] [-cN] [-CN]
@@ -165,6 +169,8 @@ Short summary of the available tools. If the status of the tool is not **stable*
165169

166170
./keytap2-gui record.kbd ../data
167171

172+
Online demo: https://keytap2.ggerganov.com
173+
168174
---
169175

170176
* **keytap3**
@@ -177,6 +183,16 @@ Short summary of the available tools. If the status of the tool is not **stable*
177183

178184
---
179185

186+
* **keytap3-gui**
187+
188+
GUI version of the **keytap3** tool.
189+
190+
./keytap3-gui input.kbd ../data [-cN] [-CN] [-pF] [-tF] [-FN] [-fN]
191+
192+
Online demo: https://keytap3-gui.ggerganov.com
193+
194+
---
195+
180196
* **view-full-gui**
181197

182198
Visualize waveforms recorded with the **record-full** tool. Can also playback the audio data.

index-keytap2-gui-tmpl.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,6 @@ <h2>Keytap2 - acoustic keyboard eavesdropping based on language n-gram frequenci
269269

270270
</script>
271271

272-
<script async type="text/javascript" src="keytap2-gui.js"></script>
272+
<script async type="text/javascript" src="@TARGET@.js"></script>
273273
</body>
274274
</html>

index-keytap3-gui-tmpl.html

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
<!doctype html>
2+
<html lang="en-us">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Keytap3</title>
6+
7+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
8+
9+
<meta name="twitter:card" content="summary">
10+
<meta name="twitter:title" content="Keytap3" />
11+
<meta name="twitter:description" content="Acoustic keyboard eavesdropping based on language n-gram frequencies" />
12+
<meta name="twitter:image" content="https://keytap3.ggerganov.com/keytap3.png" />
13+
14+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
15+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
16+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
17+
<link rel="manifest" href="/site.webmanifest">
18+
19+
<meta name="msapplication-TileColor" content="#ffffff">
20+
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
21+
<meta name="theme-color" content="#ffffff">
22+
23+
<link rel="stylesheet" href="style.css">
24+
</head>
25+
<body>
26+
<div id="main-controls">
27+
<div id="description">
28+
<h2>Keytap3 - acoustic keyboard eavesdropping</h2>
29+
30+
<span class="text-body">
31+
This is a GUI for <a class="nav-link2" href="https://keytap3.ggerganov.com">Keytap3</a><br>
32+
33+
<br>
34+
35+
This tool runs entirely in your browser. No data is sent or stored to a server.
36+
37+
<br><br>
38+
39+
Press the "Init" button to start:
40+
41+
<br><br>
42+
</span>
43+
44+
<div align="center">
45+
<button onClick="doInit()" id="butInit" style="width:60px;height:30px;" disabled>Init</button>
46+
<br><br>
47+
<div class="nav-link2" id="download-info">Downloading WASM module and n-gram stats. Please wait ...</div>
48+
</div>
49+
</div>
50+
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" hidden></canvas>
51+
</div>
52+
53+
<div id="footer" class="cell-version">
54+
<span>
55+
Build time: <span class="nav-link">@GIT_DATE@</span> |
56+
Commit hash: <a class="nav-link" href="https://github.com/ggerganov/kbd-audio/commit/@GIT_SHA1@">@GIT_SHA1@</a> |
57+
Commit subject: <span class="nav-link">@GIT_COMMIT_SUBJECT@</span> |
58+
</span>
59+
</div>
60+
<div id="footer2" class="cell-about">
61+
<a class="nav-link" href="https://github.com/ggerganov/kbd-audio"><span class="d-none d-sm-inline">View on GitHub </span>
62+
<svg version="1.1" width="16" height="16" viewBox="0 0 16 16" class="octicon octicon-mark-github" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg>
63+
</a>
64+
</div>
65+
66+
<script type='text/javascript'>
67+
window.mobilecheck = function() {
68+
var check = false;
69+
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
70+
return check;
71+
};
72+
73+
var isMobile = mobilecheck();
74+
var isInitialized = false;
75+
76+
var screenX = -1;
77+
var screenY = -1;
78+
79+
var oldOutputId = 0;
80+
81+
window.setInterval(function(){
82+
if (isMobile) {
83+
document.getElementById("footer").innerHTML = '';
84+
document.getElementById("footer2").innerHTML = '';
85+
}
86+
if (isInitialized == false) return;
87+
var w = window,
88+
d = document,
89+
e = d.documentElement,
90+
g = d.getElementsByTagName('body')[0],
91+
x = w.innerWidth || e.clientWidth || g.clientWidth,
92+
y = w.innerHeight|| e.clientHeight|| g.clientHeight;
93+
Module._set_window_size(0.99*x, y - 1.40*document.getElementById('footer').clientHeight);
94+
}, 500);
95+
96+
function checkLoop() {
97+
var outputId = Module._get_output_id();
98+
if (outputId != oldOutputId) {
99+
offerFileAsDownload('record.kbd', 'mime/type');
100+
oldOutputId = outputId;
101+
}
102+
103+
setTimeout(checkLoop, 100);
104+
}
105+
106+
function onkeydown(event) {
107+
if (event.keyCode >= 112 && event.keyCode <= 123) {
108+
event.stopImmediatePropagation();
109+
}
110+
}
111+
112+
function init() {
113+
document.getElementById("butInit").disabled = false;
114+
document.getElementById("download-info").innerHTML = "WASM module initialized!";
115+
116+
window.addEventListener('keydown', onkeydown, true);
117+
118+
document.getElementById("canvas").addEventListener("dragover", (event) => {
119+
event.preventDefault();
120+
});
121+
122+
document.getElementById("canvas").addEventListener("drop", (event) => {
123+
event.preventDefault();
124+
125+
dropHandler(event);
126+
});
127+
128+
setTimeout(checkLoop, 100);
129+
//window.requestAnimationFrame(renderFrame);
130+
}
131+
132+
function doInit() {
133+
let constraints = {
134+
audio: {
135+
sampleRate: 16000,
136+
channelCount: 1,
137+
echoCancellation: false,
138+
autoGainControl: false,
139+
noiseSuppression: false
140+
}
141+
};
142+
143+
let mediaInput = navigator.mediaDevices.getUserMedia( constraints );
144+
145+
if (isInitialized == false) {
146+
Module._do_init();
147+
document.getElementById("butInit").disabled = true;
148+
document.getElementById("description").hidden = true;
149+
isInitialized = true;
150+
}
151+
152+
var x = document.getElementById("canvas");
153+
x.hidden = false;
154+
}
155+
156+
//function renderFrame() {
157+
// window.requestAnimationFrame(renderFrame);
158+
//}
159+
160+
function dropHandler(event) {
161+
// Prevent default behavior (Prevent file from being opened)
162+
event.preventDefault();
163+
164+
// fetch FileList object
165+
var files = event.target.files || event.dataTransfer.files;
166+
167+
// process all File objects
168+
for (var i = 0, f; f = files[i]; i++) {
169+
ParseFile(f);
170+
}
171+
}
172+
173+
function ParseFile(file) {
174+
console.log(
175+
"<p>File information: <strong>" + file.name +
176+
"</strong> type: <strong>" + file.type +
177+
"</strong> size: <strong>" + file.size +
178+
"</strong> bytes</p>"
179+
);
180+
181+
var reader = new FileReader();
182+
reader.onload = function(event) {
183+
var buf = new Uint8Array(reader.result);
184+
var stream = FS.open("record.kbd", "w");
185+
FS.write(stream, buf, 0, buf.length, 0);
186+
FS.close(stream);
187+
188+
Module._do_reload();
189+
}
190+
reader.readAsArrayBuffer(file);
191+
}
192+
193+
function offerFileAsDownload(filename, mime) {
194+
mime = mime || "application/octet-stream";
195+
196+
let content = FS.readFile(filename);
197+
console.log(`Offering download of "${filename}", with ${content.length} bytes...`);
198+
199+
var a = document.createElement('a');
200+
a.download = filename;
201+
a.href = URL.createObjectURL(new Blob([content], {type: mime}));
202+
a.style.display = 'none';
203+
204+
document.body.appendChild(a);
205+
a.click();
206+
setTimeout(() => {
207+
document.body.removeChild(a);
208+
URL.revokeObjectURL(a.href);
209+
}, 2000);
210+
}
211+
212+
var Module = {
213+
arguments: ["record.kbd", "./data"],
214+
preRun: [(function() {
215+
}) ],
216+
postRun: [(function () {
217+
init();
218+
})
219+
],
220+
canvas: (function() {
221+
var canvas = document.getElementById('canvas');
222+
//canvas.style.marginLeft = "-230px";
223+
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
224+
225+
return canvas;
226+
})(),
227+
print: (function() {
228+
return function(text) {
229+
text = Array.prototype.slice.call(arguments).join(' ');
230+
console.log(text);
231+
};
232+
})(),
233+
printErr: function(text) {
234+
text = Array.prototype.slice.call(arguments).join(' ');
235+
console.error(text);
236+
},
237+
setStatus: function(text) {
238+
console.log("status: " + text);
239+
},
240+
monitorRunDependencies: function(left) {
241+
}
242+
};
243+
window.onerror = function() {
244+
document.getElementById("download-info").innerHTML = "Failed to initialize the WASM module. Your browser might not be supported.";
245+
document.getElementById('download-info').style.color='red';
246+
console.log("onerror: " + event);
247+
};
248+
249+
</script>
250+
251+
<script async type="text/javascript" src="@TARGET@.js"></script>
252+
</body>
253+
</html>

0 commit comments

Comments
 (0)