Introduction

calc.pw is a hardware project that connects between the keyboard and the computer. Its password mode allows you to easily generate as many passwords as you need. To do so it uses a master password that is entered through the keyboard on startup. When a new password is needed you switch to the password mode with a certain key combination and enter an information. That information can be dependent on the service you need a password for (like "twitter", "e-mail", banking). With the master password and the information the actual password is generated. When entering the same master password and the same information you will always get the same password. Thanks to that you no longer have to remember all those passwords anymore. And as calc.pw is implemented in hardware viruses and trojans on your computer cannot intercept the data that are used to generate your passwords.

Security experts say it is best to have different passwords for all the services you use. The reason for that is that every service can be hacked. When an attacker gets access to the passwords of a service he is able to try these passwords with other services. When you use one password for all services then losing your password means losing the control over all of these services.

Over time several schemas have been developed to prevent this by telling you how to generate a new password for every new service. One way that is used quite often is to find a really secure password and then attach a service-dependent information to it. So let us say your master password is "Very57r0ngP455w0rd" and you need a password for your online banking account then your service password could be something like "Very57r0ngP455w0rd_banking". There are several ways to get to the service-dependent part of the password. But this method has also a problem: As soon as an attacker learns the password of one service he can easily deduce the possible passwords of other services you use.

Another method is to generate and use totally random passwords. However, these are very hard to remember, especially when you have more than a few of them. Because of that password databases emerged that are encrypted with a master password and let you store and retreive all your precious random passwords. But when this database is lost you actually lose access to all of your services. In addition to that this database has to be available everywhere which led to strange solutions like putting all your passwords in the cloud where NSA, CIA and so on have access to it.

Finally XKCD proposed a password schema in a rather funny way. Their idea is to just put some random words together. This - according to them - is secure enough for most purposes. But even this has some problems. Several password forms enforce a maximal password length which greatly reduces the number of possibilities. And like before: Nowadays you have dozens of different accounts. For all of these accounts you would have to find random word combinations and you would have to find a way to link these to the actual service you are using the password for. So, which service would be "correct horse battery staple" for?

calc.pw works somewhat differently. It uses a master password and a service-dependent information from which it generates a service password with the help of cryptographic methods (SHA-1 and Arc4). From this service password neither the master password nor the service-dependent information can be deduced. Therefore an attacker who found one of your passwords is not able to generate the passwords of the other services you are using. If that happens you just have to change the compromised service password while all of your other services stay save.

Functionality

calc.pw is somewhat easy to use. Instead of connecting the keyboard directly to the computer you connect it to calc.pw and then you connect calc.pw to your computer. During startup calc.pw displays some information about itself.

Boot process A Boot process B Boot process C
(boot process)

When startup is complete calc.pw asks for the master password. This one is necessary to calculate the service passwords later on. The password you enter here will not be shown but it will be masked with asterisks instead. The arrow on the right side tells you whether a new character was added to the password or if it was removed by pressing the backspace key. During this mode only the red LED is switched on.

Password entry A Passwort entry B Password entry C
(password entry)

After you entered the master password by pressing the return key calc.pw asks for it again. By entering the master password for a second time you can make sure that you really entered it correctly. Only if both password entries match you will proceed to the next mode. If you enter a wrong password here you will start with the first password entry again. In this mode the yellow and red LEDs are switched on.

Repeated password entry A Repeated passwort entry B Repeated password entry C
(repeated password entry)

When both passwords match calc.pw switches into the usage mode. In the so-called passthrough mode calc.pw forwards all key presses directly to the computer without modifying them in any way. In this mode the green LED is turned on. By pressing the key combination CTRL+ESC you switch to the password generation mode. Here you are requested to enter the service-dependent information. You can use some password features when entering the information. The password features are described in chapter "Information". In this mode the green, yellow and red LEDs are turned on.

Passthrough mode Information entry A Information entry B Information entry C
(passthrough mode & information entry)

To finish the entry of the information there are two possibilities. The first one is to press CTRL+ESC again and the second one is to hit the return key. The difference is that pressing CTRL+ESC will just generate the password while hitting the return key will also send the return key to the computer. This comes in handy when filling out a login form as you do not have to press the return key again to finish the login process. During the calculation of the password the green and yellow LEDs are turned on. When the password calculation is done calc.pw automatically switches to the passthrough mode again.

