Skip to content

Commit

Permalink
Keytap3 - fully automated attack + many improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
ggerganov committed Apr 25, 2022
1 parent 5665b57 commit d800029
Show file tree
Hide file tree
Showing 40 changed files with 5,381 additions and 499 deletions.
75 changes: 59 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,24 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
endif()

set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD_REQUIRED ON)

if (EMSCRIPTEN)
set (CMAKE_CXX_FLAGS " \
-s USE_SDL=2 \
-s USE_PTHREADS=1 \
")
#-s DISABLE_EXCEPTION_CATCHING=1 \

set(CMAKE_EXE_LINKER_FLAGS " \
--bind \
--use-preload-cache \
--closure 1 \
-s ASSERTIONS=1 \
-s NO_EXIT_RUNTIME=0 \
-s PTHREAD_POOL_SIZE=8 \
-s INITIAL_MEMORY=536870912 \
")
elseif(MINGW)
find_package(PkgConfig REQUIRED)
Expand Down Expand Up @@ -101,7 +113,7 @@ include_directories(${OPENGL_INCLUDE_DIR})

add_library(Core STATIC
common.cpp
audio_logger.cpp
audio-logger.cpp
)

target_include_directories(Core PRIVATE
Expand Down Expand Up @@ -158,11 +170,17 @@ else()
)
endif()

add_executable(keytap-gui keytap-gui.cpp)
target_link_libraries(keytap-gui PRIVATE Core Gui)

add_executable(keytap2-gui keytap2-gui.cpp subbreak2.cpp)
target_link_libraries(keytap2-gui PRIVATE Core Gui)

add_executable(keytap-gui keytap-gui.cpp)
target_link_libraries(keytap-gui PRIVATE Core Gui)
add_executable(keytap3-gui keytap3-gui.cpp subbreak3.cpp)
target_link_libraries(keytap3-gui PRIVATE Core Gui)

add_executable(keytap3-app keytap3-app.cpp subbreak3.cpp)
target_link_libraries(keytap3-app PRIVATE Core)

add_executable(view-full-gui view-full-gui.cpp)
target_link_libraries(view-full-gui PRIVATE Core Gui)
Expand All @@ -175,7 +193,7 @@ if (EMSCRIPTEN)
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \
-s TOTAL_MEMORY=536870912 \
-s LZ4=1 \
--preload-file ${PROJECT_SOURCE_DIR}/chal/record@/ \ \
--preload-file ${PROJECT_SOURCE_DIR}/chal/record@/chal/record/ \ \
")

## keytap-gui
Expand All @@ -195,20 +213,39 @@ if (EMSCRIPTEN)
-s TOTAL_MEMORY=536870912 \
-s FORCE_FILESYSTEM=1 \
-s LZ4=1 \
--preload-file ${PROJECT_SOURCE_DIR}/data@/ \
--preload-file ${PROJECT_SOURCE_DIR}/data/@/data/ \
")

configure_file(${CMAKE_SOURCE_DIR}/index-${TARGET}-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/index.html @ONLY)
configure_file(${CMAKE_SOURCE_DIR}/style.css ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/style.css @ONLY)

# keytap3-app
set(TARGET keytap3-app)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${TARGET})

set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \
-s FORCE_FILESYSTEM=1 \
-s LZ4=1 \
--preload-file ${PROJECT_SOURCE_DIR}/data/ggwords-6-gram.dat.binary@/data/ \
")

configure_file(${CMAKE_SOURCE_DIR}/index-${TARGET}-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/index.html @ONLY)
configure_file(${CMAKE_SOURCE_DIR}/style.css ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/style.css @ONLY)
endif()

if (NOT EMSCRIPTEN)
add_executable(key-detector key-detector.cpp)
target_link_libraries(key-detector PRIVATE Core)

add_executable(keytap keytap.cpp)
target_link_libraries(keytap PRIVATE Core)

add_executable(keytap2 keytap2.cpp)
target_link_libraries(keytap2 PRIVATE Core)

add_executable(keytap3 keytap3.cpp subbreak3.cpp)
target_link_libraries(keytap3 PRIVATE Core)

add_executable(play play.cpp)
target_link_libraries(play PRIVATE Core)

Expand All @@ -227,26 +264,29 @@ if (NOT EMSCRIPTEN)
add_executable(view-gui view-gui.cpp)
target_link_libraries(view-gui PRIVATE Core Gui)

add_executable(compress-n-grams compress-n-grams.cpp subbreak3.cpp)
target_link_libraries(compress-n-grams PRIVATE Core)

#
## Experimental stuff

