Jump to content

[Version 1] Password Manager


Recommended Posts

Hi,

First post, first teensy program so don't judge. :lol:

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:

  1. Edit your login info in the source code
  2. Compile and upload it to teensy (duh)
  3. When you need to login, select the username field and plug teensy in
  4. Enter the secret key sequence (aka secret knock)
  5. Use scroll lock and num lock to navigate the login list
  6. Find the one you need and press caps lock
  7. Press login
  8. ???
  9. 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?

Edited by something_evil
Link to post
Share on other sites
Hi,

First post, first teensy program so don't judge. :lol:

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:

  1. Edit your login info in the source code
  2. Compile and upload it to teensy (duh)
  3. When you need to login, select the username field and plug teensy in
  4. Enter the secret key sequence (aka secret knock)
  5. Use scroll lock and num lock to navigate the login list
  6. Find the one you need and press caps lock
  7. Press login
  8. ???
  9. 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?

it is indeet possible to turn it into a flash drive, and the 24k fat32 partition it can handle internally would be enough for a passwords.txt, BUT its more then a mess if you want to switch easily between the two modes, fortunately you want read only support for the teensy and read write for windows but if both methods want to write to the file you will be in AVR hell.

maybe Paul can help ;) *wink*

Link to post
Share on other sites

Hi,

A couple of usability tweaks:

  1. Doesn't mess up caps/num/scroll state after use.
  2. After printing the login goes to sleep. Double tap scroll lock to wake up.
  3. When sleeping double tap num lock to print your selected favourite/default login.

Cleaned up the code, so it doesn't require you to mess with usb_api.xxx files. Also, if your code uses lots of status led input you might want to take a look at LedInput.h (bonus: it has key double tap support - high tech shit :o )

:angry: Argh... couldn't upload the sources or zip here so uploaded it to google docs: keychain.zip

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...