PassDuino is a open-source project for storing and auto typing passwords, it stores the passwords on a arduino (i use the CJMCU-32 with the ATMEGA32U4).
This is the configuration tool, you can use this tool to update/add/remove passwords to/from the PassDuino. This tool can be used as a monitor tool, so when you remove the PassDuino the PC will automatically lock.
You can also add global hotkey's for auto typing passwords stored in the PassDuino, like your gmail password, facebook etc. This allows you to use much more complicated / longer passwords without the need to remember them.
This is a open-source project, so the Windows application code is available for free, and you can change it / update / do whatever you like with it. It would be nice to share, so I can update the Github so others can enjoy new features.
/*
PassDuino.
Erdesigns.eu - Ernst Reidinga
Version 1.0 (August 2020)
First Release, for private use at this current moment :)
*** Lock your PC (Windows/Linux) when the COM port closes (USB Removal),
and enter the password on insertion. ***
You can use the configurator to switch passwords, so you can use it
to store more passwords. The settings are password protected, you can
set the password for the settings in the constructor.
When using the configuration tool (Windows/Linux) you can use it to
quickly enter the password you select in the configuration tool.
This allows you to take your passwords with you in a safe manner,
and be able to use very long or difficult passwords.
If you set the password length to 20 characters, you should be able to
save up to 50 passwords in the EEPROM. Should be more then enough for
most people :)
********************************************************************************
Note 1:
You can change the global variables in the Variables.h and Variables.cpp files,
i stored them in there so i dont have to redeclare the variables, and i have
all String's in one place.
Note 2:
The configuration integers are used for the settings, i personally use index
0 to 3 for the settings.
- 0: String Length (default 20 characters)
- 1: Default Password index (This password will auto type on USB insert)
- 2: Press Return after typing (0 = false, 1 = true)
- 3: Type delay in seconds, set to 0 to start typing directly. I use this
so i have the time to select another window when i use the configuration
software to enter a selected password.
- 4: This is set to FIRSTRUNKEY, this is a check to see if this is the "first" run,
if this is the first run (with a empty EEPROM) the configurator will set default
values for INT config 0-4 and a default password at index 0.
********************************************************************************
** The definition for the EEPROM Offset (bytes), EEPROM Size (bytes) and the
Password used for configurating are set here for convenience. **
I use this on the ATmega32U4 chip, with a CJMCU-32 USB stick. This chip has:
- 32KB Program Memory (Flash)
- 1KB EEPROM Memory
- 2.5KB SRAM Memory
- 16 MHZ Clock speed
** https://www.microchip.com/wwwproducts/en/ATmega32u4 **
** https://www.banggood.com/CJMCU-32-Virtual-Keyboard-Badusb-For-Leonardo-USB-ATMEGA32U4-p-1098876.html **
Other Keyboard Layouts - create a backup of the Keyboard.cpp file and update with the layout found here:
- https://github.com/Dukweeno/LocaleKeyboard.js/tree/master/locales
** Keyboard.cpp can be found here: arduino-x.x.x/libraries/Keyboard/src **
*/
#include "Keyboard.h"
#include "Variables.h"
#include "Configurator.h"
#include "PassDuino.h"
#define EEPROMOFFSET 0
#define EEPROMSIZE 1024
#define PASSWORD "YOURPASSWORD"
Configurator configurator(EEPROMSIZE, EEPROMOFFSET, PASSWORD);
PassDuino passduino(EEPROMSIZE, EEPROMOFFSET);
char character;
String command;
void setup() {
Keyboard.begin();
delay(KEYLONGDELAY);
// On plugging the USB we want to type the default password.
passduino.type_default_password();
// Then we want to wait for a connection on the COM Port
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB
}
}
void loop() {
// Here we will have a connection on the COM Port, and we'll read
// the commands send over the COM Port. This allows using the
// configuration tool to type passwords, but also allows us
// to add/delete/change passwords and settings with the configuration
// software (Windows / Linux).
while (Serial.available() > 0) {
// Read the input from the Serial Port
character = Serial.read();
// If the character is a newline or carriagereturn we want
// to process the command.
if (character == CR || character == LF) {
// Get the CMD from the command string and convert to uppercase.
String cmd = configurator.string_part(command, 0);
cmd.toUpperCase();
// Flush the Serial Port
Serial.flush();
// Start Configurator
if (cmd == STRCONFSTART && !configurator.is_active()) {
if (configurator.start(configurator.string_part(command, 1))) {
passduino.init();
}
} else
// Stop Configurator
if (cmd == STRCONFSTOP && configurator.is_active()) {
configurator.stop();
} else
// If the configurator is active, we want to respond to
// commands send from the Configuration software.
if (configurator.is_active()) {
// Set configuration - write to EEPROM
if (cmd == STRCONFSET) {
// Set CMD
cmd = configurator.string_part(command, 1);
cmd.toUpperCase();
// Write Int
if (cmd == STRCONFINT) {
configurator.write_configuration(configurator.string_part(command, 2).toInt(), "", configurator.string_part(command, 3).toInt());
// When settings are written like string length, we need to reinitialize the passduino because it still has the "old" string length
// and/or press return key / type delay settings. So when we use the configurator and update the string length and rewrite the
// passwords - the passduino must know what the new stringlength is because else it wont be able to read valid strings.
passduino.init();
} else
// Write String (password)
if (cmd == STRCONFSTR) {
configurator.write_configuration(configurator.string_part(command, 2).toInt() + STRCONFOFFSET, configurator.string_part(command, 3), -1);
} else
// All other commands are invalid.
{
configurator.error();
}
} else
// Get configuration - read from EEPROM
if (cmd == STRCONFGET) {
// Set CMD
cmd = configurator.string_part(command, 1);
cmd.toUpperCase();
// Read Int
if (cmd == STRCONFINT) {
configurator.read_configuration(configurator.string_part(command, 2).toInt());
} else
// Read String (password)
if (cmd == STRCONFSTR) {
configurator.read_configuration(configurator.string_part(command, 2).toInt() + STRCONFOFFSET);
} else
// All other commands are invalid.
{
configurator.error();
}
} else
// Execute action, used to type the password set by
// the configuration software, but you can add more
// commands if you like to extend the functionality.
if (cmd == STRCONFEXEC) {
// Set CMD
cmd = configurator.string_part(command, 1);
cmd.toUpperCase();
// Type the password at index
if (cmd == STRCMDTYPE) {
passduino.type_password(configurator.string_part(command, 2).toInt());
}
// Dump all passwords. This can come handy if you want all your stored passwords
// in a txt file for quick searching.
if (cmd == STRCMDDUMP) {
for (int i = 0; i < passduino.max_passwords(); ++i) {
passduino.type_password(i);
}
}
// Clear all passwords
if (cmd == STRCMDCLEAR) {
configurator.clear();
}
} else
// All other commands are invalid.
{
configurator.error();
}
} else
// If the configurator is not active, and we receive commands other
// than starting the configuration or request for a reboot -
// we send an error.
{
configurator.error();
}
// Clear the command, so we can read the next command.
command = "";
} else
// If the character is not the end of a command, we want to add
// the character to the command.
{
command.concat(character);
delay(10);
}
}
}
/*
PassDuino Configurator
Erdesigns.eu - Ernst Reidinga
Version 1.0 (August 2020)
Description:
- Read String and Int from EEPROM
- Write String and Int to EEPROM
*/
#include "Arduino.h"
#include "avr/wdt.h"
#include "Configurator.h"
#include "Variables.h"
// Reading or writing to build-in EEPROM
#include <EEPROM.h>
Configurator::Configurator(const int &eepromSize, const int &eepromOffset, const String &password) {
this->eepromSize = eepromSize;
this->eepromOffset = eepromOffset;
this->password = password;
// Test if this is the first time this program is run on the device
// and if it is, we want to set some default values.
if (this->get_configuration_int(4) != FIRSTRUNKEY) {
// String (Password) length
this->set_configuration_int(0, 20);
// Default password index
this->set_configuration_int(1, 0);
// Press return after typing
this->set_configuration_int(2, 0);
// Type delay - time before starting to type
this->set_configuration_int(3, 0);
// Set the first run key
this->set_configuration_int(4, FIRSTRUNKEY);
// Set "default" password at index 0
this->set_configuration_string(0 + STRCONFOFFSET, DEFAULTPASS);
}
this->stringLength = this->get_configuration_int(0);
}
int Configurator::write_string_eeprom(const int &offset, const String &str) {
byte len = str.length();
EEPROM.update(this->eepromOffset + offset, len);
for (int i = 0; i < len; i++) {
EEPROM.update(this->eepromOffset + offset + 1 + i, str[i]);
}
return offset + 1 + len;
}
int Configurator::write_integer_eeprom(const int &offset, const int &val) {
byte lowByte = ((val >> 0) & 0xFF);
byte highByte = ((val >> 8) & 0xFF);
EEPROM.update(this->eepromOffset + offset, lowByte);
EEPROM.update(this->eepromOffset + offset + 1, highByte);
return offset + 2;
}
String Configurator::get_configuration_string(const int &config) {
if (config - STRCONFOFFSET < 0) {
return "";
}
int offset;
offset = this->eepromOffset + (STRCONFOFFSET * 2) + ((config - STRCONFOFFSET) * (this->stringLength +1));
int newStrLen = EEPROM.read(offset);
char data[newStrLen + 1];
for (int i = 0; i < newStrLen; i++) {
data[i] = EEPROM.read(offset + 1 + i);
}
data[newStrLen] = '\0';
return String(data);
}
bool Configurator::set_configuration_string(const int &config, const String &str) {
if (config - STRCONFOFFSET < 0) {
return false;
}
int offset;
offset = this->eepromOffset + (STRCONFOFFSET * 2) + ((config - STRCONFOFFSET) * (this->stringLength +1));
return this->write_string_eeprom(offset, str) > offset -1;
}
int Configurator::get_configuration_int(const int &config){
if (config > STRCONFOFFSET) {
return -1;
}
int output;
int offset;
offset = this->eepromOffset + config * 2;
byte lowByte = EEPROM.read(offset);
byte highByte = EEPROM.read(offset + 1);
output = ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
return output;
}
bool Configurator::set_configuration_int(const int &config, const int &val){
if (config > STRCONFOFFSET) {
return false;
}
int offset;
offset = this->eepromOffset + config * 2;
return this->write_integer_eeprom(offset, val) > offset -1;
}
void Configurator::write_configurator_output_string(const String &str) {
String output = str;
output.concat(CNLN);
Serial.print(output);
}
void Configurator::write_configurator_output_hex(const int &val) {
Serial.print(val, HEX);
Serial.print(CNLN);
}
void Configurator::write_configurator_output_dec(const int &val) {
Serial.print(val, DEC);
Serial.print(CNLN);
}
int Configurator::max_passwords() {
return floor((this->eepromSize - (this->eepromOffset + (STRCONFOFFSET * 2))) / this->stringLength);
}
bool Configurator::start(const String &password) {
//EEPROM.begin(this->eepromSize);
this->stringLength = this->get_configuration_int(0);
if (password == this->password) {
this->active = true;
this->write_configurator_output_string(STRSTART);
} else {
this->active = false;
this->error();
}
return this->active;
}
void Configurator::stop() {
//EEPROM.end();
this->active = false;
this->write_configurator_output_string(STRSTOP);
}
void Configurator::clear() {
for (int i = 0; i < this->max_passwords(); ++i) {
this->write_configuration(i + STRCONFOFFSET, "", -1);
}
this->write_configurator_output_string(STROK);
}
bool Configurator::is_active() {
return this->active;
}
void Configurator::ok() {
this->write_configurator_output_string(STROK);
}
void Configurator::error() {
this->write_configurator_output_string(STRER);
}
void Configurator::read_configuration(const int &config) {
if (config == -1) {
this->write_configurator_output_dec(this->max_passwords());
} else
if (config < STRCONFOFFSET && config > -1) {
this->write_configurator_output_dec(this->get_configuration_int(config));
} else
if (config >= STRCONFOFFSET && config < this->max_passwords()) {
this->write_configurator_output_string(this->get_configuration_string(config));
} else
if (config >= this->max_passwords() || config < -1) {
this->error();
}
}
void Configurator::write_configuration(const int &config, const String &str, const int &val) {
if (config < STRCONFOFFSET && config > -1) {
if (this->set_configuration_int(config, val)) {
this->ok();
} else {
this->error();
}
} else
if (config >= STRCONFOFFSET && config < this->max_passwords()) {
if (this->set_configuration_string(config, str)) {
this->ok();
} else {
this->error();
}
} else
if (config >= this->max_passwords() || config < -1) {
this->error();
}
}
int Configurator::string_part_count(const String &str) {
int c = 0;
for (int i = 0; str[i] != '\0'; i++) {
if(str[i] == DIVIDER)
++c;
}
return c;
}
String Configurator::string_part(const String &str, const int &index) {
int found = 0;
int strIndex[] = { 0, -1 };
int maxIndex = str.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++) {
if (str.charAt(i) == DIVIDER || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found > index ? str.substring(strIndex[0], strIndex[1]) : "";
}
/*
PassDuino Configurator
Erdesigns.eu - Ernst Reidinga
Version 1.0 (August 2020)
Description:
- Read String and Int from EEPROM
- Write String and Int to EEPROM
*/
#ifndef Configurator_h
#define Configurator_h
#include <Arduino.h>
#include <avr/wdt.h>
#include "Variables.h"
class Configurator {
private:
int eepromSize;
int eepromOffset;
int stringLength;
bool active;
String password;
int write_string_eeprom(const int &offset, const String &str);
int write_integer_eeprom(const int &offset, const int &val);
String get_configuration_string(const int &config);
bool set_configuration_string(const int &config, const String &str);
int get_configuration_int(const int &config);
bool set_configuration_int(const int &config, const int &val);
void write_configurator_output_string(const String &str);
void write_configurator_output_hex(const int &val);
void write_configurator_output_dec(const int &val);
public:
Configurator(const int &eepromSize, const int &eepromOffset, const String &password);
int max_passwords();
bool start(const String &password);
void stop();
void clear();
bool is_active();
void ok();
void error();
void read_configuration(const int &config);
void write_configuration(const int &config, const String &str, const int &val);
int string_part_count(const String &str);
String string_part(const String &str, const int &index);
};
#endif
/*
PassDuino
Erdesigns.eu - Ernst Reidinga
Version 1.0 (August 2020)
Description:
- Type passwords as a simulated keyboard
*/
#include "Arduino.h"
#include "PassDuino.h"
#include "Variables.h"
// Reading or writing to build-in EEPROM
#include <EEPROM.h>
// Needed for emulating a keyboard.
#include <Keyboard.h>
PassDuino::PassDuino(const int &eepromSize, const int &eepromOffset) {
this->eepromSize = eepromSize;
this->eepromOffset = eepromOffset;
this->init();
}
String PassDuino::get_password(const int &index) {
if (index < 0 || index > this->max_passwords()) {
return "";
}
int offset;
offset = this->eepromOffset + (STRCONFOFFSET * 2) + (index * (this->stringLength +1));
int newStrLen = EEPROM.read(offset);
char data[newStrLen + 1];
for (int i = 0; i < newStrLen; i++) {
data[i] = EEPROM.read(offset + 1 + i);
}
data[newStrLen] = '\0';
return String(data);
}
int PassDuino::get_configuration(const int &config) {
if (config > STRCONFOFFSET) {
return -1;
}
int output;
int offset;
offset = this->eepromOffset + config * 2;
byte lowByte = EEPROM.read(offset);
byte highByte = EEPROM.read(offset + 1);
output = ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
return output;
}
int PassDuino::max_passwords() {
return floor((this->eepromSize - (this->eepromOffset + (STRCONFOFFSET * 2))) / this->stringLength);
}
void PassDuino::init() {
this->stringLength = this->get_configuration(0);
this->default_password_index = this->get_configuration(1);
this->return_after_typing = this->get_configuration(2) == 1;
this->typeDelay = this->get_configuration(3);
}
void PassDuino::type_default_password() {
this->type_string(this->get_password(this->default_password_index));
}
void PassDuino::type_string(const String &str) {
Keyboard.print(str);
if (this->return_after_typing) {
Keyboard.press(KEY_RETURN);
delay(KEYSMALLDELAY);
Keyboard.release(KEY_RETURN);
}
}
void PassDuino::type_password(const int &index) {
if (this->typeDelay > 0) {
delay(this->typeDelay * 1000);
}
this->type_string(this->get_password(index));
}
/*
PassDuino
Erdesigns.eu - Ernst Reidinga
Version 1.0 (August 2020)
Description:
- Type passwords as a simulated keyboard
*/
#ifndef PassDuino_h
#define PassDuino_h
#include <Arduino.h>
class PassDuino {
private:
int eepromSize;
int eepromOffset;
int stringLength;
int typeDelay;
int default_password_index;
bool return_after_typing;
String get_password(const int &index);
int get_configuration(const int &config);
public:
PassDuino(const int &eepromSize, const int &eepromOffset);
int max_passwords();
void init();
void type_default_password();
void type_string(const String &str);
void type_password(const int &index);
};
#endif
/*
PassDuino Variables
Erdesigns.eu - Ernst Reidinga
Version 1.0 (August 2020)
Description:
- Global variables used in Configurator and PassDuino.
Note:
- Change the STRCONOFFSET if you want to store more int values,
after this the rest space is for saving Strings (passwords).
- Change the STRSTART and STRSTOP to your liking if you like to
have another message you can insert them there.
*/
#include "Arduino.h"
#include "Variables.h"
const extern String CNLN = "\r>";
const extern String CRLF = "\r\n";
const extern char CR = '\r';
const extern char LF = '\n';
const extern char DIVIDER = '|';
const extern String STROK = "OK";
const extern String STRER = "ERROR";
const extern String STRSTART = "Configuration mode started.";
const extern String STRSTOP = "Configuration mode stopped.";
const extern String DEFAULTPASS = "Empty Password";
const extern String STRCONFSTART = "STARTCONFIG";
const extern String STRCONFSTOP = "STOPCONFIG";
const extern String STRCONFSET = "SET";
const extern String STRCONFGET = "GET";
const extern String STRCONFSTR = "STR";
const extern String STRCONFINT = "INT";
const extern String STRCONFEXEC = "EXE";
const extern String STRCMDTYPE = "TYPE";
const extern String STRCMDDUMP = "DUMP";
const extern String STRCMDCLEAR = "CLEAR";
const extern int STRCONFOFFSET = 5;
const extern int KEYSMALLDELAY = 500;
const extern int KEYLONGDELAY = 1000;
const extern int FIRSTRUNKEY = 85;
/*
PassDuino Variables
Erdesigns.eu - Ernst Reidinga
Version 1.0 (August 2020)
Description:
- Global variables used in Configurator and PassDuino.
*/
#ifndef Variables_h
#define Variables_h
#include <Arduino.h>
const extern String CNLN;
const extern String CRLF;
const extern char CR;
const extern char LF;
const extern char DIVIDER;
const extern String STROK;
const extern String STRER;
const extern String STRSTART;
const extern String STRSTOP;
const extern String DEFAULTPASS;
const extern String STRCONFSTART;
const extern String STRCONFSTOP;
const extern String STRCONFSET;
const extern String STRCONFGET;
const extern String STRCONFSTR;
const extern String STRCONFINT;
const extern String STRCONFEXEC;
const extern String STRCMDTYPE;
const extern String STRCMDDUMP;
const extern String STRCMDCLEAR;
const extern int STRCONFOFFSET;
const extern int KEYSMALLDELAY;
const extern int KEYLONGDELAY;
const extern int FIRSTRUNKEY;
#endif
Comments
Please log in or sign up to comment.