if (BUILD_EXPERIMENTAL)
if (FFTW_FOUND)
add_executable(key_average_gui key_average_gui.cpp)
target_include_directories(key_average_gui PRIVATE ${FFTW_INCLUDE_DIRS})
target_link_libraries(key_average_gui PRIVATE Core Gui ${FFTW_LIBRARIES})
add_executable(key-average-gui key-average-gui.cpp)
target_include_directories(key-average-gui PRIVATE ${FFTW_INCLUDE_DIRS})
target_link_libraries(key-average-gui PRIVATE Core Gui ${FFTW_LIBRARIES})
else()
message(WARNING "Skipping 'key_average_gui' target because FFTW is not available")
message(WARNING "Skipping 'key-average-gui' target because FFTW is not available")
endif()

add_executable(guess_qp guess_qp.cpp)
target_link_libraries(guess_qp PRIVATE Core)
add_executable(keytap3-multi keytap3-multi.cpp subbreak3.cpp)
target_link_libraries(keytap3-multi PRIVATE Core)

add_executable(guess_qp2 guess_qp2.cpp)
target_link_libraries(guess_qp2 PRIVATE Core)
add_executable(guess-qp guess-qp.cpp)
target_link_libraries(guess-qp PRIVATE Core)

add_executable(key_detector key_detector.cpp)
target_link_libraries(key_detector PRIVATE Core)
add_executable(guess-qp2 guess-qp2.cpp)
target_link_libraries(guess-qp2 PRIVATE Core)

add_executable(scale scale.cpp)
target_link_libraries(scale PRIVATE Core)
Expand All @@ -262,6 +302,9 @@ if (NOT EMSCRIPTEN)

add_executable(generate-clusters generate-clusters.cpp subbreak2.cpp)
target_link_libraries(generate-clusters PRIVATE Core)

add_executable(test-subbreak3 test-subbreak3.cpp subbreak3.cpp)
target_link_libraries(test-subbreak3 PRIVATE Core)
endif()

endif()
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ The **keytap2** tool is another interesting tool for recovering text from audio.

