This file explains how wallet encryption should be implemented. In general we are using AES 256 in CBC mode.
When trying to unlock wallet, password should be obtained from the user. Key for AES encryption is derived from password using sha256
hash function, salted with bytes which are hard-coded into all ElectronPass applications.
To imagine how hard is to break
sha256
hash, see this video
After the key has been generated, password string should be deleted, so that the chance of stealing user's password is minimal. Even key, derived from user's password should not be ever written to a disk.
IV must not be reused for two message encryptions with the same key. Therefore IV should be randomly generated, using a cryptographically secure pseudorandom number generator. When using 32 byte IV, 2^256
different IVs can be generated, which means that chance of collision for two following encryptions is less than 10^-77
.
We are aware of famous Birthday problem, so just for fun consider this: if you had
1 000 000
computers since the beginning of the Universe, and each of them generated10^9
random IVs every second, then probability of two IVs being same would be lower than10^-12
.
Since AES CBC is a block cipher, the data length needs to be a multiple of the block size (16 bytes in the case of AES). Therefore data needs to be padded before the encryption. We are using PKCS#7
padding.
We are using AES with CBC mode for encryption. It accepts the 256 bit key generated from the password. This mode also needs Initialization Vector. After we generate the key and IV vector and apply the PKCS#7 padding to the wallet, we encrypt it.
For authentication we hash the encrypted wallet and the key with sha256
. Pseudocode on that is shown bellow:
signature = sha256(encrypted_wallet + key)
Now that we have everything encrypted and we have generated the signature, we can put it all together into one string. The string is generated by appending the signature and iv to the encrypted wallet. Since all of these things are arrays of bytes, we can't really save them to a file. For that we first convert it to Base64. Pseudo code for this process:
encypted_wallet = base64(encrypted_bytes + signature + iv)
where encrypted_bytes
is the result of AES256 encryption, signature
is the wallet signature and iv
is the iv used for encryption. encrypted_wallet
is the string that gets saved to disk.
For decrypting the wallet, we first convert it from Base64 back to plain array of bytes, then we can extract the IV, signature and the actual encrypted data from that array. When the user wants to unlock a wallet with a password, we generate the key from that password as described above. Then we generate another signature by hashing the encrypted data and the newly generated key with sha256. If the signature extracted from the wallet and the generated signature are not the same, that means one of two things:
- Someone has changed the encrypted wallet
- The user has typed in the wrong password Since the latter is a lot more probable, we display the error as a wrong password.
If the signatures match, then we can just decrypt the encrypted data with the provided key and previously extracted IV.
When the wallet is locked, all sensitive data should be deleted (this includes AES key and also all data stored in wallet). We also recommend that you don't store the password in the RAM, but after the user types it in immediately generate a key and discard the plain password.