2022-10-28 09:13:05 +00:00
{. used . }
2024-03-15 23:08:47 +00:00
import std / [ json , os ] , stew / byteutils , testutils / unittests , chronos , eth / keys
import .. / .. / waku / waku_keystore , . / testlib / common
2022-10-28 09:13:05 +00:00
2023-08-09 17:11:50 +00:00
from .. / .. / waku / waku_noise / noise_utils import randomSeqByte
2022-10-28 09:13:05 +00:00
suite " KeyFile test suite " :
test " Create/Save/Load single keyfile " :
# The password we use to encrypt our secret
let password = " randompassword "
# The filepath were the keyfile will be stored
let filepath = " ./test.keyfile "
2024-03-15 23:08:47 +00:00
defer :
removeFile ( filepath )
2022-10-28 09:13:05 +00:00
# The secret
var secret = randomSeqByte ( rng [ ] , 300 )
# We create a keyfile encrypting the secret with password
let keyfile = createKeyFileJson ( secret , password )
check :
keyfile . isOk ( )
# We save to disk the keyfile
saveKeyFile ( filepath , keyfile . get ( ) ) . isOk ( )
# We load from the file all the decrypted keyfiles encrypted under password
var decodedKeyfiles = loadKeyFiles ( filepath , password )
check :
decodedKeyfiles . isOk ( )
# Since only one secret was stored in file, we expect only one keyfile being decrypted
decodedKeyfiles . get ( ) . len = = 1
# We check if the decrypted secret is the same as the original secret
let decodedSecret = decodedKeyfiles . get ( ) [ 0 ]
check :
secret = = decodedSecret . get ( )
test " Create/Save/Load multiple keyfiles in same file " :
# We set different passwords for different keyfiles that will be stored in same file
let password1 = string . fromBytes ( randomSeqByte ( rng [ ] , 20 ) )
let password2 = " "
let password3 = string . fromBytes ( randomSeqByte ( rng [ ] , 20 ) )
2023-02-13 10:43:49 +00:00
var keyfile : KfResult [ JsonNode ]
2022-10-28 09:13:05 +00:00
let filepath = " ./test.keyfile "
2024-03-15 23:08:47 +00:00
defer :
removeFile ( filepath )
2022-10-28 09:13:05 +00:00
# We generate 6 different secrets and we encrypt them using 3 different passwords, and we store the obtained keystore
let secret1 = randomSeqByte ( rng [ ] , 300 )
keyfile = createKeyFileJson ( secret1 , password1 )
check :
keyfile . isOk ( )
saveKeyFile ( filepath , keyfile . get ( ) ) . isOk ( )
let secret2 = randomSeqByte ( rng [ ] , 300 )
keyfile = createKeyFileJson ( secret2 , password2 )
check :
keyfile . isOk ( )
saveKeyFile ( filepath , keyfile . get ( ) ) . isOk ( )
let secret3 = randomSeqByte ( rng [ ] , 300 )
keyfile = createKeyFileJson ( secret3 , password3 )
check :
keyfile . isOk ( )
saveKeyFile ( filepath , keyfile . get ( ) ) . isOk ( )
# We encrypt secret4 with password3
let secret4 = randomSeqByte ( rng [ ] , 300 )
keyfile = createKeyFileJson ( secret4 , password3 )
check :
keyfile . isOk ( )
saveKeyFile ( filepath , keyfile . get ( ) ) . isOk ( )
# We encrypt secret5 with password1
let secret5 = randomSeqByte ( rng [ ] , 300 )
keyfile = createKeyFileJson ( secret5 , password1 )
check :
keyfile . isOk ( )
saveKeyFile ( filepath , keyfile . get ( ) ) . isOk ( )
# We encrypt secret6 with password1
let secret6 = randomSeqByte ( rng [ ] , 300 )
keyfile = createKeyFileJson ( secret6 , password1 )
check :
keyfile . isOk ( )
saveKeyFile ( filepath , keyfile . get ( ) ) . isOk ( )
# Now there are 6 keyfiles stored in filepath encrypted with 3 different passwords
2023-02-13 10:43:49 +00:00
# We decrypt the keyfiles using the respective passwords and we check that the number of
2022-10-28 09:13:05 +00:00
# successful decryptions corresponds to the number of secrets encrypted under that password
var decodedKeyfilesPassword1 = loadKeyFiles ( filepath , password1 )
check :
decodedKeyfilesPassword1 . isOk ( )
decodedKeyfilesPassword1 . get ( ) . len = = 3
var decodedSecretsPassword1 = decodedKeyfilesPassword1 . get ( )
var decodedKeyfilesPassword2 = loadKeyFiles ( filepath , password2 )
check :
decodedKeyfilesPassword2 . isOk ( )
decodedKeyfilesPassword2 . get ( ) . len = = 1
var decodedSecretsPassword2 = decodedKeyfilesPassword2 . get ( )
var decodedKeyfilesPassword3 = loadKeyFiles ( filepath , password3 )
check :
decodedKeyfilesPassword3 . isOk ( )
decodedKeyfilesPassword3 . get ( ) . len = = 2
var decodedSecretsPassword3 = decodedKeyfilesPassword3 . get ( )
2023-02-13 10:43:49 +00:00
2022-10-28 09:13:05 +00:00
# We check if the corresponding secrets are correct
check :
# Secrets encrypted with password 1
secret1 = = decodedSecretsPassword1 [ 0 ] . get ( )
secret5 = = decodedSecretsPassword1 [ 1 ] . get ( )
secret6 = = decodedSecretsPassword1 [ 2 ] . get ( )
# Secrets encrypted with password 2
secret2 = = decodedSecretsPassword2 [ 0 ] . get ( )
# Secrets encrypted with password 3
secret3 = = decodedSecretsPassword3 [ 0 ] . get ( )
secret4 = = decodedSecretsPassword3 [ 1 ] . get ( )
# The following tests are originally from the nim-eth keyfile tests module https://github.com/status-im/nim-eth/blob/master/tests/keyfile/test_keyfile.nim
# and are slightly adapted to test backwards compatibility with nim-eth implementation of our customized version of the utils/keyfile module
# Note: the original nim-eth "Create/Save/Load test" is redefined and expanded above in "KeyFile test suite"
suite " KeyFile test suite (adapted from nim-eth keyfile tests) " :
# Testvectors originally from https://github.com/status-im/nim-eth/blob/fef47331c37ee8abb8608037222658737ff498a6/tests/keyfile/test_keyfile.nim#L22-L168
let TestVectors = [
% * {
" keyfile " : {
2024-03-15 23:08:47 +00:00
" crypto " : {
" cipher " : " aes-128-ctr " ,
" cipherparams " : { " iv " : " 6087dab2f9fdbbfaddc31a909735c1e6 " } ,
" ciphertext " :
" 5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46 " ,
" kdf " : " pbkdf2 " ,
" kdfparams " : {
" c " : 262144 ,
" dklen " : 32 ,
" prf " : " hmac-sha256 " ,
" salt " : " ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" mac " : " 517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2 " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" id " : " 3198bc9c-6672-5ab3-d995-4942343ae5b6 " ,
" version " : 3 ,
2022-10-28 09:13:05 +00:00
} ,
" name " : " test1 " ,
" password " : " testpassword " ,
2024-03-15 23:08:47 +00:00
" priv " : " 7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d " ,
2022-10-28 09:13:05 +00:00
} ,
% * {
" keyfile " : {
" version " : 3 ,
" crypto " : {
2024-03-15 23:08:47 +00:00
" ciphertext " :
" ee75456c006b1e468133c5d2a916bacd3cf515ced4d9b021b5c59978007d1e87 " ,
2022-10-28 09:13:05 +00:00
" version " : 1 ,
" kdf " : " pbkdf2 " ,
" kdfparams " : {
" dklen " : 32 ,
" c " : 262144 ,
" prf " : " hmac-sha256 " ,
2024-03-15 23:08:47 +00:00
" salt " : " 504490577620f64f43d73f29479c2cf0 " ,
2022-10-28 09:13:05 +00:00
} ,
" mac " : " 196815708465de9af7504144a1360d08874fc3c30bb0e648ce88fbc36830d35d " ,
" cipherparams " : { " iv " : " 514ccc8c4fb3e60e5538e0cf1e27c233 " } ,
2024-03-15 23:08:47 +00:00
" cipher " : " aes-128-ctr " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" id " : " 98d193c7-5174-4c7c-5345-c1daf95477b5 " ,
2022-10-28 09:13:05 +00:00
} ,
" name " : " python_generated_test_with_odd_iv " ,
" password " : " foo " ,
2024-03-15 23:08:47 +00:00
" priv " : " 0101010101010101010101010101010101010101010101010101010101010101 " ,
2022-10-28 09:13:05 +00:00
} ,
% * {
" keyfile " : {
" version " : 3 ,
" crypto " : {
2024-03-15 23:08:47 +00:00
" ciphertext " :
" d69313b6470ac1942f75d72ebf8818a0d484ac78478a132ee081cd954d6bd7a9 " ,
2022-10-28 09:13:05 +00:00
" cipherparams " : { " iv " : " ffffffffffffffffffffffffffffffff " } ,
" kdf " : " pbkdf2 " ,
" kdfparams " : {
" dklen " : 32 ,
" c " : 262144 ,
" prf " : " hmac-sha256 " ,
2024-03-15 23:08:47 +00:00
" salt " : " c82ef14476014cbf438081a42709e2ed " ,
2022-10-28 09:13:05 +00:00
} ,
" mac " : " cf6bfbcc77142a22c4a908784b4a16f1023a1d0e2aff404c20158fa4f1587177 " ,
" cipher " : " aes-128-ctr " ,
2024-03-15 23:08:47 +00:00
" version " : 1 ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" id " : " abb67040-8dbe-0dad-fc39-2b082ef0ee5f " ,
2022-10-28 09:13:05 +00:00
} ,
" name " : " evilnonce " ,
" password " : " bar " ,
2024-03-15 23:08:47 +00:00
" priv " : " 0202020202020202020202020202020202020202020202020202020202020202 " ,
2022-10-28 09:13:05 +00:00
} ,
% * {
" keyfile " : {
2024-03-15 23:08:47 +00:00
" version " : 3 ,
" crypto " : {
" cipher " : " aes-128-ctr " ,
" cipherparams " : { " iv " : " 83dbcc02d8ccb40e466191a123791e0e " } ,
" ciphertext " :
" d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c " ,
" kdf " : " scrypt " ,
" kdfparams " : {
" dklen " : 32 ,
" n " : 262144 ,
" r " : 1 ,
" p " : 8 ,
" salt " : " ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19 " ,
} ,
" mac " : " 2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097 " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" id " : " 3198bc9c-6672-5ab3-d995-4942343ae5b6 " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" name " : " test2 " ,
2022-10-28 09:13:05 +00:00
" password " : " testpassword " ,
2024-03-15 23:08:47 +00:00
" priv " : " 7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d " ,
2022-10-28 09:13:05 +00:00
} ,
% * {
" keyfile " : {
" version " : 3 ,
" address " : " 460121576cc7df020759730751f92bd62fd78dd6 " ,
" crypto " : {
2024-03-15 23:08:47 +00:00
" ciphertext " :
" 54ae683c6287fa3d58321f09d56e26d94e58a00d4f90bdd95782ae0e4aab618b " ,
" cipherparams " : { " iv " : " 681679cdb125bba9495d068b002816a4 " } ,
" cipher " : " aes-128-ctr " ,
" kdf " : " scrypt " ,
" kdfparams " : {
" dklen " : 32 ,
" salt " : " c3407f363fce02a66e3c4bf4a8f6b7da1c1f54266cef66381f0625c251c32785 " ,
" n " : 8192 ,
" r " : 8 ,
" p " : 1 ,
} ,
" mac " : " dea6bdf22a2f522166ed82808c22a6311e84c355f4bbe100d4260483ff675a46 " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" id " : " 0eb785e0-340a-4290-9c42-90a11973ee47 " ,
2022-10-28 09:13:05 +00:00
} ,
" name " : " mycrypto " ,
" password " : " foobartest121 " ,
2024-03-15 23:08:47 +00:00
" priv " : " 05a4d3eb46c742cb8850440145ce70cbc80b59f891cf5f50fd3e9c280b50c4e4 " ,
2022-10-28 09:13:05 +00:00
} ,
% * {
" keyfile " : {
2024-03-15 23:08:47 +00:00
" crypto " : {
" cipher " : " aes-128-ctr " ,
" cipherparams " : { " iv " : " 7e7b02d2b4ef45d6c98cb885e75f48d5 " } ,
" ciphertext " :
" a7a5743a6c7eb3fa52396bd3fd94043b79075aac3ccbae8e62d3af94db00397c " ,
" kdf " : " scrypt " ,
" kdfparams " : {
" dklen " : 32 ,
" n " : 8192 ,
" p " : 1 ,
" r " : 8 ,
" salt " : " 247797c7a357b707a3bdbfaa55f4c553756bca09fec20ddc938e7636d21e4a20 " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" mac " : " 5a3ba5bebfda2c384586eda5fcda9c8397d37c9b0cc347fea86525cf2ea3a468 " ,
} ,
" address " : " 0b6f2de3dee015a95d3330dcb7baf8e08aa0112d " ,
" id " : " 3c8efdd6-d538-47ec-b241-36783d3418b9 " ,
" version " : 3 ,
2022-10-28 09:13:05 +00:00
} ,
" password " : " moomoocow " ,
" priv " : " 21eac69b9a52f466bfe9047f0f21c9caf3a5cdaadf84e2750a9b3265d450d481 " ,
2024-03-15 23:08:47 +00:00
" name " : " eth-keyfile-conftest " ,
} ,
2022-10-28 09:13:05 +00:00
]
test " Testing nim-eth test vectors " :
var secret : KfResult [ seq [ byte ] ]
var expectedSecret : seq [ byte ]
2024-03-15 23:08:47 +00:00
for i in 0 .. < TestVectors . len :
2022-10-28 09:13:05 +00:00
# Decryption with correct password
expectedSecret = decodeHex ( TestVectors [ i ] . getOrDefault ( " priv " ) . getStr ( ) )
2024-03-15 23:08:47 +00:00
secret = decodeKeyFileJson (
TestVectors [ i ] . getOrDefault ( " keyfile " ) ,
TestVectors [ i ] . getOrDefault ( " password " ) . getStr ( ) ,
)
2022-10-28 09:13:05 +00:00
check :
secret . isOk ( )
secret . get ( ) = = expectedSecret
2023-02-13 10:43:49 +00:00
# Decryption with wrong password
2024-03-15 23:08:47 +00:00
secret =
decodeKeyFileJson ( TestVectors [ i ] . getOrDefault ( " keyfile " ) , " wrongpassword " )
2022-10-28 09:13:05 +00:00
check :
secret . isErr ( )
2023-02-08 15:26:23 +00:00
secret . error = = KeyFileError . KeyfileIncorrectMac
2022-10-28 09:13:05 +00:00
test " Wrong mac in keyfile " :
2023-02-13 10:43:49 +00:00
# This keyfile is the same as the first one in TestVectors,
2022-10-28 09:13:05 +00:00
# but the last byte of mac is changed to 00.
# While ciphertext is the correct encryption of priv under password,
# mac verfication should fail and nothing will be decrypted
2024-03-15 23:08:47 +00:00
let keyfileWrongMac =
% * {
2022-10-28 09:13:05 +00:00
" keyfile " : {
2024-03-15 23:08:47 +00:00
" crypto " : {
" cipher " : " aes-128-ctr " ,
" cipherparams " : { " iv " : " 6087dab2f9fdbbfaddc31a909735c1e6 " } ,
" ciphertext " :
" 5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46 " ,
" kdf " : " pbkdf2 " ,
" kdfparams " : {
" c " : 262144 ,
" dklen " : 32 ,
" prf " : " hmac-sha256 " ,
" salt " : " ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" mac " : " 517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e900 " ,
2022-10-28 09:13:05 +00:00
} ,
2024-03-15 23:08:47 +00:00
" id " : " 3198bc9c-6672-5ab3-d995-4942343ae5b6 " ,
" version " : 3 ,
2022-10-28 09:13:05 +00:00
} ,
" name " : " test1 " ,
" password " : " testpassword " ,
2024-03-15 23:08:47 +00:00
" priv " : " 7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d " ,
2022-10-28 09:13:05 +00:00
}
# Decryption with correct password
let expectedSecret = decodeHex ( keyfileWrongMac . getOrDefault ( " priv " ) . getStr ( ) )
2024-03-15 23:08:47 +00:00
let secret = decodeKeyFileJson (
keyfileWrongMac . getOrDefault ( " keyfile " ) ,
keyfileWrongMac . getOrDefault ( " password " ) . getStr ( ) ,
)
2022-10-28 09:13:05 +00:00
check :
secret . isErr ( )
2023-02-08 15:26:23 +00:00
secret . error = = KeyFileError . KeyFileIncorrectMac
2022-10-28 09:13:05 +00:00
test " Scrypt keyfiles " :
let
expectedSecret = randomSeqByte ( rng [ ] , 300 )
password = " miawmiawcat "
2023-02-13 10:43:49 +00:00
# By default, keyfiles' encryption key is derived from password using PBKDF2.
2022-10-28 09:13:05 +00:00
# Here we test keyfiles encypted with a key derived from password using scrypt
jsonKeyfile = createKeyFileJson ( expectedSecret , password , 3 , AES128CTR , SCRYPT )
check :
jsonKeyfile . isOk ( )
let secret = decodeKeyFileJson ( jsonKeyfile . get ( ) , password )
check :
2024-03-15 23:08:47 +00:00
secret . isOk ( )
secret . get ( ) = = expectedSecret
2022-10-28 09:13:05 +00:00
test " Load non-existent keyfile test " :
check :
loadKeyFiles ( " nonexistant.keyfile " , " password " ) . error = =
2023-02-13 10:43:49 +00:00
KeyFileError . KeyfileDoesNotExist