Skip to content
Open hardware wallet. Supports Arduino AVR / ESP/ STM chips (atmega328 included), Emercoin / Bitcoin / Ethereum etc compatible
C++ C
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

openHW project 0.5

Open hardware wallet. Supports Arduino AVR / ESP/ STM chips (atmega328 included), Emercoin / Bitcoin / Ethereum etc compatible

Made by Denis

I welcome the use of this code, or parts of it, in any application that enhances the security and privacy of users. Feel free to ask questions. The code is based on a number of libraries listed in the corresponding section of this document.

The project is being developed with the support of the Emer community as a part of the Emer's infrastructure


openHW is an open-sourse free-licensed project aimed at developing a universal hardware wallet code that supports a wide range of computer chips (including the weakest ones) as well as a wide range of popular boards The main emphasis is on the simplicity of program code integration, simplicity of building and configuring. However, the cryptography used to preserve sensitive data is strong enough to ensure the security of the data at an industrial level if the wallet used correctly.

One of the scenarios for using this code is when the end user buys the device compatible with the code and flashes it himself. An alternative is to buy a device specially designed for use with this firmware, but with the subsequent flashing of the code by the user himself.

This important point provides the user with sufficient assurance that the device does not have built-in backdoors allowing the attacker who sold it to gain access to sensitive information.

We believe that only open-source firmware and self-flashing on independent devices can provide sufficient security guarantees today.

How to use

The ready-to-use device with firmware is connected to a computer or smartphone (hereinafter - the Host) via USB cable or via Bluetooth (if supported by the choosen board)

Host must have openHW-compatible software installed (e.g. EmerAPI KeyKeeper app). The device can be automatically detected or set in the program settings on the host.

At the first connection the user is offered to load his private key (and optionally the second private key for the Plausible deniability scheme) into openHW After the private key(s) is loaded, it is proposed to create a PIN code to protect the private key in case the device is stolen (and, optionally, a second PIN to be reported as a real)

Optionally, a Private Key Encryption Password can be set, which binds openHW to a specific host and makes it impossible to obtain the Private Кey in case the device is stolen.

Further, to perform any operations that require the use of the private key (signing transactions and messages, initiating secure ECDH channel, encryption and decryption) it will be necessary to connect the device to the host and enter the PIN Optionally, the user can be required the hardware button to be pressed (if it exists on the selected board)

When operating without a Password Encryption scheme, the key can be used on any host that has compatible software installed. With the Encryption Password scheme, the key can only be used with one host. If the host is broken or stolen, the device will not be able to use the key (you will need to re-program the private key on the device)

The Password Encryption scheme is preferred for storing valuable information. Don't store the openHW device and the host device in the same place, as stealing both devices can probably allows the Intruder to gain access to your Private Key.

Multiple openHW devices can be mapped to a single host; one openHW device can be paired with multiple hosts.

Attention! DO NOT use the openHW device as the only place to store your private key. Be sure to keep a copy of the master password on paper in a safe place (can be stored in parts).

How it works

Private key storage and protection

The private key is stored in the EEPROM (NVS when using ESP chips) of the microcontroller and never leaves it. There are two storage modes

  1. The key is stored as is. In this case, if the device is stolen, it is technically possible to physically open the chip and read the key.
  2. The key is encrypted in a crypto-resistant way based on the secret (Encryption Password) stored on the bound host. This password is generated by the host when initializing the wallet and is never entered manually.

All cryptographic operations with the key are performed inside the chip. Thus, even if the computer is under the control of a malefactor, the interception of a private key becomes complicated.

In addition, the chip stores a PIN (short string of letters and numbers, 4 or more characters are recommended).

To access sensitive information, the device must be given the correct PIN The private key is set when initializing the wallet with the SetPrivateKey() command. As a second parameter, a second private key can be passed to this function for using in the PD scheme.

The data exchange with the host

The openHW device is controlled by the host by sending text commands. This can be done via a serial port, via USB (USB serial port), if the board has a Bluetooth module and this function is supported in the firmware - via a Bluetooth connection. Commands and data can be ended by line feed characters or be send as a unbreakable stream.

