Hi,
First post, first teensy program so don't judge.
Finally got around to implementing an idea I had for some time - a HID password manager. No buttons, no screen, no soldering required. Plug it in and use your normal keyboard to control it. Not very useful for the offensive aspect of things, but not getting pwned is important, right?
It uses keyboard status leds for input: you press caps/scroll/num lock, the led turns on and teensy can act on that.
The way you would use it is:
Edit your login info in the source code
Compile and upload it to teensy (duh)
When you need to login, select the username field and plug teensy in
Enter the secret key sequence (aka secret knock)
Use scroll lock and num lock to navigate the login list
Find the one you need and press caps lock
Press login
???
Profit
The secret knock is just a sequence of caps, num and scroll lock keys (see source code). Default is: caps, scroll, num lock, num lock.
To make it work I had to add a couple of lines to usb_api.cpp:
bool usb_keyboard_class::led_numlock_on()
{
return keyboard_leds & 1;
}
bool usb_keyboard_class::led_scrolllock_on()
{
return keyboard_leds & 4;
}
bool usb_keyboard_class::led_capslock_on()
{
return keyboard_leds & 2;
}
bool usb_keyboard_class::isReady()
{
return usb_configuration;
}
Obviously you need to add definitions to usb_keyboard_class class in usb_api.h:
bool led_numlock_on();
bool led_capslock_on();
bool led_scrolllock_on();
bool isReady();
And the actual program code:
/*
Teensy Password Manager
By: something_evil on 30 May 2010.
--------- Controls:
Scroll lock - up;
Num lock - down;
Caps lock - type the selected login info;
--------- Configuration and general info:
Edit the logins in setup() and don't forget to change LOGIN_COUNT constant to how many passwords
you have.
knockSeq - sequence of keys, that unlocks the device. Once plugged in the device will flash twice.
This means you can start entering the secret sequence. Default is caps lock, scroll lock, num lock,
num lock. If you mess up, just start from the beginning.
LOCK_OUT_AFTER - This sets the number of times you can mess up the secret sequence. By default
you have 3 tries. After that - unplug, relax, plug it in and try again.
The input is a bit flaky so don't rush it. It's not a race ;)
Hack away!
*/
/* ---- Stuff for the logins DB ---- */
struct loginEntry {
char* description;
char* userName;
char* password;
};
const int LOGIN_COUNT = 4;
loginEntry logins[LOGIN_COUNT];
int currentPos = 0; // currently selected entry
/* ---- Authentication stuff ---- */
byte knockSeq[] = {KEY_CAPS_LOCK, KEY_SCROLL_LOCK, KEY_NUM_LOCK, KEY_NUM_LOCK};
int knockPos = 0;
byte knockFails = 0;
boolean authenticated = false;
boolean lockedOut = false;
const int LOCK_OUT_AFTER = 3;
/* ---- LED stuff ---- */
const int ledPin = 11;
int ledState = LOW;
long previousMillis = 0; // will store last time LED was updated
long interval = 250; // interval at which to blink (milliseconds)
boolean firstRun = true;
void setup() {
pinMode(ledPin, OUTPUT);
/* ------------------- Enter your logins here ------------------- */
logins[0].description = "First site";
logins[0].userName = "Administrator";
logins[0].password = "rootpassword!@#$%^&*()_+|";
logins[1].description = "Second site";
logins[1].userName = "Username";
logins[1].password = "meh";
logins[2].description = "My gmail password";
logins[2].userName = "Loosername";
logins[2].password = "looserpassword";
logins[3].description = "other email password";
logins[3].userName = "name";
logins[3].password = "pass";
}
/* Stolen (and changed a bit) from
http://www.irongeek.com/i.php?page=securit...eystroke-dongle
*/
void PressAndRelease(int KeyCode, int ModifierCode = 0, int KeyCount = 1){
for (int KeyCounter = 0; KeyCounter < KeyCount; KeyCounter++){
Keyboard.set_modifier(ModifierCode);
Keyboard.set_key1(KeyCode);
Keyboard.send_now();
Keyboard.set_modifier(0);
Keyboard.set_key1(0);
Keyboard.send_now();
}
}
/* Clears the keyboard's status lights */
void ClearLights() {
if (Keyboard.led_numlock_on())
PressAndRelease(KEY_NUM_LOCK);
if (Keyboard.led_capslock_on())
PressAndRelease(KEY_CAPS_LOCK);
if (Keyboard.led_scrolllock_on())
PressAndRelease(KEY_SCROLL_LOCK);
}
/* Clears the currently focused field/line */
void ClearCurrentField() {
/* Key sequence: END (just to be sure), SHIFT + HOME, DEL */
PressAndRelease(KEY_END);
PressAndRelease(KEY_HOME, MODIFIERKEY_SHIFT);
PressAndRelease(KEY_DELETE);
}
/* Erases previous login description and prints current one */
void PrintCurrentDescription() {
ClearCurrentField();
/* Lets print our current position in the list and the total number of logins */
Keyboard.print(currentPos + 1);
Keyboard.print("/");
Keyboard.print(LOGIN_COUNT);
Keyboard.print(" ");
Keyboard.print(logins[currentPos].description);
}
/* Navigates the login info array */
void NavigateDown() {
currentPos++;
if (currentPos >= LOGIN_COUNT)
currentPos = 0;
PrintCurrentDescription();
}
/* Navigates the login info array */
void NavigateUp() {
currentPos--;
if (currentPos < 0)
currentPos = LOGIN_COUNT - 1;
PrintCurrentDescription();
}
/* Prints login info */
void DoLogin() {
ClearCurrentField();
Keyboard.print(logins[currentPos].userName);
PressAndRelease(KEY_TAB);
Keyboard.print(logins[currentPos].password);
}
/* Authenticates the user based on 'knock' sequence */
void DoAuthentication() {
byte currentKey = 0; // key pressed (caps, num or scroll lock)
if (Keyboard.led_numlock_on())
{
delay(100); // user input messes up without this
currentKey = KEY_NUM_LOCK;
PressAndRelease(KEY_NUM_LOCK);
}
if (Keyboard.led_scrolllock_on())
{
delay(100); // user input messes up without this
currentKey = KEY_SCROLL_LOCK;
PressAndRelease(KEY_SCROLL_LOCK);
}
if (Keyboard.led_capslock_on())
{
delay(100); // user input messes up without this
currentKey = KEY_CAPS_LOCK;
PressAndRelease(KEY_CAPS_LOCK);
}
if (currentKey == 0) // User didn't press any keys this loop
return;
if (knockSeq[knockPos] == currentKey) { // is the sequence correct ?
knockPos++; // correct, now ask for next key
} else {
knockPos = 0; // wrong sequence, start over
knockFails++;
if (knockFails >= LOCK_OUT_AFTER){
lockedOut = true; // game over. Unplug, plug in and try again
// Keyboard.print("Better luck next time, foo");
}
}
if (knockPos >= sizeof(knockSeq)){ // user correctly entered the whole sequnce
authenticated = true;
PrintCurrentDescription();
}
}
/* Blinks the LED (code from BlinkWithoutDelay example) */
void BlinkLed(){
if (millis() - previousMillis > interval) {
previousMillis = millis(); // save the last time you blinked the LED
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
digitalWrite(ledPin, ledState); // set the LED with the ledState of the variable:
}
}
/* ---------------------- Main loop ---------------------- */
void loop() {
if (!Keyboard.isReady()) return; // can't run until usb stuff ready
if (firstRun == true){ // also, on first run wait a bit so the drivers start working
delay(1500);
ClearLights(); // turn off num lock, caps lock and scroll lock
firstRun = false;
/* Let the user know we're ready by blinking twice */
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
}
if (lockedOut) // just exit if the user failed to correctly enter the knock sequence
return;
if (!authenticated){ // not authenticated ?
DoAuthentication(); // then guess the knocks
delay(100);
return; // can't go further until authenticated
}
BlinkLed(); // blinks the led so user knows it's working
/* Process input */
if (Keyboard.led_numlock_on())
{
delay(100); // user input messes up without this
NavigateDown();
PressAndRelease(KEY_NUM_LOCK);
}
if (Keyboard.led_scrolllock_on())
{
delay(100); // user input messes up without this
NavigateUp();
PressAndRelease(KEY_SCROLL_LOCK);
}
if (Keyboard.led_capslock_on())
{
/* If we start typing immediately wierd screwups happen -
password in uppercase (user hasn't released capslock yet?).
Just to be safe wait for 200 ms */
delay(200);
PressAndRelease(KEY_CAPS_LOCK);
DoLogin();
}
delay(100);
}
Tested on Win7 (x64), but since it's HID it should work anywhere.
It would be cool if there was a way to easily edit the login info without needing to recompile. I was thinking about having two secret knocks - one to turn on the password manager and another to turn it into a flash drive. The passwords would be in a text file. Any ideas?