295 lines
10 KiB
Nim
295 lines
10 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
{.used.}
|
|
|
|
import
|
|
json, unittest,
|
|
stew/byteutils, blscurve, eth/keys, json_serialization,
|
|
libp2p/crypto/crypto as lcrypto,
|
|
nimcrypto/utils as ncrutils,
|
|
../beacon_chain/spec/[crypto, keystore],
|
|
./testutil
|
|
|
|
from strutils import replace
|
|
|
|
func isEqual*(a, b: ValidatorPrivKey): bool =
|
|
# `==` on secret keys is not allowed
|
|
let pa = cast[ptr UncheckedArray[byte]](a.unsafeAddr)
|
|
let pb = cast[ptr UncheckedArray[byte]](b.unsafeAddr)
|
|
result = true
|
|
for i in 0 ..< sizeof(a):
|
|
result = result and pa[i] == pb[i]
|
|
|
|
const
|
|
scryptVector = """{
|
|
"crypto": {
|
|
"kdf": {
|
|
"function": "scrypt",
|
|
"params": {
|
|
"dklen": 32,
|
|
"n": 262144,
|
|
"p": 1,
|
|
"r": 8,
|
|
"salt": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
|
},
|
|
"message": ""
|
|
},
|
|
"checksum": {
|
|
"function": "sha256",
|
|
"params": {},
|
|
"message": "d2217fe5f3e9a1e34581ef8a78f7c9928e436d36dacc5e846690a5581e8ea484"
|
|
},
|
|
"cipher": {
|
|
"function": "aes-128-ctr",
|
|
"params": {
|
|
"iv": "264daa3f303d7259501c93d997d84fe6"
|
|
},
|
|
"message": "06ae90d55fe0a6e9c5c3bc5b170827b2e5cce3929ed3f116c2811e6366dfe20f"
|
|
}
|
|
},
|
|
"description": "This is a test keystore that uses scrypt to secure the secret.",
|
|
"pubkey": "9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07",
|
|
"path": "m/12381/60/3141592653/589793238",
|
|
"uuid": "1d85ae20-35c5-4611-98e8-aa14a633906f",
|
|
"version": 4
|
|
}"""
|
|
|
|
pbkdf2Vector = """{
|
|
"crypto": {
|
|
"kdf": {
|
|
"function": "pbkdf2",
|
|
"params": {
|
|
"dklen": 32,
|
|
"c": 262144,
|
|
"prf": "hmac-sha256",
|
|
"salt": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
|
},
|
|
"message": ""
|
|
},
|
|
"checksum": {
|
|
"function": "sha256",
|
|
"params": {},
|
|
"message": "8a9f5d9912ed7e75ea794bc5a89bca5f193721d30868ade6f73043c6ea6febf1"
|
|
},
|
|
"cipher": {
|
|
"function": "aes-128-ctr",
|
|
"params": {
|
|
"iv": "264daa3f303d7259501c93d997d84fe6"
|
|
},
|
|
"message": "cee03fde2af33149775b7223e7845e4fb2c8ae1792e5f99fe9ecf474cc8c16ad"
|
|
}
|
|
},
|
|
"description": "This is a test keystore that uses PBKDF2 to secure the secret.",
|
|
"pubkey": "9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07",
|
|
"path": "m/12381/60/0/0",
|
|
"uuid": "64625def-3331-4eea-ab6f-782f3ed16a83",
|
|
"version": 4
|
|
}"""
|
|
|
|
pbkdf2NetVector = """{
|
|
"crypto":{
|
|
"kdf":{
|
|
"function":"pbkdf2",
|
|
"params":{
|
|
"dklen":32,
|
|
"c":262144,
|
|
"prf":"hmac-sha256",
|
|
"salt":"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
|
},
|
|
"message":""
|
|
},
|
|
"checksum":{
|
|
"function":"sha256",
|
|
"params":{
|
|
|
|
},
|
|
"message":"3aaebceb5e81cce464d62287414befaa03522eb8f56cad4296c0dc9301e5f091"
|
|
},
|
|
"cipher":{
|
|
"function":"aes-128-ctr",
|
|
"params":{
|
|
"iv":"264daa3f303d7259501c93d997d84fe6"
|
|
},
|
|
"message":"c6e22dfed4aec458af6e46efff72937972a9360a8b4dc32c8c266de73a90b421d8892db3"
|
|
}
|
|
},
|
|
"description":"PBKDF2 Network private key storage",
|
|
"pubkey":"08021221031873e6f4e1bf837b93493d570653cb219743d4fab0ff468d4e005e1679730b0b",
|
|
"uuid":"7a053160-1cdf-4faf-a2bb-331e1bc2eb5f",
|
|
"version":1
|
|
}"""
|
|
|
|
scryptNetVector = """{
|
|
"crypto":{
|
|
"kdf":{
|
|
"function":"scrypt",
|
|
"params":{
|
|
"dklen":32,
|
|
"n":262144,
|
|
"p":1,
|
|
"r":8,
|
|
"salt":"d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
|
},
|
|
"message":""
|
|
},
|
|
"checksum":{
|
|
"function":"sha256",
|
|
"params":{
|
|
|
|
},
|
|
"message":"9a7d03a3f2107a11b6e34a081fb13d551012ff081efb81fc94ec114381fa707f"
|
|
},
|
|
"cipher":{
|
|
"function":"aes-128-ctr",
|
|
"params":{
|
|
"iv":"264daa3f303d7259501c93d997d84fe6"
|
|
},
|
|
"message":"0eac82f5a1bd53f81df688970ffeea8425ad7b8f877bcba5a74b87f090c340836cd52095"
|
|
}
|
|
},
|
|
"description":"SCRYPT Network private key storage",
|
|
"pubkey":"08021221031873e6f4e1bf837b93493d570653cb219743d4fab0ff468d4e005e1679730b0b",
|
|
"uuid":"83d77fa3-86cb-466a-af11-eeb338b0e258",
|
|
"version":1
|
|
}"""
|
|
password = string.fromBytes hexToSeqByte("7465737470617373776f7264f09f9491")
|
|
secretBytes = hexToSeqByte "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
|
secretNetBytes = hexToSeqByte "08021220fe442379443d6e2d7d75d3a58f96fbb35f0a9c7217796825fc9040e3b89c5736"
|
|
salt = hexToSeqByte "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
|
iv = hexToSeqByte "264daa3f303d7259501c93d997d84fe6"
|
|
|
|
let
|
|
rng = keys.newRng()
|
|
|
|
suiteReport "KeyStorage testing suite":
|
|
setup:
|
|
let secret = ValidatorPrivKey.fromRaw(secretBytes).get
|
|
let nsecret = init(lcrypto.PrivateKey, secretNetBytes).get
|
|
|
|
timedTest "[PBKDF2] Keystore decryption":
|
|
let
|
|
keystore = Json.decode(pbkdf2Vector, Keystore)
|
|
decrypt = decryptKeystore(keystore, KeystorePass.init password)
|
|
|
|
check decrypt.isOk
|
|
check secret.isEqual(decrypt.get())
|
|
|
|
timedTest "[SCRYPT] Keystore decryption":
|
|
let
|
|
keystore = Json.decode(scryptVector, Keystore)
|
|
decrypt = decryptKeystore(keystore, KeystorePass.init password)
|
|
|
|
check decrypt.isOk
|
|
check secret.isEqual(decrypt.get())
|
|
|
|
timedTest "[PBKDF2] Network Keystore decryption":
|
|
let
|
|
keystore = Json.decode(pbkdf2NetVector, NetKeystore)
|
|
decrypt = decryptNetKeystore(keystore, KeystorePass.init password)
|
|
|
|
check decrypt.isOk
|
|
check nsecret == decrypt.get()
|
|
|
|
timedTest "[SCRYPT] Network Keystore decryption":
|
|
let
|
|
keystore = Json.decode(scryptNetVector, NetKeystore)
|
|
decrypt = decryptNetKeystore(keystore, KeystorePass.init password)
|
|
|
|
check decrypt.isOk
|
|
check nsecret == decrypt.get()
|
|
|
|
timedTest "[PBKDF2] Keystore encryption":
|
|
let keystore = createKeystore(kdfPbkdf2, rng[], secret,
|
|
KeystorePass.init password,
|
|
salt=salt, iv=iv,
|
|
description = "This is a test keystore that uses PBKDF2 to secure the secret.",
|
|
path = validateKeyPath("m/12381/60/0/0").expect("Valid Keypath"))
|
|
var
|
|
encryptJson = parseJson Json.encode(keystore)
|
|
pbkdf2Json = parseJson(pbkdf2Vector)
|
|
encryptJson{"uuid"} = %""
|
|
pbkdf2Json{"uuid"} = %""
|
|
|
|
check encryptJson == pbkdf2Json
|
|
|
|
timedTest "[PBKDF2] Network Keystore encryption":
|
|
let nkeystore = createNetKeystore(kdfPbkdf2, rng[], nsecret,
|
|
KeystorePass.init password,
|
|
salt = salt, iv = iv,
|
|
description =
|
|
"PBKDF2 Network private key storage")
|
|
var
|
|
encryptJson = parseJson Json.encode(nkeystore)
|
|
pbkdf2Json = parseJson(pbkdf2NetVector)
|
|
encryptJson{"uuid"} = %""
|
|
pbkdf2Json{"uuid"} = %""
|
|
check encryptJson == pbkdf2Json
|
|
|
|
timedTest "[SCRYPT] Keystore encryption":
|
|
let keystore = createKeystore(kdfScrypt, rng[], secret,
|
|
KeystorePass.init password,
|
|
salt=salt, iv=iv,
|
|
description = "This is a test keystore that uses scrypt to secure the secret.",
|
|
path = validateKeyPath("m/12381/60/3141592653/589793238").expect("Valid keypath"))
|
|
var
|
|
encryptJson = parseJson Json.encode(keystore)
|
|
scryptJson = parseJson(scryptVector)
|
|
encryptJson{"uuid"} = %""
|
|
scryptJson{"uuid"} = %""
|
|
|
|
check encryptJson == scryptJson
|
|
|
|
timedTest "[SCRYPT] Network Keystore encryption":
|
|
let nkeystore = createNetKeystore(kdfScrypt, rng[], nsecret,
|
|
KeystorePass.init password,
|
|
salt = salt, iv = iv,
|
|
description =
|
|
"SCRYPT Network private key storage")
|
|
var
|
|
encryptJson = parseJson Json.encode(nkeystore)
|
|
pbkdf2Json = parseJson(scryptNetVector)
|
|
encryptJson{"uuid"} = %""
|
|
pbkdf2Json{"uuid"} = %""
|
|
check encryptJson == pbkdf2Json
|
|
|
|
timedTest "Pbkdf2 errors":
|
|
expect Defect:
|
|
echo createKeystore(kdfPbkdf2, rng[], secret, salt = [byte 1])
|
|
|
|
expect Defect:
|
|
echo createKeystore(kdfPbkdf2, rng[], secret, iv = [byte 1])
|
|
|
|
check decryptKeystore(JsonString pbkdf2Vector,
|
|
KeystorePass.init "wrong pass").isErr
|
|
|
|
check decryptKeystore(JsonString pbkdf2Vector,
|
|
KeystorePass.init "").isErr
|
|
|
|
check decryptKeystore(JsonString "{\"a\": 0}",
|
|
KeystorePass.init "").isErr
|
|
|
|
check decryptKeystore(JsonString "",
|
|
KeystorePass.init "").isErr
|
|
|
|
check decryptKeystore(JsonString "{}",
|
|
KeystorePass.init "").isErr
|
|
|
|
template checkVariant(remove): untyped =
|
|
check decryptKeystore(JsonString pbkdf2Vector.replace(remove, "1234"),
|
|
KeystorePass.init password).isErr
|
|
|
|
checkVariant "f876" # salt
|
|
checkVariant "75ea" # checksum
|
|
checkVariant "b722" # cipher
|
|
|
|
var badKdf = parseJson(pbkdf2Vector)
|
|
badKdf{"crypto", "kdf", "function"} = %"invalid"
|
|
|
|
check decryptKeystore(JsonString $badKdf,
|
|
KeystorePass.init password).iserr
|