Password calculation
(password calculation)

In addition to that calc.pw has a reset button. calc.pw will reboot when this button is pressed for a longer time. During this reboot the master password in the Arduino's memory will be overwritten. This is helpful when you have to leave calc.pw unattended and do not want others to be able to generate passwords with your master password.

Youtube video

Information

There are reasons why one way to generate passwords is not enough. There are for example password forms that require different password lengths. Some of them require special characters while others disallow special characters. There are also cases where at least one character and one number has to be in the password. To cover all of these requirements calc.pw provides three password features that can be used during the password generation.

Password length:
calc.pw allows you to define the length of the generated password. By default generated passwords are 8 characters long. To change the length of the generated password you have to put a question mark ("?") after the service-dependent information. The question mark has to be followed by a number that denotes the length of the generated password. The minimal password length is 3 and the maximal password length is 50 due to the limited memory of the Arduino platform.

Examples:

twitter?10
OnlineBanking?50
ultrakurz?3

Special characters:
Some password forms require the password to contain special characters. Which special characters are allowed can vary from form to form. Therefore it is not a good idea to predefine the characters that can be used for the passwords. Instead you can define which special characters can be used for every single password you generate. To do that you have to a append an exclamation mark ("!") to the service-dependent information. The exclamation mark is then followed by the list of special characters that are allowed in the password. One of these is selected during the password generation process.

Examples:

twitter!+-*/
OnlineBanking!+-*/!?#@&%$=
ultrakurz!!?

Alphanumerics:
There are login forms that require the password to contain at least one number and one character. To make sure that the generated password meets this requirement you can prepend the service-dependent information with a hash sign ("#"). This tells calc.pw to check for a number and a character in the generated password. If this check fails a new password is generated until the requirement is met. The hash sign resembles a Sudoku field that typically contains the numbers from 1 to 9.

Examples:

#twitter
#OnlineBanking
#ultrakurz

It is of course possible to combine those three password features. That means you can define the password length, activate the alphanumerics check and specify special characters all at the same time. However, you have to be aware of one thing: When you specify the length and define special characters the length always has to come first. The reason is this: All characters after the exclamation mark are handled like special characters. This would also include the question mark and the numbers following it.

WRONG: twitter!+-/?10
CORRECT: twitter?10!+-
/

Examples:

#twitter?10!+-*/
#OnlineBanking?50!+-*/!?#@&%$=
#ultrakurz?3!!?

Password generation

The password generation is based on several cryptographic primitives which in combination generate the actual output. Please take care of the order in which the parameters are supplied. After listing the primitives the algorithm of the password generation is shown in the form of pseudocode.

Primitives:

arc4(input, password) - encrypt with Arc4-drop1024
alphanum(input) - remove special characters
base64(input) - encode with Base64
check(input) - check for numbers and characters
concat(left, right) - concatenate strings
hash(input) - hash with SHA-1
hmac(input, password) - HMAC based on SHA-1
strip(input, length) - shorten the length of the input

For the password generation the following assumptions are made: The entered information was split into its single parts. checkAlphaNum contains the information whether the password has to be alphanumeric or not. info[] contains the service-dependent information, pass[] contains the master password, outLength contains the requested password length and specials[] contains the list of the requested special characters. The characters in specials[] are sorted by their position according to ISO-8859-1 and duplicates are not removed.

Sequence:

function generatePass(info[], pass[], checkAlphaNum, outLength, specials[]) {
  if (checkAlphaNum) {
    index = 0;
    do {
      temp = concat(index, info);

      result = singlePass(temp, pass, outLength, specials);
      if (check(result)) {
        break;
      } else {
        result = "";
        index++;
      }
    } while (index < 255);
  } else {
    result = singlePass(info, pass, outLength, specials);
  }

  return result;
}

