Number: SLIP-0016
Title: Format for password storage and its encryption
Type: Standard
Status: Draft
Authors: Peter Jensen <[email protected]>
Created: 2016-18-02
SLIP-0016 describes simple encryption concept for hardware device for secure storage of passwords.
At first, we derive a master key from HW device itself, which is divided in two parts. First part is hashed and used as a name of storage file. Second part is used for primary storage encryption.
Storage file is encrypted JSON object, which contains configuration, tags and separate entries. Each entry has other two encrypted properties derivated from device to provide higher level of security with low risk of leaks.
We derive masterKey from hardware device by sending cipherKeyValue with following params:
- path:
m/10016'/0
(hardened path, see BIP32) - ENC_KEY:
'Unlock encrypted storage?'
(max length message is 256 bytes) - ENC_VALUE:
'2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee'
(in hexadecimal (128 /2), max length is 1024 bytes) - encrypt:
true
- ask_on_encrypt:
true
- ask_on_decrypt:
true
- iv: unset
JS EXAMPLE:
session.cipherKeyValue(
[(10016 | 0x80000000) >>> 0, 0],
'Unlock encrypted storage?',
'2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee',
true, true, true)
CipherKeyValue is defined in SLIP-0011.
From the first half of master key, we derive the file name for every user/device in the following way: First, we use the HMAC function:
HMAC-SHA256(fileKey, FILENAME_MESS)
where:
- fileKey is the first half of masterKey (
masterKey.substring(0, masterKey.length / 2)
) - FILENAME_MESS is a constant string
'5f91add3fa1c3c76e90c90a3bd0999e2bd7833d06a483fe884ee60397aca277a'
The output result is digested to HEX string. After, we append extension '.pswd'
EXAMPLE RESULT: a80387a2222f4360f71fd2165368c6ed91b26287d9bc1ce8be71e64e6b216a4f.pswd
As an encryption key is used the SECOND half (32 bytes) of master key for the first level of data file encryption. Encryption key is in HEX string.
For encrypt/decrypt we are using AES-256-GCM
algorithm.
- Input Vector (IV) is 12 randomly generated bytes
- GCM is used with full 128-bit autentication tag (authTag)
The result output stored in file is:
- first 12 bytes of the file is randomly generated IV
- next 16 bytes is the GCM authTag
- the rest is output ciphertext
(Decrypted) data file is serialized JSON object with the following keys:
version
: for future backwards compatibility and data storage manipulationconfig
: for remembering personal setup of applicationtags
: contain set of labels with their icons (from icomoon set). Default tag is All and it is only tag, unable to edit or delete.
{title:"My social networks", icon:"person", active:"active"}
entries
: is object of all password entries encrypted second time
{
"title": "http://wwww.github.com",
"username": "Satoshi Nakamoto",
"nonce": "8688105887642a3cbb61889d8762432ef864df107e097d2b19e93c8d808c2e21",
"note": "public note",
"password": {},
"safe_note": {},
"tags": [1]
}
Every entry contains keys from upper example.
title
: title is represented as string. If given string is matching URL, it will be shown on device as domain without protocol prefix.username
: string, will be passed to device, in encryption/decryption processnonce
: hidden generated string which is output of cipherKeyValue over Title + Username key and random valuespassword
: is buffer array output of plain string and nonce (encryption process described later)safe_note
: is also buffer array output of plain string and nonce (also described later)note
: is plain UTF8 stringtags
: is array of Tags key values
Step by step entry encryption:
- Generate random 32 bytes buffer and convert to HEX string inadequately called
nonce
- Set key as
'Unlock ' + title + ' for user ' + username + '?'
- Ask device for
cipherKeyValue
, where path is the same as in the deriving file name, key is described in second step and enc_value is ournonce
from the first step. Do not forget to setup properly other three bool values!
EXAMPLE:
session.cipherKeyValue(
[(10016 | 0x80000000) >>> 0, 0], // same path
'Unlock github.com for user Satoshi Nakamoto?',
'2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee',
true, //encrypt? - has to be TRUE in encryption
false, //askOnEncrypt? is the same in encryption and decryption
true) // askOnDecrypt? we want this becuase otherwise somebody could rob us!
- Then we use our famous
nonce
from the first step inAES-256-GCM
algorithm encryption forpassword
string andsafe_note
string. Process of encryption is the same as in the deriving encryption key and file level encryption. So basically we get some Buffer array output with 12 bytes of IV and 16 bytes of GCM authTag and the rest is cipherText. - Output of each encryption is stored to appropriate keys, just instead of generated
nonce
we store result from third step (cipherKeyValue
) which we later use for decryption process
- We ask device for the same
cipherKeyValue
as in encryption process, just instead ofnonce
, we use our encrypted result and boolean valueencrypt?
is false!
EXAMPLE:
session.cipherKeyValue(
[(10016 | 0x80000000) >>> 0, 0], // same path
'Unlock github.com for user Satoshi Nakamoto?',
'8688105887642a3cbb61889d8762432ef864df107e097d2b19e93c8d808c2e21',
false, //encrypt? - has to be FALSE in decryption
false, //askOnEncrypt? is the same in encryption and decryption
true) // askOnDecrypt? we want this becuase otherwise somebody could rob us!
- Other steps are the same as in entry encryption, we just symetrically decrypt values of
password
andsafe_note
viaAES-256-GCM
algorithm. Size of IV and authTag for AES is the same as in encryption. Beware on cipher Key data type - it must be hex. Output is in JSON.
Check example of password reader implementation in python pwdreader.py there is an example code for decryption.