Skip to content

Commit

Permalink
Add entropy pool actor
Browse files Browse the repository at this point in the history
We introduce an actor that listens to events in the system and inject them
in our weak pseudo-RNG.
  • Loading branch information
t-bast committed Apr 20, 2021
1 parent 812872c commit dd903c4
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 2 deletions.
9 changes: 7 additions & 2 deletions eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
package fr.acinq.eclair

import akka.Done
import akka.actor.{ActorContext, ActorRef, ActorSystem, Props, SupervisorStrategy}
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.adapter.ClassicActorSystemOps
import akka.actor.typed.{SupervisorStrategy => TypedSupervisorStrategy}
import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy}
import akka.pattern.after
import akka.util.Timeout
import com.softwaremill.sttp.okhttp.OkHttpFutureBackend
Expand All @@ -28,7 +31,8 @@ import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, Batch
import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor
import fr.acinq.eclair.blockchain.bitcoind.{BitcoinCoreWallet, ZmqWatcher}
import fr.acinq.eclair.blockchain.fee._
import fr.acinq.eclair.channel.{Channel, Register, TxPublisher}
import fr.acinq.eclair.channel.{Channel, Register}
import fr.acinq.eclair.crypto.WeakEntropyPool
import fr.acinq.eclair.crypto.keymanager.{LocalChannelKeyManager, LocalNodeKeyManager}
import fr.acinq.eclair.db.Databases.FileBackup
import fr.acinq.eclair.db.{Databases, DbEventHandler, FileBackupHandler}
Expand Down Expand Up @@ -80,6 +84,7 @@ class Setup(datadir: File,
logger.info(s"initializing secure random generator")
// this will force the secure random instance to initialize itself right now, making sure it doesn't hang later (see comment in package.scala)
initRandom()
system.spawn(Behaviors.supervise(WeakEntropyPool()).onFailure(TypedSupervisorStrategy.restart), "entropy-pool")

datadir.mkdirs()
val config = system.settings.config.getConfig("eclair")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2021 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package fr.acinq.eclair.crypto

import akka.actor.typed.Behavior
import akka.actor.typed.eventstream.EventStream
import akka.actor.typed.scaladsl.Behaviors
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Crypto}
import fr.acinq.eclair.blockchain.NewBlock
import fr.acinq.eclair.channel.ChannelSignatureReceived
import fr.acinq.eclair.io.PeerConnected
import fr.acinq.eclair.payment.ChannelPaymentRelayed
import fr.acinq.eclair.router.NodeUpdated
import fr.acinq.eclair.weakRandom
import scodec.bits.ByteVector

import scala.concurrent.duration.DurationInt

/**
* Created by t-bast on 20/04/2021.
*/

/**
* This actor gathers entropy from several events and from the runtime, and regularly injects it into our [[WeakRandom]]
* instance.
*/
object WeakEntropyPool {

// @formatter:off
sealed trait Command
private case object FlushEntropy extends Command
private case class WrappedNewBlock(block: Block) extends Command
private case class WrappedPaymentRelayed(e: ChannelPaymentRelayed) extends Command
private case class WrappedPeerConnected(nodeId: PublicKey) extends Command
private case class WrappedChannelSignature(wtxid: ByteVector32) extends Command
private case class WrappedNodeUpdated(sig: ByteVector64) extends Command
// @formatter:on

def apply(): Behavior[Command] = {
Behaviors.setup { context =>
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewBlock](e => WrappedNewBlock(e.block)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[ChannelPaymentRelayed](e => WrappedPaymentRelayed(e)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[PeerConnected](e => WrappedPeerConnected(e.nodeId)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NodeUpdated](e => WrappedNodeUpdated(e.ann.signature)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[ChannelSignatureReceived](e => WrappedChannelSignature(e.commitments.localCommit.publishableTxs.commitTx.tx.wtxid)))
Behaviors.withTimers { timers =>
timers.startTimerWithFixedDelay(FlushEntropy, 30 seconds)
collecting(ByteVector.empty)
}
}
}

private def collecting(entropy: ByteVector): Behavior[Command] = {
Behaviors.receiveMessage {
case FlushEntropy =>
weakRandom.addEntropy(Crypto.sha256(entropy).toArray)
collecting(ByteVector.empty)

case WrappedNewBlock(block) => collecting(entropy ++ block.hash ++ ByteVector.fromLong(System.currentTimeMillis()))

case WrappedPaymentRelayed(e) => collecting(entropy ++ e.fromChannelId ++ e.toChannelId ++ e.paymentHash ++ ByteVector.fromLong(e.timestamp))

case WrappedPeerConnected(nodeId) => collecting(entropy ++ nodeId.value ++ ByteVector.fromLong(System.currentTimeMillis()))

case WrappedNodeUpdated(sig) => collecting(entropy ++ sig ++ ByteVector.fromLong(System.currentTimeMillis()))

case WrappedChannelSignature(wtxid) => collecting(entropy ++ wtxid ++ ByteVector.fromLong(System.currentTimeMillis()))
}
}

}

0 comments on commit dd903c4

Please sign in to comment.