function singlePass(information[], password[], len, specialChars[]) {
  hmacPassword = hmac(information, password);
  hmacInformation = "";

  tempInput = hash(hmacPassword);
  tempOutput = hmac(tempInput, information);
  hmacInformation = concat(hmacInformation, tempOutput);

  tempInput = tempOutput;
  tempOutput = hmac(tempInput, information);
  hmacInformation = concat(hmacInformation, tempOutput);

  tempInput = tempOutput;
  tempOutput = hmac(tempInput, information);
  hmacInformation = concat(hmacInformation, tempOutput);

  result = arc4(hmacInformation, hmacPassword);
  result = base64(result);
  result = alphanum(result);
  result = strip(result, len);

  specialCharIndex = 0;
  specialCharPos = 0;
  for (index = 0; index < length(hmacPassword); index++) {
    if (index < length(hmacPassword) / 2) {
      specialCharIndex ^= hmacPassword[index];
    } else {
      specialCharPos ^= hmacPassword[index];
    }
  }
  specialCharIndex = (specialCharIndex % length(specialChars));
  specialCharPos = (specialCharPos % (len - 2)) + 1;

  result[specialCharPos] = specialChars[specialCharIndex];

  return result;
}

Buildup

calc.pw was developed with the help of a so-called microcontroller. The used Arduino platform is an Open Source hardware platform. All sources including the API and the hardware specifications are available online. The advantages of this platform are its prevalence in the market, the extensive documentation, the availability of additional hardware and its easy availability here in Germany.

The project evolved in several steps. In the first step (revision A) only the program logic was implemented. Therefore this revision can be built with all regular Arduino boards. The communication with the hardware is done through its serial interface. The master password and the information are sent through it and the generated password is received through it as well.

In the second step (revision B) the communication between the Arduino and the computer was changed. Instead of using a serial interface the input into the Arduino is now performed through a USB keyboard. To accomplish this the USB-Host-Shield from Circuits@Home is used. You should really consider to use this shield as the original library is developed for it. Additionally the output of calc.pw to the computer is now performed through a keyboard emulation. To do this the Arduino Leonardo has to be used as it provides such a functionality.

Finally a LCD was introduced in the third step (revision C) for a more advanced communication with the user. This leads to a much easier usage as you can tell from reading the display which mode calc.pw currently is in. Below you can see a build schema of the revision C. Unfortunately the USB-Host-Shield could not be displayed in this schema:

Buildup schema
(buildup schema)

The following components are necessary to build calc.pw:

1x Arduino Leonardo
1x Liquid Crystal Display
1x Light-Emitting Diode (yellow)
1x Light-Emitting Diode (green)
1x Light-Emitting Diode (red)
1x Potentiometer (10k ohms)
1x Pushbutton
4x Resistor (220 ohms)
1x USB-Host-Shield

The Arduino Leonardo is the heart of the hardware as it performs the actual program logic. Its keyboard emulation functionality acts as the output method to the computer. The USB-Host-Shield is used to connect the USB keyboard to the Arduino so that it can be used as an input device. The pushbutton is used as a reset button through which the status of calc.pw can be overwritten. The LEDs are used as a simplified status display. The LCD is used as a more advanced status display. The potentiometer is used to regulate the contrast of the LCD and the resistors are used for the LEDs and the LED of the display.

If calc.pw behaves in an unusual way this may be because your keyboard draws too much current. Connect calc.pw to a power outlet to provide it with more current. The USB port of a computer does not provide more than 500mA.

Development

calc.pw was developed with portability in mind. The backend library "cryptuino" can be compiled for the Arduino platform as well as other Little Endian platforms like x86 and x64 systems. Support for Big Endian systems has been prepared but could not be implemented due to a missing test platform.

The core functionality including the algorithms for the password generation has been encapsulated in the "cryptuino" library. It includes a memory manager, cryptographic functions (SHA-1 and Arc4) and a compatibility layer for PROGMEM.

Due to the platform independence there are not only the hardware revisions A to C but also a so-called software revision 1 which compiles for Windows as well as for unixoid systems. It generates the same passwords when provided with the same master password and information.

Revision 1 on macOS Revision 1 on Windows
(revision 1 on macOS and Windows)

For the USB connectivity the USB-Host-Shield-Library developed by Circuits@Home was used. An additional event was added to handle control key presses. Due to a problem with the keyboard emulation of the Arduino Leonardo the Arduino core had to be modified. This allows us to forward keyboard input to the computer without modifying the input.

In addition a mechanism was implemented which allows calc.pw to be used with different keyboard layouts. To do this the EEPROM of the Arduino is used. A layout file is written to it which serves as a translation table beween the scancodes of the keyboard and the characters of the ISO 8859-1 encoding.

Keyboard layouts

