eXXecutor
Published © GPL3+

PassDuino

PassDuino is a native USB arduino that auto types your password on insertion, type passwords with hotkeys, and lock windows upon removal

BeginnerWork in progress596
PassDuino

Things used in this project

Hardware components

CJMCU-32 (Arduino Leonardo Compatible)
×1

Software apps and online services

PassDuino Windows Software

Hand tools and fabrication machines

Embarcadero Delphi

Story

Read more

Schematics

CJMCU-32

Can be bought like this for €5 - €15, there are also variations with SD card reader and/or wifi. Arduino Leonardo compatible

Code

PassDuino

Arduino
Main Project file
/*

  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);
    }

  }

}

Configurator.cpp

C/C++
/*

	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]) : "";
}

Configurator.h

C Header File
/*

	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.cpp

C/C++
/*

	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.h

C Header File
/*

	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

Variables.cpp

C/C++
/*

	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;

Variables.h

C Header File
/*

	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

Credits

eXXecutor
0 projects • 0 followers
Contact

Comments

Please log in or sign up to comment.