[CTF: can you guess the text being typed?](https://ggerganov.github.io/keytap-challenge/)

### Keytap3

This version introduces significant algorithm improvements and better n-gram statistics. The attack is now fully
automated and and does not require any manual intervation during the decoding process.

[Check if your keyboard is vulnerable to Keytap](https://keytap3.ggerganov.com)

### What people say about Keytap

*"This works incredibly well.\
Expand All @@ -48,7 +55,7 @@ Dependencies:

[Mac OS with brew]
$ brew install sdl2

[MSYS2]
$ pacman -S git cmake make mingw-w64-x86_64-dlfcn mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2

Expand All @@ -75,17 +82,20 @@ Short summary of the available tools. If the status of the tool is not **stable*
| **play-full** | text | **stable** |
| **view-gui** | gui | **stable** |
| **view-full-gui** | gui | **stable** |
| **key-detector** | text | **stable** |
| **keytap** | text | **stable** |
| **keytap-gui** | gui | **stable** |
| **keytap2** | text | development |
| **keytap2-gui** | gui | **stable** |
| **keytap2-gui** | gui | **stable** |
| **keytap3** | text | **stable** |
| - | *extra* | - |
| **guess_qp** | text | experiment |
| **guess_qp2** | text | experiment |
| **key_detector** | text | experiment |
| **guess-qp** | text | experiment |
| **guess-qp2** | text | experiment |
| **keytap3-multi** | text | experiment |
| **scale** | text | experiment |
| **subreak** | text | experiment |
| **key_average_gui** | gui | experiment |
| **key-average-gui** | gui | experiment |
| **keytap2** | text | experiment |
| **keytap3-gui** | gui | experiment |

## Tool details

Expand Down Expand Up @@ -151,6 +161,16 @@ Short summary of the available tools. If the status of the tool is not **stable*

---

* **keytap3**

Fully automated recovery of unknown text from audio recordings.

./keytap3 input.kbd ../data [-cN] [-CN] [-pF] [-tF] [-FN] [-fN]

Online demo: https://keytap3.ggerganov.com

---

* **view-full-gui**

Visualize waveforms recorded with the **record-full** tool. Can also playback the audio data.
Expand Down
107 changes: 13 additions & 94 deletions audio_logger.cpp → audio-logger.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/*! \file audio_logger.cpp
/*! \file audio-logger.cpp
* \brief Enter description here.
* \author Georgi Gerganov
*/

#include "audio_logger.h"
#include "audio-logger.h"

#include <SDL.h>
#include <SDL_audio.h>
Expand All @@ -12,93 +12,7 @@
#include <atomic>
#include <algorithm>

#ifndef pi
#define pi 3.1415926535897932384626433832795
#endif

#ifndef sqrt2
#define sqrt2 (2.0 * 0.707106781186547524401)
#endif

#ifndef sqrt2over2
#define sqrt2over2 0.707106781186547524401
#endif

namespace {

// DSP filter
// ref : https://github.com/dimtass/DSP-Cpp-filters

struct TFilterCoefficients {
float a0;
float a1;
float a2;
float b1;
float b2;
float c0;
float d0;

float xnz1;
float xnz2;
float ynz1;
float ynz2;
};

TFilterCoefficients calculateCoefficientsFirstOrderHighPass(int fc, int fs) {
TFilterCoefficients res;

float th = 2.0 * pi * fc / fs;
float g = cos(th) / (1.0 + sin(th));
res.a0 = (1.0 + g) / 2.0;
res.a1 = -((1.0 + g) / 2.0);
res.a2 = 0.0;
res.b1 = -g;
res.b2 = 0.0;

return res;
}

TFilterCoefficients calculateCoefficientsSecondOrderButterworthHighPass(int fc, int fs) {
TFilterCoefficients res;

float c = tan(pi*fc / fs);
res.a0 = 1.0 / (1.0 + sqrt2*c + pow(c, 2.0));
res.a1 = -2.0 * res.a0;
res.a2 = res.a0;
res.b1 = 2.0 * res.a0*(pow(c, 2.0) - 1.0);
res.b2 = res.a0 * (1.0 - sqrt2*c + pow(c, 2.0));

return res;
}

AudioLogger::Sample filterFirstOrderHighPass(TFilterCoefficients & coefficients, AudioLogger::Sample sample) {
AudioLogger::Sample xn = sample;
AudioLogger::Sample yn =
coefficients.a0*xn + coefficients.a1*coefficients.xnz1 + coefficients.a2*coefficients.xnz2 -
coefficients.b1*coefficients.ynz1 - coefficients.b2*coefficients.xnz2;

coefficients.xnz2 = coefficients.xnz1;
coefficients.xnz1 = xn;
coefficients.xnz2 = coefficients.ynz1;
coefficients.ynz1 = yn;

return yn;
}

AudioLogger::Sample filterSecondOrderButterworthHighPass(TFilterCoefficients & coefficients, AudioLogger::Sample sample) {
AudioLogger::Sample xn = sample;
AudioLogger::Sample yn =
coefficients.a0*xn + coefficients.a1*coefficients.xnz1 + coefficients.a2*coefficients.xnz2 -
coefficients.b1*coefficients.ynz1 - coefficients.b2*coefficients.xnz2;

coefficients.xnz2 = coefficients.xnz1;
coefficients.xnz1 = xn;
coefficients.xnz2 = coefficients.ynz1;
coefficients.ynz1 = yn;

return yn;
}

void cbAudioReady(void * userData, uint8_t * stream, int32_t /*nbytes*/) {
AudioLogger * logger = (AudioLogger *)(userData);
logger->addFrame((AudioLogger::Sample *)(stream));
Expand Down Expand Up @@ -235,16 +149,16 @@ bool AudioLogger::install(Parameters && parameters) {
parameters.nChannels = obtainedSpec.channels;

switch (parameters.filter) {
case AudioLogger::Parameters::EFilter::None:
case EAudioFilter::None:
{
}
break;
case AudioLogger::Parameters::EFilter::FirstOrderHighPass:
case EAudioFilter::FirstOrderHighPass:
{
data.filterCoefficients = ::calculateCoefficientsFirstOrderHighPass(parameters.freqCutoff_Hz, parameters.sampleRate);
}
break;
case AudioLogger::Parameters::EFilter::SecondOrderButterworthHighPass:
case EAudioFilter::SecondOrderButterworthHighPass:
{
data.filterCoefficients = ::calculateCoefficientsSecondOrderButterworthHighPass(parameters.freqCutoff_Hz, parameters.sampleRate);
}
Expand All @@ -254,6 +168,11 @@ bool AudioLogger::install(Parameters && parameters) {
data.parameters = parameters;
data.isReady = true;

// print filter paramters
printf(" Audio Filter: %d\n", parameters.filter);
printf(" Cutoff frequency: %g Hz\n", parameters.freqCutoff_Hz);
printf("Capturing audio ..\n");

return true;
}

Expand Down Expand Up @@ -289,18 +208,18 @@ bool AudioLogger::addFrame(const Sample * stream) {
}

switch (data.parameters.filter) {
case AudioLogger::Parameters::EFilter::None:
case EAudioFilter::None:
{
}
break;
case AudioLogger::Parameters::EFilter::FirstOrderHighPass:
case EAudioFilter::FirstOrderHighPass:
{
for (auto & s : curFrame) {
s = ::filterFirstOrderHighPass(data.filterCoefficients, s);
}
}
break;
case AudioLogger::Parameters::EFilter::SecondOrderButterworthHighPass:
case EAudioFilter::SecondOrderButterworthHighPass:
{
for (auto & s : curFrame) {
s = ::filterSecondOrderButterworthHighPass(data.filterCoefficients, s);
Expand Down
Loading

0 comments on commit d800029

Please sign in to comment.