# SLIP-0011 : Symmetric encryption of key-value pairs using deterministic hierarchy ``` Number: SLIP-0011 Title: Symmetric encryption of key-value pairs using deterministic hierarchy Type: Standard Status: Final Authors: Pavol Rusnak Marek Palatinus Karel Bilek Created: 2014-06-12 ``` ## Abstract This document is explaining symmetric encryption on hardware devices, using deterministic hierarchy. ## Motivation We want to provide a symmetric encryption in the hardware wallet, where the key doesn't exit the device, and where the user might be forced to confirm the encryption/decryption on the display. ## Body ### Overview The following data are sent to the hardware wallet: * BIP 32 path * key (that is being shown on the device) * value * encrypt/decrypt direction * should user confirm on encrypt? * should user confirm on decrypt? * optional IV Value is what is actually being encrypted. The key for the encryption is constructed from the private key on the BIP address, the key displayed on the device, and the two informations about whether to ask for confirmation. It is constructed in such a way, that different path, key or the confirm information will get a different encryption key and IV. So, you cannot "skip" the confirmation by using different input. IV can be either manually set, or it is computed together with the key. The value must be divisible into 16-byte blocks. The application has to pad the blocks itself and ensure safety; for example, by using PKCS7. ### Details The details are best explained on a slightly simplified code from TREZOR Python emulator. ```python def _cipher_keyvalue(self, address_n, key, value, encrypt, ask_on_encrypt, ask_on_decrypt, iv): if len(value) % 16 > 0: return Failure(message="Input length must be a multiple of 16") private_key = BIP32(self.storage.get_node()).get_private_node(list(address_n)).private_key key += "E1" if ask_on_encrypt else "E0" key += "D1" if ask_on_decrypt else "D0" secret = hmac.HMAC(key=private_key, msg=key, digestmod=hashlib.sha512).digest() aes_key = secret[0:32] aes_iv = iv if iv else secret[32:48] aes = pyaes.AESModeOfOperationCBC(key=aes_key, iv=aes_iv) if encrypt: res = ''.join([aes.encrypt(value[i:i+16]) for i in range(0, len(value), 16)]) else: res = ''.join([aes.decrypt(value[i:i+16]) for i in range(0, len(value), 16)]) return CipheredKeyValue(value=res) ``` ```python if len(value) % 16 > 0: return Failure(message="Input length must be a multiple of 16") ``` First, the value is checked, if it is divisable into 16-byte blocks, since the symmetric cipher is block cipher. The application has to pad the blocks itself and ensure safety; for example, by using PKCS7. ```python private_key = BIP32(self.storage.get_node()).get_private_node(list(address_n)).private_key ``` Private key of the BIP32 node is derived. ```python key += "E1" if ask_on_encrypt else "E0" key += "D1" if ask_on_decrypt else "D0" ``` The key, displayed on the device, is concatenated with either E1 or E0 and either D1 or D0, depending on whether the confirmation is enabled in a given direction. ```python secret = hmac.HMAC(key=private_key, msg=key, digestmod=hashlib.sha512).digest() ``` The key, with the concatenated E1/E0 or D1/D0, is HMACed, with the private key from HD Node, with SHA512 as a hash function. ```python aes_key = secret[0:32] aes_iv = iv if iv else secret[32:48] ``` The AES key is the first 32 bytes of the HMAC; the input vector is the next 16 bytes. ```python aes = pyaes.AESModeOfOperationCBC(key=aes_key, iv=aes_iv) ``` The algorithm is AES, in CBC mode. ```python if encrypt: res = ''.join([aes.encrypt(value[i:i+16]) for i in range(0, len(value), 16)]) else: res = ''.join([aes.decrypt(value[i:i+16]) for i in range(0, len(value), 16)]) ``` The result are the encrypted/decrypted blocks, concatenated together. ## Test Vectors Check [test_msg_cipherkeyvalue.py](https://github.com/trezor/python-trezor/blob/master/trezorlib/tests/device_tests/test_msg_cipherkeyvalue.py) for the test vectors. ## References The algorithm is implemented in [TREZOR firmware](https://github.com/trezor/trezor-mcu/blob/master/firmware/fsm.c) (function `fsm_msgCipherKeyValue`) and its [emulator](https://github.com/trezor/trezor-emu/blob/master/trezor/machine.py#L781) (function `_cipher_keyvalue`). It is used in algorithms described in [SLIP-0015](slip-0015.md) and [SLIP-0016](slip-0016.md).