diff --git a/src/main/scala/uk/ac/soton/ecs/can/core/BlockInitializer.scala b/src/main/scala/uk/ac/soton/ecs/can/core/BlockInitializer.scala new file mode 100644 index 0000000000000000000000000000000000000000..255f000a5a5f48fad367b3fa79701b46e80f3028 --- /dev/null +++ b/src/main/scala/uk/ac/soton/ecs/can/core/BlockInitializer.scala @@ -0,0 +1,41 @@ +package uk.ac.soton.ecs.can.core + +import chisel3._ +import chisel3.util.random.GaloisLFSR + +class BlockInitializer extends MultiIOModule { + val fillConstants = IO(Input(Bool())) + val generateKey = IO(Input(Bool())) + val incrementBlockCount = IO(Input(Bool())) + val generateNonce = IO(Input(Bool())) + val in = IO(Input(Vec(16, UInt(32.W)))) + val out = IO(Output(Vec(16, UInt(32.W)))) + + private val constants = VecInit( + "h61707865".U(32.W), + "h3320646e".U(32.W), + "h79622d32".U(32.W), + "h6b206574".U(32.W) + ) + private val randomKey = (0 until 16).map(_ => GaloisLFSR.maxPeriod(32)) + private val incrementedBlockCount = in(12) + 1.U(32.W) + private val randomNonce = (0 until 3).map(_ => GaloisLFSR.maxPeriod(32)) + + private val io = in.zip(out) + + io.take(4).zip(constants).foreach { case ((i, o), c) => + o := Mux(fillConstants, c, i) + } + + io.slice(4, 12).zip(randomKey).foreach { case ((i, o), k) => + o := Mux(generateKey, k, i) + } + + io(12) match { + case (i, o) => o := Mux(incrementBlockCount, incrementedBlockCount, i) + } + + io.takeRight(3).zip(randomNonce).foreach { case ((i, o), n) => + o := Mux(generateNonce, n, i) + } +} diff --git a/src/test/scala/uk/ac/soton/ecs/can/core/BlockInitializerTest.scala b/src/test/scala/uk/ac/soton/ecs/can/core/BlockInitializerTest.scala new file mode 100644 index 0000000000000000000000000000000000000000..d83bf991e403182c85fbebc2b5486c755c70bde0 --- /dev/null +++ b/src/test/scala/uk/ac/soton/ecs/can/core/BlockInitializerTest.scala @@ -0,0 +1,78 @@ +package uk.ac.soton.ecs.can.core + +import org.scalatest._ +import chiseltest._ +import chisel3._ + +class BlockInitializerTest extends FlatSpec with ChiselScalatestTester { + private val input = Seq.fill(16)(0.U(32.W)) + + behavior of "The Block Initializer" + + it should "fill the ChaCha constant when requested" in { + test(new BlockInitializer) { c => + c.in.zip(input).foreach { case (p, n) => p.poke(n) } + + c.fillConstants.poke(true.B) + c.out(0).expect("h61707865".U(32.W)) + c.out(1).expect("h3320646e".U(32.W)) + c.out(2).expect("h79622d32".U(32.W)) + c.out(3).expect("h6b206574".U(32.W)) + c.out.takeRight(12).foreach(_.expect(0.U(32.W))) + + c.fillConstants.poke(false.B) + c.out.foreach(_.expect(0.U(32.W))) + } + } + + it should "randomly generate some keys when requested" in { + test(new BlockInitializer) { c => + c.in.zip(input).foreach { case (p, n) => p.poke(n) } + + c.generateKey.poke(true.B) + (0 until 10).foreach { _ => + c.out.take(4).foreach(_.expect(0.U(32.W))) + c.out.slice(4, 12).foreach(o => assert(o.peek().litValue() != 0)) + c.out.takeRight(4).foreach(_.expect(0.U(32.W))) + c.clock.step() + } + + c.generateKey.poke(false.B) + c.out.foreach(_.expect(0.U(32.W))) + } + } + + it should "increment the block count when requested" in { + test(new BlockInitializer) { c => + c.in.zip(input).foreach { case (p, n) => p.poke(n) } + + c.incrementBlockCount.poke(true.B) + (0 until 10).foreach { i => + c.in(12).poke(i.U(32.W)) + c.out(12).expect((i + 1).U(32.W)) + } + + c.in(12).poke(0.U(32.W)) + + c.incrementBlockCount.poke(false.B) + c.out.foreach(_.expect(0.U(32.W))) + } + } + + it should "randomly generate some nonce when requested" in { + test(new BlockInitializer) { c => + c.in.zip(input).foreach { case (p, n) => p.poke(n) } + + c.generateNonce.poke(true.B) + (0 until 10).foreach { _ => + c.out.take(9).foreach(_.expect(0.U(32.W))) + c.out.takeRight(3).foreach(o => assert(o.peek().litValue() != 0)) + c.clock.step() + } + + c.generateNonce.poke(false.B) + c.out.foreach(_.expect(0.U(32.W))) + } + } + +}