The device responds with strings ending with \n This is either a data string (with an answer to a request) or a string starting with one of the signatures: "OK: " (success), "ERROR: " (error), "PIN: " (PIN request), "PASSWORD: " (Password request), "BUTTON: " (a request of pressing of the device's hardware button) More detailed information on the commands can be obtained by sending the command "help" to the device The default port speed is 115200

Plausible deniability (PD) scheme

In order to protect the user from the violent demand for a PIN code (from criminals or public authorities), a second private key (PD Private Key) and a second PIN code (pin2) support is provided If the criminals force require access to the device, the user disclosures the PD pin (pin 2)

The first time you enter it, the device switches into PD mode. In this mode

  • The device can be unlocked by PIN2 and the PD private key will be used
  • There is no way to determine that the PIN is a PD-PIN, not a normal PIN. The behavior of the device is identical to normal.
  • The correct pin must be entered no earlier than five days after the power is supplied to the device in order for the device to switch into the standard mode. If the correct PIN is entered earlier, the device reacts to it as if it was an erroneous.

For complete reliability we recommend to create a fake activity on the PD address - to transfer coins to it, to create some digital assets.

Do not disclose the fact of PD scheme usage to your friends, partners and loved ones for their own protection and your safety.


  1. No chip guarantees the security of your data when it is stolen. The system uses a PIN code that is strong enough to protect the data (if the instructions are followed) in case the chip is not physically opened. However, any chip can be hacked with special equipment and the data can be read. All existing devices on the market are exposed to this vulnerability. The only difference is the complexity of their hacking. Using a password (in addition to the pin, computer + device mode) solves the problem in many ways, but creates the risk of losing access to your data if your computer is damaged

  2. Chips that are most vulnerable to physical hacking Protecting the memory of some chips at the moment can be easily hacked by physically exposing the chip (costs around USD$1000 with 50% chance of success) Storing your private keys on these chips is comparable in security to storing paper with a password in a locked box. If this chip is stolen, all assets should be immediately transferred to another private key and the old one should not be used. Below is a list of unsafe chips: Atmel Atmega*** STM32F1xx STMicroelectronics STR7xxxxx

Chips that are not known to be unsafe at the time of writing this document: ESP32 ESP8256

How to compile

First, you should purchase any board with an avr/stm/esp chip board supported by the Ardiuno development environment. It can be implemented as a USB token, a separate board or otherwise. I recommend using ESP chips (esp32, esp8266) It will be easier if you choose a board on which the firmware has already been tested. The list is given at the beginning of the main firmware file openHW.ino In this case you just need to uncomment (remove "//" from the beginnig) on the line corresponding to your board. For example, if you purchase a Heltec WiFi Kit 32 board ( you will need to change line "//#define board_Heltec_WiFi_Kit_32" to "#define board_Heltec_WiFi_Kit_32", removing firts two symbols

this test is also repeated in openHW.ino file

  1. Intall Ardino board framework
  2. Install additional libraries for you board: Go to File > Preferences : Enter library json URL into the “Additional Board Manager URLs” and click "ok"
  1. install micro-ecc library: go to Sketch=>Include Library=>Manage Libraries; find uECC library and install it

  2. Perform additional steps (you should restart arduino application after the changes): ---For AVR (Arduino UNO, etc):--- A.1. change #define uECC_SUPPORT_COMPRESSED_POINT 1 to #define uECC_SUPPORT_COMPRESSED_POINT 0 in uECC.h A.2. make modification to the board's settings for protect the memory and increase buffer size:

    1. locate your arduino directory (in windows, it is C:\Program Files\arduino...)
    2. copy the core directory \hardware\arduino\avr\cores\arduino to a different name, for example \hardware\arduino\avr\cores\arduinohw
    3. edit the hardware serial file in the new folder. It could be called HardwareSerial.h or USBAPI.h depends on version you must add two lines on the beginning: #define SERIAL_TX_BUFFER_SIZE 129 #define SERIAL_RX_BUFFER_SIZE 150
    4. open \hardware\arduino\cores\arduino\avr\boards.txt find your board (for example, Arduino UNO) (the section will be started from " Uno") copy the whole section and rename all preffixes to the new one for example, "uno" -> "unohw" change in the new section: 1: .name parameter (for example, " Uno" will be " HW" 2: .build.core change to the folder you created before (for example, "" will be "")
  3. Now let's make changes in the main file 5.1. Set your board in FIRMWARE SETTINGS section in the openHW.ino file. (for example, if you board is Heltec ESP32 you should delete "//" in the line "//#define board_Heltec") If your board is not listed, you should configure it below in the boards ection or do nothing (screen and buttons will not work in this case) 5.2. Make sure that DEBUG option is turned off if you are not a developer.
    This is in the second section right after the board selection. if you see the row "#define DEBUG 1" - change it to "//#define DEBUG 1" otherwise it will be possible to see your private keys just using command getConfig()

  4. Connect your board. Select COM port and your board type in Tools menu. If you created a new platfor (recommended) you have to choose it.

  5. Press arrow (flash) button to flash the board.

  6. Check if firmware works

  7. Protect EEPROM memory data from firmware changes (and do other protection thinks) ---For AVR (Arduino UNO, etc):--- A: You will need an additional device to make less or more good protection. It costs less than usd$1 first, you have to set EESAVE = 1 (off, there 1 means off) to prevent EEP reading using a changed firmware second, it would be a good to change DWEN, SPIEN and RSTDSBL (or only DWEN and RSTDSBL) bits. DWEN = 1(off); SPIEN=1(off); RSTDSBL = 0 (on). The last two options will make firmware updating impossible

    For most of the available Arduino boards this point can be omitted (the system security will be reduced, be sure to use the Password, if you lose the key - remove the paring on the host with it), because by default the memory cleaning when flashing is enabled and debugging is disabled. You can easily check this by flashing the device, setting a private key and flashing it again. If the private key is disappeared and the key returns to the uninitialized state - that's okay, changing EESAVE bit is not required.

    if you not pass this step you private key can be read from the chip easily BE VERY CAREFUL HERE, YOU CAN BRICK YOUR BOARD universal calculator:

    A partial solution may be to buy a board with the EESAVE flag sets to 1 (off). It is a default value for many boards. In this case if intruders wants to read your key he must at least disassembly device and use additional equipment. DWEN is usually turned off (1) by default.

Protocol usage example

this test is also repeated in openHW.ino file

">>" means we send the text to the device (do not type ">" symbol itself

"<<" means the device's answer (do not type "<" symbol itself

You can communication with the device using text commands, which can be ended by a line feed character or be sent by a solid stream. the device's answers will be ended with \n char

the device port will be COMnn (for example, COM5) on windows, ttySnn or ttyUSBnn on linux/unix (depends on board) the default speed is 115200; 8 N 1

The following examples use Emer (Emercoin) address formats, but this will also work with Bitcoin or Ethereum addresses (and others)

Checking connection

use it for check if you are connected to the device

>> helloHW

The result must be started from "HELLO OPENHW" string

Initial setup:

First, we have to set up private key(s). In this example we will set the both main and PD private keys. You can perform this procedure on a device that has just been programmed or on a device that is already set up (it will be reset)

>> setPrivateKey(KxTjwqzzZjYfXEY1JsaywDXYkWPjTQc9MynkAdJbj78Aki4b9wEg,L3VdUrEEAeKwvyxZ81XkVxBGwiM5QxTXuUrrMJcr4pN6u8hV37om)
<< OK: Both keys were assigned

This command sets the main Private Key of the KxTjwqzzZjYfXEY1JsaywDXYkWPjTQc9MynkAdJbj78Aki4b9wEg (this key corresponds to the master password of the KeyKeeper program "1" s/0/0 HD and to the address EMYEfuf21PyEt4GfBpoZkEWhaQNAW6GXQR ) and PD Private Key L3VdUrEEAeKwvyxZ81XkVxBGwiM5QxTXuUrrMJcr4pN6u8hV37om (Master Password "2", EYjzaPvKAxCfVvztLFv2s4qqYhyf1F9NVB )

For bitcoin and default electrum BIP39 salt ("mnemonic") it will be the same: setPrivateKey(KzXvca32X4NaxNcKYAQ6iduf3PRZsAezLczPLvfHu96jw7jjTB5D,KxwtfCyJJfFkZSSjSP67F8ejRZQYZExRAZmznbKxC9z9MNsAhVMh) for addresses 1Fdy5g8mG1sGmQb39HhAYSTjYpfHztouFh and 13W9cELWjSd6bHstr4hkmyhGZ26r46rKKy

So, the two private keys - the main one and the one to be provided under the forcible demand - are set. Now you need to set the PIN codes: set main pin: 9876

>> setPin(9876)
<< OK: PIN has been set up

ok. now let set PD pin: 123

>> setPin2(123)
<< OK: PIN has been set up

ok, the initialization has been done. Was it difficult?


Retrieving the Public Key and determining address:

Getting the current address is easy, just run the getPublicAddress command If the device has been locked, you will need to enter the PIN.

>> getPublicKey
<< BAB1473EED46A7266430CFA69F48184481B0D3B30DF9964CF9E3055D230B9D5B6817D514B7ED40F0DB48A721AE2C5DF301EE8066263237B42F506932958A4CB0
  • The key is uncompressed. For determine the address, we must compress it first: just drop second part (Y coord) of the key and add 02 because Y is odd. The compressed will be 02BAB1473EED46A7266430CFA69F48184481B0D3B30DF9964CF9E3055D230B9D5B .
  • Calculate RipeMD160(sha256(02BAB1473EED46A7266430CFA69F48184481B0D3B30DF9964CF9E3055D230B9D5B)) = 3019A9CD558E4F9A3E197A1D7DF5BEF71522BBA2
  • Add network signature (0x21 for Emercoin): we get 213019A9CD558E4F9A3E197A1D7DF5BEF71522BBA2
  • Convert it to the code58check encoding: we get EMYEfuf21PyEt4GfBpoZkEWhaQNAW6GXQR

Signing transactions

First of all, the application on the host must calculate hash to sign.

This must be done one by one for each TX input that requires a signature.

Next, the resulting hash is passed to the device using the signMessage([,]) command.

>> signMessage(E3FFCC983D047EAB781C31AFA21B71AD15393C3B3EBD9944F278652F85E18266,1)
<< 0589B5F4E91C0E2B9C0767BF9ECAA7E9993C8663823029DF6C4D345EEBF8B175169C75B1BCED40C11B187B6B751062A15065DEA1EFD26FF8421B5335D9E9ABA9

The result is the Input signature (R and S). You can conver it into DER format yourself.

Other operations

-------changing PD PIN-------

now let set PD pin: 123. Setting the PIN2 requires the main PIN to be entered, which will be requested

>> setPin2(123)
<< PIN: please provide your pin

An important remark. We could have avoided the second iteration of the PIN entering by introducing the PIN right after the command. Then we would have to enter just "setPin2(123)9876".

If the device had been unlocked, no PIN entry would have been required.

ok. enter the pin:

>> 9876
<< OK: PIN has been set up

ok, done

-------just unlock the device with pin 9876-------

>> unlock9876
<< OK: locked

-------unlock the locked device in the dialog-------

>> unlock
<< PIN: please provide your pin
>> 9876
<< OK: unlocked

-------lock it-------

>> lock
<< OK: locked

-------get settings-------

>> getConfig()
<< OK: openHW configuration:
<< Name:
<< Ask pin every time:0
<< Lock pin after (ms):0
<< Button mode: use only for unlock pin
<< Sig:21

-------get current device state-------

>> status


arduino libraries micro ecc library (you need install it into your system) for sha256 hashing used (with modifications) (included)


This "Software" is Licensed Under BSD 2-Clause "Simplified" License .

Version history


one board support added

password support

hardware button support


  • Solve the password salt problem to close the privkey0 decryption vulnerability knowing privkey1
  • settings ( pinlock, pinmode... )
  • bluetooth support for ESP32


solved buffer problem on AVR

ECDH added

manuals improved

bugs fixed


  • Password support
  • setPinMode, setPinLockDelay, setButtonMode support


minor changes

arduino memory problem solved (-Og added... this is only way)


Fully functional (?).


  • ECDH
  • JTAG disable / EEPROM erase manual
  • Password support
  • setPinMode, setPinLockDelay, setButtonMode support


Initial version

You can’t perform that action at this time.