Crypto rng (#1284)
* use bearssl rng throughout * bump * bump * move keygen out of crypto
This commit is contained in:
parent
6ef2e71468
commit
6fe0a623f5
|
@ -7,7 +7,7 @@
|
|||
|
||||
import
|
||||
# Standard library
|
||||
algorithm, os, tables, strutils, times, math, terminal, random,
|
||||
algorithm, os, tables, strutils, times, math, terminal, bearssl, random,
|
||||
|
||||
# Nimble packages
|
||||
stew/[objects, byteutils], stew/shims/macros,
|
||||
|
@ -129,9 +129,11 @@ func enrForkIdFromState(state: BeaconState): ENRForkID =
|
|||
next_fork_version: forkVer,
|
||||
next_fork_epoch: FAR_FUTURE_EPOCH)
|
||||
|
||||
proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async.} =
|
||||
proc init*(
|
||||
T: type BeaconNode, rng: ref BrHmacDrbgContext,
|
||||
conf: BeaconNodeConf): Future[BeaconNode] {.async.} =
|
||||
let
|
||||
netKeys = getPersistentNetKeys(conf)
|
||||
netKeys = getPersistentNetKeys(rng[], conf)
|
||||
nickname = if conf.nodeName == "auto": shortForm(netKeys)
|
||||
else: conf.nodeName
|
||||
db = BeaconChainDB.init(kvStore SqStoreRef.init(conf.databaseDir, "nbc").tryGet())
|
||||
|
@ -221,7 +223,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
|||
enrForkId = enrForkIdFromState(blockPool.headState.data.data)
|
||||
topicBeaconBlocks = getBeaconBlocksTopic(enrForkId.forkDigest)
|
||||
topicAggregateAndProofs = getAggregateAndProofsTopic(enrForkId.forkDigest)
|
||||
network = await createEth2Node(conf, enrForkId)
|
||||
network = await createEth2Node(rng, conf, enrForkId)
|
||||
|
||||
var res = BeaconNode(
|
||||
nickname: nickname,
|
||||
|
@ -1030,12 +1032,14 @@ when hasPrompt:
|
|||
# var t: Thread[ptr Prompt]
|
||||
# createThread(t, processPromptCommands, addr p)
|
||||
|
||||
proc createWalletInteractively(conf: BeaconNodeConf): OutFile {.raises: [Defect].} =
|
||||
proc createWalletInteractively(
|
||||
rng: var BrHmacDrbgContext,
|
||||
conf: BeaconNodeConf): OutFile {.raises: [Defect].} =
|
||||
if conf.nonInteractive:
|
||||
fatal "Wallets can be created only in interactive mode"
|
||||
quit 1
|
||||
|
||||
var mnemonic = generateMnemonic()
|
||||
var mnemonic = generateMnemonic(rng)
|
||||
defer: keystore_management.burnMem(mnemonic)
|
||||
|
||||
template readLine: string =
|
||||
|
@ -1102,7 +1106,8 @@ proc createWalletInteractively(conf: BeaconNodeConf): OutFile {.raises: [Defect]
|
|||
continue
|
||||
break
|
||||
|
||||
let (uuid, walletContent) = KdfPbkdf2.createWalletContent(mnemonic, name)
|
||||
let (uuid, walletContent) = KdfPbkdf2.createWalletContent(
|
||||
rng, mnemonic, name)
|
||||
try:
|
||||
var outWalletFile: OutFile
|
||||
|
||||
|
@ -1133,6 +1138,10 @@ programMain:
|
|||
|
||||
setupMainProc(config.logLevel)
|
||||
|
||||
# Single RNG instance for the application - will be seeded on construction
|
||||
# and avoid using system resources (such as urandom) after that
|
||||
let rng = keys.newRng()
|
||||
|
||||
case config.cmd
|
||||
of createTestnet:
|
||||
var
|
||||
|
@ -1188,7 +1197,7 @@ programMain:
|
|||
let bootstrapFile = config.outputBootstrapFile.string
|
||||
if bootstrapFile.len > 0:
|
||||
let
|
||||
networkKeys = getPersistentNetKeys(config)
|
||||
networkKeys = getPersistentNetKeys(rng[], config)
|
||||
metadata = getPersistentNetMetadata(config)
|
||||
bootstrapEnr = enr.Record.init(
|
||||
1, # sequence number
|
||||
|
@ -1212,7 +1221,7 @@ programMain:
|
|||
|
||||
config.createDumpDirs()
|
||||
|
||||
var node = waitFor BeaconNode.init(config)
|
||||
var node = waitFor BeaconNode.init(rng, config)
|
||||
|
||||
ctrlCHandling: status = BeaconNodeStatus.Stopping
|
||||
|
||||
|
@ -1238,6 +1247,7 @@ programMain:
|
|||
createDir(config.outSecretsDir)
|
||||
|
||||
let deposits = generateDeposits(
|
||||
rng[],
|
||||
config.totalDeposits,
|
||||
config.outValidatorsDir,
|
||||
config.outSecretsDir)
|
||||
|
@ -1270,7 +1280,7 @@ programMain:
|
|||
of wallets:
|
||||
case config.walletsCmd:
|
||||
of WalletsCmd.create:
|
||||
let walletFile = createWalletInteractively(config)
|
||||
let walletFile = createWalletInteractively(rng[], config)
|
||||
of WalletsCmd.list:
|
||||
# TODO
|
||||
discard
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import
|
||||
os, strutils, options, json,
|
||||
chronos, nimcrypto, confutils, web3, stint,
|
||||
chronos, confutils, web3, stint,
|
||||
eth/keys
|
||||
|
||||
# Compiled version of /scripts/depositContract.v.py in this repo
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import
|
||||
os, sequtils, strutils,
|
||||
chronicles, stew/shims/net, stew/results, eth/keys, eth/trie/db,
|
||||
chronicles, stew/shims/net, stew/results, eth/keys, eth/trie/db, bearssl,
|
||||
eth/p2p/discoveryv5/[enr, protocol, discovery_db, node],
|
||||
conf
|
||||
|
||||
|
@ -76,7 +76,7 @@ proc new*(T: type Eth2DiscoveryProtocol,
|
|||
conf: BeaconNodeConf,
|
||||
ip: Option[ValidIpAddress], tcpPort, udpPort: Port,
|
||||
pk: PrivateKey,
|
||||
enrFields: openarray[(string, seq[byte])]):
|
||||
enrFields: openarray[(string, seq[byte])], rng: ref BrHmacDrbgContext):
|
||||
T {.raises: [Exception, Defect].} =
|
||||
# TODO
|
||||
# Implement more configuration options:
|
||||
|
@ -97,4 +97,5 @@ proc new*(T: type Eth2DiscoveryProtocol,
|
|||
loadBootstrapFile(persistentBootstrapFile, bootstrapEnrs, ourPubKey)
|
||||
|
||||
let enrFieldPairs = mapIt(enrFields, toFieldPair(it[0], it[1]))
|
||||
newProtocol(pk, db, ip, tcpPort, udpPort, enrFieldPairs, bootstrapEnrs)
|
||||
newProtocol(
|
||||
pk, db, ip, tcpPort, udpPort, enrFieldPairs, bootstrapEnrs, rng = rng)
|
||||
|
|
|
@ -4,7 +4,7 @@ import
|
|||
options as stdOptions,
|
||||
|
||||
# Status libs
|
||||
stew/[varints, base58, base64, endians2, results, byteutils],
|
||||
stew/[varints, base58, base64, endians2, results, byteutils], bearssl,
|
||||
stew/shims/net as stewNet,
|
||||
stew/shims/[macros, tables],
|
||||
faststreams/[inputs, outputs, buffers], snappy, snappy/framing,
|
||||
|
@ -65,6 +65,7 @@ type
|
|||
connWorkers: seq[Future[void]]
|
||||
connTable: Table[PeerID, PeerInfo]
|
||||
forkId: ENRForkID
|
||||
rng*: ref BrHmacDrbgContext
|
||||
|
||||
EthereumNode = Eth2Node # needed for the definitions in p2p_backends_helpers
|
||||
|
||||
|
@ -796,7 +797,7 @@ proc getPersistentNetMetadata*(conf: BeaconNodeConf): Eth2Metadata =
|
|||
|
||||
proc init*(T: type Eth2Node, conf: BeaconNodeConf, enrForkId: ENRForkID,
|
||||
switch: Switch, ip: Option[ValidIpAddress], tcpPort, udpPort: Port,
|
||||
privKey: keys.PrivateKey): T =
|
||||
privKey: keys.PrivateKey, rng: ref BrHmacDrbgContext): T =
|
||||
new result
|
||||
result.switch = switch
|
||||
result.wantedPeers = conf.maxPeers
|
||||
|
@ -810,7 +811,8 @@ proc init*(T: type Eth2Node, conf: BeaconNodeConf, enrForkId: ENRForkID,
|
|||
result.forkId = enrForkId
|
||||
result.discovery = Eth2DiscoveryProtocol.new(
|
||||
conf, ip, tcpPort, udpPort, privKey,
|
||||
{"eth2": SSZ.encode(result.forkId), "attnets": SSZ.encode(result.metadata.attnets)})
|
||||
{"eth2": SSZ.encode(result.forkId), "attnets": SSZ.encode(result.metadata.attnets)},
|
||||
rng)
|
||||
|
||||
newSeq result.protocolStates, allProtocols.len
|
||||
for proto in allProtocols:
|
||||
|
@ -1064,13 +1066,14 @@ proc initAddress*(T: type MultiAddress, str: string): T =
|
|||
template tcpEndPoint(address, port): auto =
|
||||
MultiAddress.init(address, tcpProtocol, port)
|
||||
|
||||
proc getPersistentNetKeys*(conf: BeaconNodeConf): KeyPair =
|
||||
proc getPersistentNetKeys*(
|
||||
rng: var BrHmacDrbgContext, conf: BeaconNodeConf): KeyPair =
|
||||
let
|
||||
privKeyPath = conf.dataDir / networkKeyFilename
|
||||
privKey =
|
||||
if not fileExists(privKeyPath):
|
||||
createDir conf.dataDir.string
|
||||
let key = PrivateKey.random(Secp256k1).tryGet()
|
||||
let key = PrivateKey.random(Secp256k1, rng).tryGet()
|
||||
writeFile(privKeyPath, key.getBytes().tryGet())
|
||||
key
|
||||
else:
|
||||
|
@ -1086,7 +1089,7 @@ func gossipId(data: openArray[byte]): string =
|
|||
func msgIdProvider(m: messages.Message): string =
|
||||
gossipId(m.data)
|
||||
|
||||
proc createEth2Node*(conf: BeaconNodeConf, enrForkId: ENRForkID): Future[Eth2Node] {.async, gcsafe.} =
|
||||
proc createEth2Node*(rng: ref BrHmacDrbgContext, conf: BeaconNodeConf, enrForkId: ENRForkID): Future[Eth2Node] {.async, gcsafe.} =
|
||||
var
|
||||
(extIp, extTcpPort, extUdpPort) = setupNat(conf)
|
||||
hostAddress = tcpEndPoint(conf.libp2pAddress, conf.tcpPort)
|
||||
|
@ -1096,7 +1099,7 @@ proc createEth2Node*(conf: BeaconNodeConf, enrForkId: ENRForkID): Future[Eth2Nod
|
|||
info "Initializing networking", hostAddress,
|
||||
announcedAddresses
|
||||
|
||||
let keys = conf.getPersistentNetKeys
|
||||
let keys = getPersistentNetKeys(rng[], conf)
|
||||
# TODO nim-libp2p still doesn't have support for announcing addresses
|
||||
# that are different from the host address (this is relevant when we
|
||||
# are running behind a NAT).
|
||||
|
@ -1104,14 +1107,15 @@ proc createEth2Node*(conf: BeaconNodeConf, enrForkId: ENRForkID): Future[Eth2Nod
|
|||
triggerSelf = true, gossip = true,
|
||||
sign = false, verifySignature = false,
|
||||
transportFlags = {ServerFlags.ReuseAddr},
|
||||
msgIdProvider = msgIdProvider)
|
||||
msgIdProvider = msgIdProvider,
|
||||
rng = rng)
|
||||
result = Eth2Node.init(conf, enrForkId, switch,
|
||||
extIp, extTcpPort, extUdpPort,
|
||||
keys.seckey.asEthKey)
|
||||
keys.seckey.asEthKey, rng = rng)
|
||||
|
||||
proc getPersistenBootstrapAddr*(conf: BeaconNodeConf,
|
||||
proc getPersistenBootstrapAddr*(rng: var BrHmacDrbgContext, conf: BeaconNodeConf,
|
||||
ip: ValidIpAddress, port: Port): EnrResult[enr.Record] =
|
||||
let pair = getPersistentNetKeys(conf)
|
||||
let pair = getPersistentNetKeys(rng, conf)
|
||||
return enr.Record.init(1'u64, # sequence number
|
||||
pair.seckey.asEthKey,
|
||||
some(ip), port, port, @[])
|
||||
|
|
|
@ -593,6 +593,7 @@ proc run(conf: InspectorConf) {.async.} =
|
|||
var pubsubPeers = newTable[PeerID, PeerInfo]()
|
||||
var resolveQueue = newAsyncQueue[PeerID](10)
|
||||
var connectQueue = newAsyncQueue[PeerInfo](10)
|
||||
let rng = lcrypto.newRng()
|
||||
|
||||
let bootnodes = loadBootstrapNodes(conf)
|
||||
if len(bootnodes) == 0:
|
||||
|
@ -667,7 +668,7 @@ proc run(conf: InspectorConf) {.async.} =
|
|||
bootstrap_fork_digest = forkDigest.get()
|
||||
forkDigest = argForkDigest
|
||||
|
||||
let seckey = lcrypto.PrivateKey.random(PKScheme.Secp256k1).tryGet()
|
||||
let seckey = lcrypto.PrivateKey.random(PKScheme.Secp256k1, rng[]).tryGet()
|
||||
# let pubkey = seckey.getKey()
|
||||
|
||||
let hostAddress = tryGetMultiAddress(conf.bindAddress)
|
||||
|
@ -677,7 +678,7 @@ proc run(conf: InspectorConf) {.async.} =
|
|||
|
||||
var switch = newStandardSwitch(some(seckey), hostAddress.get(),
|
||||
triggerSelf = true, gossip = true,
|
||||
sign = false, verifySignature = false)
|
||||
sign = false, verifySignature = false, rng = rng)
|
||||
|
||||
if len(conf.topics) > 0:
|
||||
for item in conf.topics:
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import
|
||||
os, strutils, terminal,
|
||||
chronicles, chronos, blscurve, nimcrypto, json_serialization, serialization,
|
||||
web3, stint, eth/keys, confutils,
|
||||
spec/[datatypes, digest, crypto, keystore], conf, ssz/merkleization, merkle_minimal
|
||||
os, strutils, terminal, stew/byteutils,
|
||||
chronicles, chronos, blscurve, json_serialization, serialization,
|
||||
web3, stint, eth/keys, confutils, bearssl,
|
||||
spec/[datatypes, digest, crypto, keystore],
|
||||
conf, ssz/merkleization, merkle_minimal
|
||||
|
||||
export
|
||||
keystore
|
||||
|
@ -101,20 +102,19 @@ type
|
|||
FailedToCreateKeystoreFile
|
||||
FailedToCreateDepositFile
|
||||
|
||||
proc generateDeposits*(totalValidators: int,
|
||||
proc generateDeposits*(rng: var BrHmacDrbgContext, totalValidators: int,
|
||||
validatorsDir: string,
|
||||
secretsDir: string): Result[seq[Deposit], GenerateDepositsError] =
|
||||
var deposits: seq[Deposit]
|
||||
|
||||
info "Generating deposits", totalValidators, validatorsDir, secretsDir
|
||||
for i in 0 ..< totalValidators:
|
||||
let password = KeyStorePass getRandomBytesOrPanic(32).toHex
|
||||
let credentials = generateCredentials(password = password)
|
||||
let password = KeyStorePass getRandomBytes(rng, 32).toHex
|
||||
let credentials = generateCredentials(rng, password = password)
|
||||
|
||||
let
|
||||
keyName = intToStr(i, 6) & "_" & $(credentials.signingKey.toPubKey)
|
||||
validatorDir = validatorsDir / keyName
|
||||
passphraseFile = secretsDir / keyName
|
||||
depositFile = validatorDir / depositFileName
|
||||
keystoreFile = validatorDir / keystoreFileName
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ import
|
|||
./digest,
|
||||
# Status
|
||||
stew/[endians2, objects, results, byteutils],
|
||||
nimcrypto/sysrand,
|
||||
blscurve,
|
||||
chronicles,
|
||||
json_serialization,
|
||||
|
@ -175,25 +174,6 @@ func blsFastAggregateVerify*(
|
|||
|
||||
fastAggregateVerify(unwrapped, message, signature.blsValue)
|
||||
|
||||
proc newKeyPair*(): BlsResult[tuple[pub: ValidatorPubKey, priv: ValidatorPrivKey]] =
|
||||
## Generates a new public-private keypair
|
||||
## This requires entropy on the system
|
||||
# The input-keying-material requires 32 bytes at least for security
|
||||
# The generation is deterministic and the input-keying-material
|
||||
# must be protected against side-channel attacks
|
||||
|
||||
var ikm: array[32, byte]
|
||||
if randomBytes(ikm) != 32:
|
||||
return err "bls: no random bytes"
|
||||
|
||||
var
|
||||
sk: SecretKey
|
||||
pk: PublicKey
|
||||
if keyGen(ikm, pk, sk):
|
||||
ok((ValidatorPubKey(kind: Real, blsValue: pk), ValidatorPrivKey(sk)))
|
||||
else:
|
||||
err "bls: cannot generate keypair"
|
||||
|
||||
proc toGaugeValue*(hash: Eth2Digest): int64 =
|
||||
# Only the last 8 bytes are taken into consideration in accordance
|
||||
# to the ETH2 metrics spec:
|
||||
|
@ -362,16 +342,3 @@ func init*(T: typedesc[ValidatorSig], data: array[RawSigSize, byte]): T {.noInit
|
|||
if v.isErr:
|
||||
raise (ref ValueError)(msg: $v.error)
|
||||
return v[]
|
||||
|
||||
proc getRandomBytes*(n: Natural): seq[byte]
|
||||
{.raises: [RandomSourceDepleted, Defect].} =
|
||||
result = newSeq[byte](n)
|
||||
if randomBytes(result) != result.len:
|
||||
raise newException(RandomSourceDepleted, "Failed to generate random bytes")
|
||||
|
||||
proc getRandomBytesOrPanic*(output: var openArray[byte]) =
|
||||
doAssert randomBytes(output) == output.len
|
||||
|
||||
proc getRandomBytesOrPanic*(n: Natural): seq[byte] =
|
||||
result = newSeq[byte](n)
|
||||
getRandomBytesOrPanic(result)
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
json, math, strutils, strformat, typetraits,
|
||||
json, math, strutils, strformat, typetraits, bearssl,
|
||||
stew/[results, byteutils, bitseqs, bitops2], stew/shims/macros,
|
||||
eth/keyfile/uuid, blscurve, json_serialization,
|
||||
nimcrypto/[sha2, rijndael, pbkdf2, bcmode, hash, sysrand, utils],
|
||||
nimcrypto/[sha2, rijndael, pbkdf2, bcmode, hash, utils],
|
||||
./datatypes, ./crypto, ./digest, ./signatures
|
||||
|
||||
export
|
||||
|
@ -132,6 +132,11 @@ template burnMem*(m: var (SensitiveData|TaintedString)) =
|
|||
# to make its usage less error-prone.
|
||||
utils.burnMem(string m)
|
||||
|
||||
proc getRandomBytes*(rng: var BrHmacDrbgContext, n: Natural): seq[byte]
|
||||
{.raises: [Defect].} =
|
||||
result = newSeq[byte](n)
|
||||
brHmacDrbgGenerate(rng, result)
|
||||
|
||||
macro wordListArray(filename: static string): array[wordListLen, cstring] =
|
||||
result = newTree(nnkBracket)
|
||||
var words = slurp(filename).split()
|
||||
|
@ -180,14 +185,17 @@ func getSeed*(mnemonic: Mnemonic, password: KeyStorePass): KeySeed =
|
|||
let salt = "mnemonic-" & password.string
|
||||
KeySeed sha512.pbkdf2(mnemonic.string, salt, 2048, 64)
|
||||
|
||||
proc generateMnemonic*(words: openarray[cstring] = englishWords,
|
||||
proc generateMnemonic*(
|
||||
rng: var BrHmacDrbgContext,
|
||||
words: openarray[cstring] = englishWords,
|
||||
entropyParam: openarray[byte] = @[]): Mnemonic =
|
||||
# https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#generating-the-mnemonic
|
||||
doAssert words.len == wordListLen
|
||||
|
||||
var entropy: seq[byte]
|
||||
if entropyParam.len == 0:
|
||||
entropy = getRandomBytesOrPanic(32)
|
||||
setLen(entropy, 32)
|
||||
brHmacDrbgGenerate(rng, entropy)
|
||||
else:
|
||||
doAssert entropyParam.len >= 128 and
|
||||
entropyParam.len <= 256 and
|
||||
|
@ -325,6 +333,7 @@ proc decryptKeystore*(data: KeyStoreContent,
|
|||
ValidatorPrivKey.fromRaw(? secret)
|
||||
|
||||
proc createCryptoField(T: type[KdfParams],
|
||||
rng: var BrHmacDrbgContext,
|
||||
secret: openarray[byte],
|
||||
password = KeyStorePass "",
|
||||
salt: openarray[byte] = @[],
|
||||
|
@ -340,13 +349,13 @@ proc createCryptoField(T: type[KdfParams],
|
|||
doAssert salt.len == saltSize
|
||||
@salt
|
||||
else:
|
||||
getRandomBytesOrPanic(saltSize)
|
||||
getRandomBytes(rng, saltSize)
|
||||
|
||||
let aesIv = if iv.len > 0:
|
||||
doAssert iv.len == AES.sizeBlock
|
||||
@iv
|
||||
else:
|
||||
getRandomBytesOrPanic(AES.sizeBlock)
|
||||
getRandomBytes(rng, AES.sizeBlock)
|
||||
|
||||
when T is KdfPbkdf2:
|
||||
decKey = sha256.pbkdf2(password.string, kdfSalt, pbkdf2Params.c,
|
||||
|
@ -374,6 +383,7 @@ proc createCryptoField(T: type[KdfParams],
|
|||
message: byteutils.toHex(cipherMsg)))
|
||||
|
||||
proc encryptKeystore*(T: type[KdfParams],
|
||||
rng: var BrHmacDrbgContext,
|
||||
privKey: ValidatorPrivkey,
|
||||
password = KeyStorePass "",
|
||||
path = KeyPath "",
|
||||
|
@ -382,7 +392,7 @@ proc encryptKeystore*(T: type[KdfParams],
|
|||
pretty = true): KeyStoreContent =
|
||||
let
|
||||
secret = privKey.toRaw[^32..^1]
|
||||
cryptoField = createCryptoField(T, secret, password, salt, iv)
|
||||
cryptoField = createCryptoField(T, rng, secret, password, salt, iv)
|
||||
pubkey = privKey.toPubKey()
|
||||
uuid = uuidGenerate().expect("Random bytes should be available")
|
||||
keystore = Keystore[T](
|
||||
|
@ -396,6 +406,7 @@ proc encryptKeystore*(T: type[KdfParams],
|
|||
else: $(%keystore)
|
||||
|
||||
proc createWallet*(T: type[KdfParams],
|
||||
rng: var BrHmacDrbgContext,
|
||||
mnemonic: Mnemonic,
|
||||
name = WalletName "",
|
||||
salt: openarray[byte] = @[],
|
||||
|
@ -409,7 +420,7 @@ proc createWallet*(T: type[KdfParams],
|
|||
# we want the wallet restoration procedure to depend only on the
|
||||
# mnemonic (the user is asked to treat the mnemonic as a password).
|
||||
seed = getSeed(mnemonic, KeyStorePass"")
|
||||
cryptoField = %createCryptoField(T, distinctBase seed, password, salt, iv)
|
||||
cryptoField = %createCryptoField(T,rng, distinctBase seed, password, salt, iv)
|
||||
|
||||
Wallet(
|
||||
uuid: uuid,
|
||||
|
@ -422,6 +433,7 @@ proc createWallet*(T: type[KdfParams],
|
|||
nextAccount: nextAccount.get(0))
|
||||
|
||||
proc createWalletContent*(T: type[KdfParams],
|
||||
rng: var BrHmacDrbgContext,
|
||||
mnemonic: Mnemonic,
|
||||
name = WalletName "",
|
||||
salt: openarray[byte] = @[],
|
||||
|
@ -429,10 +441,12 @@ proc createWalletContent*(T: type[KdfParams],
|
|||
password = KeyStorePass "",
|
||||
nextAccount = none(Natural),
|
||||
pretty = true): (UUID, WalletContent) =
|
||||
let wallet = createWallet(T, mnemonic, name, salt, iv, password, nextAccount, pretty)
|
||||
let wallet = createWallet(
|
||||
T, rng, mnemonic, name, salt, iv, password, nextAccount, pretty)
|
||||
(wallet.uuid, WalletContent Json.encode(wallet, pretty = pretty))
|
||||
|
||||
proc restoreCredentials*(mnemonic: Mnemonic,
|
||||
proc restoreCredentials*(rng: var BrHmacDrbgContext,
|
||||
mnemonic: Mnemonic,
|
||||
password = KeyStorePass ""): Credentials =
|
||||
let
|
||||
withdrawalKeyPath = makeKeyPath(0, withdrawalKeyKind)
|
||||
|
@ -443,14 +457,15 @@ proc restoreCredentials*(mnemonic: Mnemonic,
|
|||
|
||||
Credentials(
|
||||
mnemonic: mnemonic,
|
||||
keyStore: encryptKeystore(KdfPbkdf2, signingKey, password, signingKeyPath),
|
||||
keyStore: encryptKeystore(KdfPbkdf2, rng, signingKey, password, signingKeyPath),
|
||||
signingKey: signingKey,
|
||||
withdrawalKey: withdrawalKey)
|
||||
|
||||
proc generateCredentials*(entropy: openarray[byte] = @[],
|
||||
proc generateCredentials*(rng: var BrHmacDrbgContext,
|
||||
entropy: openarray[byte] = @[],
|
||||
password = KeyStorePass ""): Credentials =
|
||||
let mnemonic = generateMnemonic(englishWords, entropy)
|
||||
restoreCredentials(mnemonic, password)
|
||||
let mnemonic = generateMnemonic(rng, englishWords, entropy)
|
||||
restoreCredentials(rng, mnemonic, password)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/deposit-contract.md#withdrawal-credentials
|
||||
proc makeWithdrawalCredentials*(k: ValidatorPubKey): Eth2Digest =
|
||||
|
|
|
@ -9,14 +9,35 @@
|
|||
# ---------------------------------------------------------------
|
||||
|
||||
import
|
||||
bearssl, eth/keys,
|
||||
# Specs
|
||||
blscurve/bls_signature_scheme,
|
||||
../../beacon_chain/spec/[datatypes, crypto]
|
||||
|
||||
proc newKeyPair(rng: var BrHmacDrbgContext): BlsResult[tuple[pub: ValidatorPubKey, priv: ValidatorPrivKey]] =
|
||||
## Generates a new public-private keypair
|
||||
## This requires entropy on the system
|
||||
# The input-keying-material requires 32 bytes at least for security
|
||||
# The generation is deterministic and the input-keying-material
|
||||
# must be protected against side-channel attacks
|
||||
|
||||
var ikm: array[32, byte]
|
||||
brHmacDrbgGenerate(rng, ikm)
|
||||
|
||||
var
|
||||
sk: SecretKey
|
||||
pk: bls_signature_scheme.PublicKey
|
||||
if keyGen(ikm, pk, sk):
|
||||
ok((ValidatorPubKey(kind: Real, blsValue: pk), ValidatorPrivKey(sk)))
|
||||
else:
|
||||
err "bls: cannot generate keypair"
|
||||
|
||||
# this is being indexed inside "mock_deposits.nim" by a value up to `validatorCount`
|
||||
# which is `num_validators` which is `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT`
|
||||
proc genMockPrivKeys(privkeys: var array[MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, ValidatorPrivKey]) =
|
||||
let rng = newRng()
|
||||
for i in 0 ..< privkeys.len:
|
||||
let pair = newKeyPair()[]
|
||||
let pair = newKeyPair(rng[])[]
|
||||
privkeys[i] = pair.priv
|
||||
|
||||
func genMockPubKeys(
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import
|
||||
unittest, ./testutil, json,
|
||||
stew/byteutils, blscurve,
|
||||
stew/byteutils, blscurve, eth/keys,
|
||||
../beacon_chain/spec/[crypto, keystore]
|
||||
|
||||
from strutils import replace
|
||||
|
@ -86,6 +86,9 @@ const
|
|||
salt = hexToSeqByte("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
||||
iv = hexToSeqByte("264daa3f303d7259501c93d997d84fe6")
|
||||
|
||||
let
|
||||
rng = newRng()
|
||||
|
||||
suiteReport "Keystore":
|
||||
setup:
|
||||
let secret = ValidatorPrivKey.fromRaw(secretBytes).get
|
||||
|
@ -97,7 +100,7 @@ suiteReport "Keystore":
|
|||
check secret == decrypt.get()
|
||||
|
||||
timedTest "Pbkdf2 encryption":
|
||||
let encrypt = encryptKeystore(KdfPbkdf2, secret,
|
||||
let encrypt = encryptKeystore(KdfPbkdf2, rng[], secret,
|
||||
KeyStorePass password,
|
||||
salt=salt, iv=iv,
|
||||
path = validateKeyPath "m/12381/60/0/0")
|
||||
|
@ -111,10 +114,10 @@ suiteReport "Keystore":
|
|||
|
||||
timedTest "Pbkdf2 errors":
|
||||
expect Defect:
|
||||
echo encryptKeystore(KdfPbkdf2, secret, salt = [byte 1]).string
|
||||
echo encryptKeystore(KdfPbkdf2, rng[], secret, salt = [byte 1]).string
|
||||
|
||||
expect Defect:
|
||||
echo encryptKeystore(KdfPbkdf2, secret, iv = [byte 1]).string
|
||||
echo encryptKeystore(KdfPbkdf2, rng[], secret, iv = [byte 1]).string
|
||||
|
||||
check decryptKeystore(KeyStoreContent pbkdf2Vector,
|
||||
KeyStorePass "wrong pass").isErr
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import
|
||||
unittest, options, json_serialization,
|
||||
nimcrypto, serialization/testing/generic_suite,
|
||||
serialization/testing/generic_suite,
|
||||
./testutil,
|
||||
../beacon_chain/spec/[datatypes, digest],
|
||||
../beacon_chain/ssz, ../beacon_chain/ssz/[navigator, dynamic_navigator]
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 68c6d27304245c948526487b37e10951acf7dbc8
|
||||
Subproject commit 33b2303fc3b64359970b77bb09274c3e012ff37f
|
|
@ -1 +1 @@
|
|||
Subproject commit bfda38ba82d73ed3f7bf41a41a51d69ba1c2cbe5
|
||||
Subproject commit 484fbcab1b25072b4c45f496a88d361fc9479be4
|
|
@ -1 +1 @@
|
|||
Subproject commit b49c619ca851ec6e832243b633efee4c57af5e40
|
||||
Subproject commit d522537b19a532bc4af94fcd146f779c1f23bad0
|
|
@ -1 +1 @@
|
|||
Subproject commit be989635994377e0e421e4a039230098ba5ccd28
|
||||
Subproject commit bf6cc94a3cbab16cf7ffadad11b50c52f161d0a8
|
|
@ -1 +1 @@
|
|||
Subproject commit a75519fe1264ea861fb65eb2ffec1d6566ebd033
|
||||
Subproject commit dd132ba024fd8784aab7b5c306c4ec61c86e8613
|
Loading…
Reference in New Issue