On three occasions during the program execution calc.pw has to translate from the encoding keyboards are using to the ISO 8859-1 encoding. To do this a lookup table is needed. However, the Arduino does not have enough memory to store such a table for every keyboard variant there is (which are dozens besides the German QWERTZ keyboard, the American QWERTY keyboard, the British QWERTY keyboard, etc.). Instead, calc.pw has the ability to flash a keyboard layout to its EEPROM storage. Keyboard layouts have a certain structure which will be shown in the following example.

Example:

63616C632E7077 00080002 01 000000

A keyboard layout is always represented in uppercase hexadecimal encoding. The example consists of several elements. A keyboard layout starts with a header which consists of the ISO 8859-1 encoded text "calc.pw" ("63616C632E7077"). Then follows an Adler-32 checksum ("00080002"). It is calculated for all the data that follow. Next comes a length field ("01") that defines how many key combinations this keyboard layout consists of. And finally there are the key combinations ("000000"). The first two numbers represent the scancode according to the USB HID Usage Table for keyboards. The second two numbers represent the modifier and the third two numbers give the ISO 8859-1 representation of the character.

These values are valid for the modifier element:

00 - no control key is pressed
01 - AltGr (or alternatively Ctrl+Alt) is pressed
02 - Shift is pressed
40 - NumLock is active (for the numpad)
80 - the pressed control keys are not relevant (Space, Enter, Tab, Backspace)

To give you a more functional example the next picture shows the keyboard layout of the German QWERTZ keyboard. It consists of the header ("63616C632E7077"), of the checksum ("00EB00D7"), of the length field ("81") and of the key combinations ("040061 040241...").

QWERTZ layout
(QWERTZ layout)

On startup and after having been reset calc.pw checks whether a valid keyboard layout has been flashed to the board. If yes, it proceeds normally. If not, a corresponding error message is presented. This may be the case when you start calc.pw for the first time or when the last flash attempt failed. This is nothing to worry about because flashing a new keyboard layout is relatively easy.

Error message due to missing layout
(error message due to missing layout)

To flash a new keyboard layout onto calc.pw it has to be set into flash mode. To do this just press the reset button and keep it pressed until the red and green LEDs light up at the same time. At this point the display tells you that calc.pw has switched to flash mode and is now waiting for a new keyboard layout. In flash mode calc.pw opens a serial interface with a transfer rate of 9600 baud. This interface has to be used to provide the keyboard layout.

Flash message A Flash message B Flash message C Flash message D
(flash messages)

calc.pw also uses the serial interface to provide you with the current status of the flash process. After flashing is complete (whether successful or not) calc.pw resets itself. If the flash process was successful you can now start using calc.pw. If it has not been successful it is best to power off calc.pw before starting a new attempt on flashing a keyboard layout.

Layout flashed
(layout flashed)

Youtube video

Downloads

The Arduino IDE and the necessary Windows drivers can be found on Arduino.cc. To compile the software revision 1 you can use Netbeans from Netbeans.org, Code::Blocks from CodeBlocks.org or any other IDE you like. The library for the USB-Host-Shield developed by Circuits@Home can be found on GitHub.com. And here you learn how to use libraries like "cryptuino" in the Arduino IDE

cryptuino v0.4.0 (cryptuino_0.4.0.zip)
Revision 1 v0.4.0 (rev1_0.4.0.zip)
Revision A v0.4.0 (reva_0.4.0.zip)
Revision B v0.6.0 (revb_0.6.0.zip)
Revision C v0.6.0 (revc_0.6.0.zip)

QWERTY UK Keyboard Layout (qwerty_uk.txt)
QWERTY US Keyboard Layout (qwerty_us.txt)
QWERTZ DE Keyboard Layout (qwertz_de.txt)

Revision 1 (Windows Binary) (rev1_0.4.0_windows.zip)
Revision 1 (macOS Binary) (rev1_0.4.0_macosx.zip)
Revision 1 (macOS Binary with Ncurses) (rev1_0.4.0_macosx.zip)

Adler-32 Checksum Calculation (adler32.zip)

All source codes published on this page are licensed under the GPLv3 license. If you need another license do not hesitate to contact the developer of this project.

Contact





Data Privacy Statement
For the correct functioning of this contact form, the personal data entered by you must be transmitted to the operator of this website. By using the contact form, you consent to the transmission and storage of the personal data you have entered. The data will be used to respond to your contact request.

The message could not be sent!
The message has been sent!

mandatory fields are marked with an *