braindump

This commit is contained in:
2026-07-26 17:35:24 -07:00
parent cedec1ed1f
commit 4f7ade81d0
21 changed files with 1247 additions and 50 deletions

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "common/sim/sub/taxi"]
path = common/sim/sub/taxi
url = https://github.com/fpganinja/taxi.git
[submodule "common/sim/sub/fifo"]
path = common/sim/sub/fifo
url = https://github.com/olofk/fifo.git

View File

@@ -24,3 +24,35 @@ Module inputs and outputs
There is no output backpressure. since the result is just a single 128 bit number,
we don't need to have a ready signal. if you want to add backpressure, add a register
slice on the output outside of this module.
the real requirement was to get something that works...
we can pipeline chacha20 as much as we want since it is trivially pipelined
since poly1305 takes in a 32 bit stream, we can redo chacha20 to work on 32 bits
at a time instead of 256 bits. We could also reuse stages, so instead of needing
all 20 stages we can just have 2 or something.
BUT since the number 1 requirement is to get it to work, lets just use what we already have.
okay they 512 bit chacha implementation is a bit crazy. the latency is too high. we need
to come up with a way to do it 32 bits at a time.
each quarter round generates 128 bits, so we could do 1 quarter round at a time and get 128
bits per clock cycle, which is plenty fast.
if we do 1 quarter round per clock cycle, There are 20 rounds, each of which is 4 quarter
rounds, so it would take 80 cycles.
Compared to the current implementation which has 20 rounds, but each round takes 7 cycles,
this has almost half the latency. We still need to store the full state between each round,
which is 512 bits.
OHHH there is a big different here with encrypt vs decrypt. basically, it is always
the encrypted packet that goes through poly1305. If the packet is being encrypted then
it needs to get xored before going into poly1305. If the packet is being decrypted, then it
needs to go through poly1305 in parallel with being xored.

View File

@@ -0,0 +1,105 @@
# Data from wireshark
Ok lets work this out by hand. The encyrpted packet matches exaactly, so we
know that the chacha20 part is correct, and this must include the first block
which is used for the keys.
84 bytes rounded to nearest multiple of 16 is 96 bytes. plus 16 for tag is 112.
so we do know that wireguard is including the tag.
the first output of chacha20, which becomes the keys, is
118b9bfd676a3bd991483cb1746252272e032bfbf2597dafec72576a54d9263ae32c815b30dbd75e7000d9ec14aac879075ada63f40d22180741336f9132e14a
The bottm 256 bits of that is
e32c815b30dbd75e7000d9ec14aac879075ada63f40d22180741336f9132e14a
splitting that into 128 bit sections, we get
0xe32c815b30dbd75e7000d9ec14aac879
0x075ada63f40d22180741336f9132e14a
r_mask is 0x0ffffffc0ffffffc0ffffffc0fffffff
so out final values of r and s are
r = 0x075ada60040d22180741336c0132e14a
s = 0xe32c815b30dbd75e7000d9ec14aac879
oh and p = 2**130-5
These values line up with what what we see in the hardware.
according to the spec, the algorithm for poly1305 is as follows:
```text
a = 0 /* a is the accumulator */
p = (1<<130)-5
for i=1 upto ceil(msg length in bytes / 16)
n = le_bytes_to_num(msg[((i-1)*16)..(i*16)] | [0x01])
a += n
a = (r * a) % p
end
a += s
return num_to_16_le_bytes(a)
end
```
Here is the cipher, which is msg in this case
b"\xa4\xeb\xc1.\xe3\xf9\x90\xda\x18\x03:\x07\x89\xc0N'\x00\xf6\xf5\xc2q\xd4*\xc4\xb4\xd6&.feI\xb4E\xa7Cn\x82\x9b\xff\xb6\xace\xf0VH\xbc\x0c9\x1f\xe7\xc5\x88Ht7a'\x16I@\x18\x8f\x03\xdb\xa6z\xf88\x8e\xaa\xb7lY6(\xbf\x9d\xc7\xbe\x034m\x91.\x91m\xad\x86%EEG\x016O-"
here it is split up into 16 byte chunks
b"\xa4\xeb\xc1.\xe3\xf9\x90\xda\x18\x03:\x07\x89\xc0N'"
b'\x00\xf6\xf5\xc2q\xd4*\xc4\xb4\xd6&.feI\xb4'
b'E\xa7Cn\x82\x9b\xff\xb6\xace\xf0VH\xbc\x0c9'
b"\x1f\xe7\xc5\x88Ht7a'\x16I@\x18\x8f\x03\xdb"
b'\xa6z\xf88\x8e\xaa\xb7lY6(\xbf\x9d\xc7\xbe\x03'
b'4m\x91.\x91m\xad\x86%EEG\x016O-'
The length of the message is 96 bytes, so we have our 6 16 byte chunks.
the next step is to interpret these as little endian integers, then add a leading 1.
0x1274ec089073a0318da90f9e32ec1eba4
0x1b44965662e26d6b4c42ad471c2f5f600
0x1390cbc4856f065acb6ff9b826e43a745
0x1db038f18404916276137744888c5e71f
0x103bec79dbf2836596cb7aa8e38f87aa6
0x12d4f36014745452586ad6d912e916d34
DON'T FORGET ABOUT THE LENGTHS!
The length of AAD is 0, and the length of the ciphertext is 96,
that becomes
this is aad length, data_length
be careful of the order, since aad length comes first, it is on the
right side of this hex number, since its little endian.
0x100000000000000600000000000000000
And these also match the hardware, so we are doing good so far.
so after the first round, a = (r * n) % p, since a started at 0, a+=n is just n
so a = 0xcfd86a9543d3377baf5be686c46d8491
ok looks good so far.
next is 0x312e451b2a526811c5bd976ce3c27d5e7, looks good as well. the next values in a row are
0x22097b74ef42792da5e02cf9dd8249776
0x2586a4ee24499da642fb6392be59137b2
0xff1ba4984e72db7199c786a00de9de1e
0x2850e6cbbf9869bf410976e82e2ee9b04
0x38878751382017086bc63eee8ba2cbdab
which all match still. So the final step is to add s,
0x46ba4f66eb2dd47e52c64c8d4ced78624
SO basically we need to appaned the length to the data when it is going through poly1305.
poly1305 should be able to look at the input fifo and know when it is outputting the last
valid beat. After this, we need to output the lengths.

View File

@@ -0,0 +1,16 @@
import base64
from chacha20poly1305 import ChaCha20Poly1305
cleartext=base64.decodebytes(b"RQAAVOVGQABAAUFMCgoAAgoKAAEIAPlcbAAAAG+tIvUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
key = base64.decodebytes(b"IA7buFUDsARj+YequW4OfQR3JsaE9M88dpXKrDjKzIs=")
nonce = bytes([0]*12)
cip = ChaCha20Poly1305(key)
ciphertag = cip.encrypt(nonce, cleartext)
ciphertext = ciphertag[:-16]
tag = ciphertag[-16:]
print(ciphertext)
print(tag)

View File

@@ -1 +1 @@
create_clock -period 2.5 -name clk [get_ports i_clk]
create_clock -period 20 -name clk [get_ports i_clk]

View File

@@ -1,16 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<efx:project name="poly1305_timing_test" description="" last_change="1752448578" sw_version="2025.1.110" last_run_state="pass" last_run_flow="bitstream" config_result_in_sync="true" design_ood="sync" place_ood="sync" route_ood="sync" xmlns:efx="http://www.efinixinc.com/enf_proj" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.efinixinc.com/enf_proj enf_proj.xsd">
<efx:project name="poly1305_timing_test" description="" last_change="1784047128" sw_version="2025.1.110" last_run_state="pass" last_run_flow="bitstream" config_result_in_sync="true" design_ood="sync" place_ood="sync" route_ood="sync" xmlns:efx="http://www.efinixinc.com/enf_proj" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.efinixinc.com/enf_proj enf_proj.xsd">
<efx:device_info>
<efx:family name="Titanium"/>
<efx:device name="Ti375N1156"/>
<efx:timing_model name="C4"/>
</efx:device_info>
<efx:design_info def_veri_version="sv_09" def_vhdl_version="vhdl_2008" unified_flow="false">
<efx:top_module name="mult_timing_test"/>
<efx:design_file name="../src/poly1305_core.sv" version="default" library="default"/>
<efx:design_file name="../../common/sim/sub/taxi/src/axis/rtl/taxi_axis_if.sv" version="default" library="default"/>
<efx:design_file name="../sim/poly1305_core_wrapper.sv" version="default" library="default"/>
<efx:design_file name="mult_timing_test.sv" version="default" library="default"/>
<efx:top_module name="chacha20_flat_qr"/>
<efx:design_file name="../src/poly1305_ll_stage.sv" version="default" library="default"/>
<efx:design_file name="../src/poly1305_width_convert.sv" version="default" library="default"/>
<efx:design_file name="../src/chacha20_flat_qr.sv" version="default" library="default"/>
<efx:top_vhdl_arch name=""/>
</efx:design_info>
<efx:constraint_info>
@@ -41,7 +40,7 @@
<efx:param name="min-sr-fanout" value="0" value_type="e_integer"/>
<efx:param name="min-ce-fanout" value="0" value_type="e_integer"/>
<efx:param name="mode" value="speed" value_type="e_option"/>
<efx:param name="mult-auto-pipeline" value="1" value_type="e_integer"/>
<efx:param name="mult-auto-pipeline" value="0" value_type="e_integer"/>
<efx:param name="mult-decomp-retime" value="1" value_type="e_option"/>
<efx:param name="operator-sharing" value="1" value_type="e_option"/>
<efx:param name="optimize-adder-tree" value="1" value_type="e_option"/>

View File

@@ -0,0 +1,208 @@
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import Timer, RisingEdge, FallingEdge
from cocotb.handle import Immediate
from cocotb.queue import Queue
from dataclasses import dataclass
from chacha_helpers import chacha_block
import random
import base64
import struct
CLK_PERIOD = 20
NONCE_BYTES = b"\x00\x00\x00\x00\x00\x00\x00\x4a\x00\x00\x00\x00"
KEY_BYTES = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
MSG_BYTES = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
NONCE = int.from_bytes(NONCE_BYTES, "little")
KEY = int.from_bytes(KEY_BYTES, "little")
CONSTANT = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]
output_queue = Queue()
async def receieve_output(dut):
packet = bytearray()
while True:
if dut.o_data_valid.value:
packet.extend(int(dut.o_data.value).to_bytes(4, "little"))
if dut.o_data_last.value:
await output_queue.put(bytes(packet))
packet = bytearray()
await RisingEdge(dut.i_clk)
# @cocotb.test
async def test_sanity(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(receieve_output(dut))
for counter in range(3):
data_in = CONSTANT[:]
data_in.extend(struct.unpack("8I", KEY_BYTES))
data_in.extend(struct.unpack("1I", counter.to_bytes(4, "little")))
data_in.extend(struct.unpack("3I", NONCE_BYTES))
data_out = chacha_block(data_in)
print([f"{data:x}" for data in data_out])
dut.i_rst.value = Immediate(0)
await RisingEdge(dut.i_clk)
await RisingEdge(dut.i_clk)
dut.i_rst.value = 1
await RisingEdge(dut.i_clk)
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await RisingEdge(dut.i_clk)
await RisingEdge(dut.i_clk)
dut.i_nonce.value = NONCE
dut.i_key.value = KEY
dut.i_data_ready.value = 1
N_PKTS = 100
for _ in range(N_PKTS):
dut.i_last.value = 0
dut.i_valid.value = 1
msg = MSG_BYTES
while len(msg) > 0:
part = msg[0:4]
dut.i_data.value = int.from_bytes(part, "little")
dut.i_countm1.value = len(part)-1
if len(msg) < 4:
dut.i_last.value = 1
msg = msg[4:]
await RisingEdge(dut.i_clk)
while not dut.o_ready.value:
await RisingEdge(dut.i_clk)
dut.i_valid.value = 0
dut.i_last.value = 0
for _ in range(N_PKTS):
packet = await output_queue.get()
print(packet)
@dataclass
class CipherContext():
key: int
counter: int
encrypt: int
tag: int
cipher: bytes
clear: bytes
MESSAGE_KEY_1_BYTES = base64.decodebytes(b"IA7buFUDsARj+YequW4OfQR3JsaE9M88dpXKrDjKzIs=")
MESSAGE_KEY_2_BYTES = base64.decodebytes(b"3sZ+QBcwflnvE8t4jv9qgIqIOVaXXwO1bxRo9qWB1fM=")
MESSAGE_KEY_1 = int.from_bytes(MESSAGE_KEY_1_BYTES, "little")
MESSAGE_KEY_2 = int.from_bytes(MESSAGE_KEY_2_BYTES, "little")
PACKETS = [
CipherContext(
key=MESSAGE_KEY_1,
counter=0,
encrypt=1,
tag=int.from_bytes(base64.decodebytes(b"BAAAAAb0fasAAAAAAAAAAKTrwS7j+ZDaGAM6B4nATicA9vXCcdQqxLTWJi5mZUm0RadDboKb/7asZfBWSLwMOR/nxYhIdDdhJxZJQBiPA9umevg4jqq3bFk2KL+dx74DNG2RLpFtrYYlRUVHATZPLSSG187UyGQs5Ufdsm72pGs=")[-16:], "little"),
cipher=base64.decodebytes(b"BAAAAAb0fasAAAAAAAAAAKTrwS7j+ZDaGAM6B4nATicA9vXCcdQqxLTWJi5mZUm0RadDboKb/7asZfBWSLwMOR/nxYhIdDdhJxZJQBiPA9umevg4jqq3bFk2KL+dx74DNG2RLpFtrYYlRUVHATZPLSSG187UyGQs5Ufdsm72pGs=")[16:-16],
clear=base64.decodebytes(b"RQAAVOVGQABAAUFMCgoAAgoKAAEIAPlcbAAAAG+tIvUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
),
CipherContext(
key=MESSAGE_KEY_2,
counter=0,
encrypt=0,
tag=int.from_bytes(base64.decodebytes(b"BAAAANg30DAAAAAAAAAAAG9PCA6fUmkbvpSFNfecE+1o8JFF1SPu2whyZfloCC9wc1cpJj7aYnx2g83AuAozVtlTbJ8OKHJ5e1yBcguguOpyM8bev58PvujxDsGJhbgkvzUPi4GA0Ipk5r6YEAiaw9E2PtXhKcoeBCXPfpSWVlk=")[-16:], "little"),
cipher=base64.decodebytes(b"BAAAANg30DAAAAAAAAAAAG9PCA6fUmkbvpSFNfecE+1o8JFF1SPu2whyZfloCC9wc1cpJj7aYnx2g83AuAozVtlTbJ8OKHJ5e1yBcguguOpyM8bev58PvujxDsGJhbgkvzUPi4GA0Ipk5r6YEAiaw9E2PtXhKcoeBCXPfpSWVlk=")[16:-16],
clear=base64.decodebytes(b"RQAAVKEdAABAAcV1CgoAAQoKAAIAAAFdbAAAAG+tIvUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
)
]
tag_queue = Queue()
async def tag_output_process(dut):
while True:
if dut.o_mac_valid.value:
await tag_queue.put(int(dut.o_mac.value))
await RisingEdge(dut.i_clk)
@cocotb.test
async def test_real_packet(dut):
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())
cocotb.start_soon(receieve_output(dut))
cocotb.start_soon(tag_output_process(dut))
dut.i_rst.value = Immediate(0)
await RisingEdge(dut.i_clk)
await RisingEdge(dut.i_clk)
dut.i_rst.value = 1
await RisingEdge(dut.i_clk)
await RisingEdge(dut.i_clk)
dut.i_rst.value = 0
await RisingEdge(dut.i_clk)
await RisingEdge(dut.i_clk)
for pkt in PACKETS:
dut.i_nonce.value = pkt.counter
dut.i_key.value = pkt.key
dut.i_encrypt.value = pkt.encrypt
msg = pkt.clear if pkt.encrypt else pkt.cipher
dut.i_data_ready.value = 1
dut.i_last.value = 0
dut.i_valid.value = 1
while len(msg) > 0:
part = msg[0:4]
dut.i_data.value = int.from_bytes(part, "little")
dut.i_countm1.value = len(part)-1
if len(msg) <= 4:
dut.i_last.value = 1
msg = msg[4:]
await RisingEdge(dut.i_clk)
while not dut.o_ready.value:
await RisingEdge(dut.i_clk)
dut.i_valid.value = 0
dut.i_last.value = 0
dut.i_nonce.value = 0
dut.i_key.value = 0
for pkt in PACKETS:
packet = await output_queue.get()
expected = pkt.cipher if pkt.encrypt else pkt.clear
# The packet is rounded up to 16 bytes before encryption, but based on the IP length we
# are supposed to truncate it. That would happen outside of this block though.
if expected not in packet:
print("Packets didn't match!")
print(f"expected={expected} actual={packet}")
tag = await tag_queue.get()
if tag != pkt.tag:
print("Tags did not match!")
print(f"expected={pkt.tag:x} actual={tag:x}")
await Timer(10, "us")

View File

@@ -34,4 +34,10 @@ tests:
modules:
- "poly1305_ll_stage"
sources: sources.list
waves: True
- name: "chacha20_poly1305_32_ll_core"
toplevel: "chacha20_poly1305_32_ll_core"
modules:
- "chacha20_poly1305_32_ll_core"
sources: sources.list
waves: True

View File

@@ -30,27 +30,28 @@ async def test_sanity(dut):
print(data_in)
async def input_data():
for count, word in zip(countm1, data_in[:-1]):
dut.i_data.value = word
dut.i_countm1.value = count
for _ in range(2):
for count, word in zip(countm1, data_in[:-1]):
dut.i_data.value = word
dut.i_countm1.value = count
dut.i_valid.value = 1
dut.i_last.value = 0
await RisingEdge(dut.i_clk)
while not dut.o_ready.value:
await RisingEdge(dut.i_clk)
dut.i_data.value = data_in[-1]
dut.i_countm1.value = countm1[-1]
dut.i_valid.value = 1
dut.i_last.value = 0
dut.i_last.value = 1
await RisingEdge(dut.i_clk)
while (not dut.o_ready.value):
await RisingEdge(dut.i_clk)
dut.i_data.value = data_in[-1]
dut.i_countm1.value = countm1[-1]
dut.i_valid.value = 1
dut.i_last.value = 1
await RisingEdge(dut.i_clk)
while (not dut.o_ready.value):
await RisingEdge(dut.i_clk)
dut.i_valid.value = 0
dut.i_last.value = 0
dut.i_valid.value = 0
dut.i_last.value = 0
cocotb.start_soon(Clock(dut.i_clk, CLK_PERIOD, unit="ns").start())

View File

@@ -2,3 +2,8 @@ poly1305_core_wrapper.sv
../src/sources.list
../../common/sim/sub/taxi/src/axis/rtl/taxi_axis_if.sv
../../common/sim/sub/fifo/rtl/verilog/fifo_fwft_adapter.v
../../common/sim/sub/fifo/rtl/verilog/fifo.v
../../common/sim/sub/fifo/rtl/verilog/fifo_fwft.v
../../common/sim/sub/fifo/rtl/verilog/simple_dpram_sclk.v

View File

@@ -1,10 +1,10 @@
module chacha20_block #(
parameter KEY_SIZE = 256,
parameter COUNTER_SIZE = 64,
parameter NONCE_SIZE = 64,
parameter COUNTER_SIZE = 32,
parameter NONCE_SIZE = 96,
parameter STATE_SIZE = 512,
parameter ROUNDS = 20,
parameter CONSTANT = 128'h657870616e642033322d62797465206b
parameter CONSTANT = 128'h617078653320646e79622d326b206574
)(
input logic i_clk,
input logic i_rst,
@@ -68,7 +68,7 @@ always_ff @(posedge i_clk) begin
initial_state_rptr <= '0;
initial_state_wptr <= '0;
end else begin
if (i_valid) begin
if (i_valid & i_ready) begin
initial_states[initial_state_wptr] <= write_initial_state;
if (initial_state_wptr < PIPE_STAGES-1) begin
initial_state_wptr <= initial_state_wptr + 1;
@@ -79,7 +79,7 @@ always_ff @(posedge i_clk) begin
pre_add_valid <= valid[ROUNDS][0];
if (valid[ROUNDS][0]) begin
if (valid[ROUNDS][0] & i_ready) begin
read_initial_state <= initial_states[initial_state_rptr];
if (initial_state_rptr < PIPE_STAGES-1) begin
initial_state_rptr <= initial_state_rptr + 1;
@@ -92,13 +92,15 @@ always_ff @(posedge i_clk) begin
end
o_valid <= pre_add_valid;
if (i_ready) begin
o_valid <= pre_add_valid;
// We cannot just add state_pre_add and read_initial state
// because the addition needs to be done wordwise, with no
// carries between 32 bit groups.
for (int i = 0; i < 16; i++) begin
o_state[i*32 +: 32] <= state_pre_add[i*32 +: 32] + read_initial_state[i*32 +: 32];
// We cannot just add state_pre_add and read_initial state
// because the addition needs to be done wordwise, with no
// carries between 32 bit groups.
for (int i = 0; i < 16; i++) begin
o_state[i*32 +: 32] <= state_pre_add[i*32 +: 32] + read_initial_state[i*32 +: 32];
end
end
end
@@ -115,10 +117,10 @@ always_comb begin
end
state[0][12] = i_counter[0 +: 32];
state[0][13] = i_counter[32 +: 32];
state[0][14] = i_nonce[0 +: 32];
state[0][15] = i_nonce[32 +: 32];
state[0][13] = i_nonce[0 +: 32];
state[0][14] = i_nonce[32 +: 32];
state[0][15] = i_nonce[64 +: 32];
for (int i = 0; i < 4; i++) begin

View File

@@ -0,0 +1,320 @@
module chacha20_poly1305_32_ll_core (
input logic i_clk,
input logic i_rst,
input logic [255:0] i_key,
input logic [95:0] i_nonce,
input logic i_encrypt,
input logic [31:0] i_data,
input logic [1:0] i_countm1,
input logic i_last,
input logic i_valid,
output logic o_ready,
output logic [31:0] o_data,
output logic [1:0] o_data_countm1,
output logic o_data_last,
output logic o_data_valid,
input logic i_data_ready,
// no backpressure for the poly1305 output
output logic [127:0] o_mac,
output logic o_mac_valid
);
logic [255:0] chacha_key;
logic [31:0] chacha_counter;
logic [95:0] chacha_nonce;
logic chacha_i_valid;
logic chacha_i_ready;
logic [511:0] chacha_state;
logic chacha_o_valid;
logic chacha_o_ready;
logic poly1305_i_valid;
logic [1:0] poly1305_countm1;
logic poly1305_i_ready;
logic [31:0] poly1305_data;
logic poly1305_last;
logic [127:0] poly1305_r;
logic [127:0] poly1305_s;
logic downconvert_i_valid;
logic [1:0] downconvert_countm1;
logic downconvert_i_ready;
logic [31:0] downconvert_data;
logic downconvert_last;
logic poly1305_o_valid;
logic [127:0] poly1305_o_result;
logic [511:0] fifo_data;
logic [5:0] fifo_countm1;
logic fifo_last;
logic fifo_rd_en;
logic fifo_empty;
logic fifo_full;
logic fifo_valid;
logic upconvert_ready;
logic upconvert_valid;
logic upconvert_last;
logic [511:0] upconvert_data;
logic [5:0] upconvert_countm1;
logic ciphertext_ready;
logic ciphertext_valid;
logic ciphertext_last;
logic [511:0] ciphertext_data;
logic [5:0] ciphertext_countm1;
logic ciphertext_skid_ready;
logic ciphertext_skid_valid;
logic ciphertext_skid_last;
logic [511:0] ciphertext_skid_data;
logic [5:0] ciphertext_skid_countm1;
logic [511:0] post_data;
logic post_skid_ready;
logic post_skid_valid;
logic post_skid_last;
logic [511:0] post_skid_data;
logic [5:0] post_skid_countm1;
logic meta_fifo_encrypt;
logic meta_fifo_read;
logic meta_fifo_empty;
assign chacha_nonce = i_nonce;
assign chacha_key = i_key;
assign fifo_valid = ~fifo_empty;
assign o_mac_valid = poly1305_o_valid;
assign o_mac = poly1305_o_result;
poly1305_width_convert_2 u_chacha_width_convert (
.i_clk (i_clk),
.i_rst (i_rst),
.i_valid (i_valid),
.i_countm1 (i_countm1),
.o_ready (o_ready),
.i_data (i_data),
.i_last (i_last),
.o_valid (upconvert_valid),
.o_countm1 (upconvert_countm1),
.i_ready (upconvert_ready & chacha_o_ready),
.o_data (upconvert_data),
.o_last (upconvert_last)
);
assign upconvert_ready = ~fifo_full;
logic sop;
logic in_pkt;
always_ff @(posedge i_clk) begin
if (i_rst) begin
in_pkt <= '0;
end else begin
if (i_valid & o_ready) begin
if (i_last) begin
in_pkt <= '0;
end else begin
in_pkt <= '1;
end
end
end
end
assign sop = i_valid & o_ready & ~in_pkt;
fifo_fwft #(
.DATA_WIDTH(512+6+1),
.DEPTH_WIDTH(8)
) u_input_fifo (
.clk (i_clk),
.rst (i_rst),
.din ({upconvert_data, upconvert_countm1, upconvert_last}),
.wr_en (upconvert_valid & chacha_o_ready),
.full (fifo_full), // probably wrong, think about this
.dout ({fifo_data, fifo_countm1, fifo_last}),
.rd_en (fifo_rd_en),
.empty (fifo_empty)
);
fifo_fwft #(
.DATA_WIDTH(1), // can increase when we have more metadata
.DEPTH_WIDTH(4)
) u_meta_fifo (
.clk (i_clk),
.rst (i_rst),
.din (i_encrypt),
.wr_en (sop),
.full (),
.dout (meta_fifo_encrypt),
.rd_en (meta_fifo_read),
.empty (meta_fifo_empty)
);
chacha20_poly1305_input_fsm u_chacha20_poly1305_input_fsm (
.i_clk (i_clk),
.i_rst (i_rst),
.i_valid (upconvert_valid),
.i_last (upconvert_last),
.o_counter (chacha_counter),
.chacha_valid (chacha_i_valid),
.chacha_ready (chacha_i_ready)
);
chacha20_block u_chacha20_block (
.i_clk (i_clk),
.i_rst (i_rst),
.i_key (chacha_key),
.i_counter (chacha_counter),
.i_nonce (chacha_nonce),
.i_valid (chacha_i_valid),
.o_ready (chacha_i_ready),
.o_state (chacha_state),
.o_valid (chacha_o_valid),
.i_ready (chacha_o_ready)
);
chacha20_poly1305_mid_fsm u_mid_fsm (
.i_clk (i_clk),
.i_rst (i_rst),
.i_chacha_state (chacha_state),
.i_chacha_valid (chacha_o_valid),
.o_chacha_ready (chacha_o_ready),
.i_metadata_din (meta_fifo_encrypt),
.o_metadata_read (meta_fifo_read),
.i_fifo_data (fifo_data),
.i_fifo_countm1 (fifo_countm1),
.i_fifo_valid (fifo_valid),
.i_fifo_last (fifo_last),
.o_fifo_rd_en (fifo_rd_en),
.o_ciphertext (ciphertext_data),
.o_post (post_data),
.o_ciphertext_countm1 (ciphertext_countm1),
.o_ciphertext_valid (ciphertext_valid),
.o_ciphertext_last (ciphertext_last),
.i_ciphertext_ready (ciphertext_ready),
// TODO this is most likely wrong.
.o_r (poly1305_r),
.o_s (poly1305_s)
);
logic rstn;
assign rstn = ~i_rst;
pipe_skid_buffer #(
.DWIDTH(512+512+6+1)
) u_ciphertext_skid_buffer (
.clk (i_clk),
.rstn (rstn),
.i_data ({post_data, ciphertext_data, ciphertext_countm1, ciphertext_last}),
.i_valid (ciphertext_valid),
.o_ready (ciphertext_ready),
.o_data ({post_skid_data, ciphertext_skid_data, ciphertext_skid_countm1, ciphertext_skid_last}),
.o_valid (ciphertext_skid_valid),
.i_ready (ciphertext_skid_ready)
);
poly1305_width_convert_3 u_pre_poly1305_width_convert (
.i_clk (i_clk),
.i_rst (i_rst),
.i_valid (ciphertext_skid_valid),
.i_countm1 (ciphertext_skid_countm1),
.o_ready (ciphertext_skid_ready),
.i_data (ciphertext_skid_data),
.i_last (ciphertext_skid_last),
.o_valid (poly1305_i_valid),
.o_countm1 (poly1305_countm1),
.i_ready (poly1305_i_ready & ~output_fifo_full),
.o_data (poly1305_data),
.o_last (poly1305_last)
);
poly1305_width_convert_3 u_output_width_convert (
.i_clk (i_clk),
.i_rst (i_rst),
.i_valid (ciphertext_skid_valid),
.i_countm1 (ciphertext_skid_countm1),
.o_ready (),
.i_data (post_skid_data),
.i_last (ciphertext_skid_last),
.o_valid (downconvert_i_valid),
.o_countm1 (downconvert_countm1),
.i_ready (poly1305_i_ready & ~output_fifo_full),
.o_data (downconvert_data),
.o_last (downconvert_last)
);
fifo_fwft #(
.DATA_WIDTH(32+1),
.DEPTH_WIDTH(4)
) u_output_fifo (
.clk (i_clk),
.rst (i_rst),
.din ({downconvert_data, downconvert_last}),
.wr_en (downconvert_i_valid & poly1305_i_ready & ~output_fifo_full),
.full (output_fifo_full),
.dout ({o_data, o_data_last}),
.rd_en (o_data_valid & i_data_ready),
.empty (output_fifo_empty)
);
assign o_data_valid = ~output_fifo_empty;
logic output_fifo_full;
logic output_fifo_empty;
// if output fifo is not ready, we need to backpressure poly1305
poly1305_ll_stage u_poly1305_ll_stage (
.i_clk (i_clk),
.i_rst (i_rst),
.i_valid (poly1305_i_valid & ~output_fifo_full),
.i_countm1 (poly1305_countm1),
.o_ready (poly1305_i_ready),
.i_data (poly1305_data),
.i_last (poly1305_last),
.i_r (poly1305_r),
.i_s (poly1305_s),
.o_valid (poly1305_o_valid),
.o_result (poly1305_o_result)
);
endmodule

View File

@@ -0,0 +1,88 @@
module chacha20_poly1305_input_fsm (
input logic i_clk,
input logic i_rst,
input logic i_valid,
input logic i_last,
output logic [31:0] o_counter,
output logic chacha_valid,
input logic chacha_ready
);
enum logic [1:0] {IDLE, ACTIVE1, ACTIVE2, ACTIVE3} state, state_next;
logic [31:0] counter, counter_next;
// in case the first beat is also the last one.
logic last_flag, last_flag_next;
assign o_counter = counter;
always_ff @(posedge i_clk) begin
if (i_rst) begin
state <= IDLE;
counter <= '0;
last_flag <= '0;
end else begin
state <= state_next;
counter <= counter_next;
last_flag <= last_flag_next;
end
end
always_comb begin
state_next = state;
counter_next = counter;
last_flag_next = '0;
chacha_valid = '0;
case (state)
IDLE: begin
counter_next = '0;
chacha_valid = '0;
if (i_valid) begin
chacha_valid = '1;
state_next = ACTIVE1;
counter_next = counter + 1;
last_flag_next = i_last;
end
end
ACTIVE1: begin
chacha_valid = '1;
if (chacha_ready) begin
state_next = ACTIVE2;
counter_next = counter + 1;
end
end
ACTIVE2: begin
if (i_valid) begin
chacha_valid = '1;
if (chacha_ready) begin
counter_next = counter + 1;
if (i_last || last_flag) begin
state_next = IDLE;
counter_next = '0;
end
end
end
end
default: begin
state_next = IDLE;
end
endcase
end
endmodule

View File

@@ -0,0 +1,110 @@
module chacha20_poly1305_mid_fsm (
input logic i_clk,
input logic i_rst,
input logic [511:0] i_chacha_state,
input logic i_chacha_valid,
output logic o_chacha_ready,
input logic [511:0] i_fifo_data,
input logic [5:0] i_fifo_countm1,
input logic i_fifo_valid,
input logic i_fifo_last,
output logic o_fifo_rd_en,
input logic [0:0] i_metadata_din,
output logic o_metadata_read,
output logic [511:0] o_ciphertext,
output logic [511:0] o_post,
output logic [5:0] o_ciphertext_countm1,
output logic o_ciphertext_valid,
output logic o_ciphertext_last,
input logic i_ciphertext_ready,
output logic [127:0] o_r,
output logic [127:0] o_s
);
localparam R_MASK = 128'h0ffffffc0ffffffc0ffffffc0fffffff;
logic encrypt;
assign encrypt = i_metadata_din[0];
enum logic {IDLE, DATA} state, state_next;
logic [127:0] r, r_next;
logic [127:0] s, s_next;
assign o_r = r;
assign o_s = s;
always_ff @(posedge i_clk) begin
if (i_rst) begin
state <= IDLE;
r <= '0;
s <= '0;
end else begin
state <= state_next;
r <= r_next;
s <= s_next;
end
end
always_comb begin
state_next = state;
r_next = r;
s_next = s;
o_fifo_rd_en = '0;
o_chacha_ready = i_ciphertext_ready;
o_ciphertext_valid = '0;
o_ciphertext_last = '0;
o_metadata_read = '0;
case (state)
IDLE: begin
if (i_chacha_valid) begin
r_next = i_chacha_state[0 +: 128] & R_MASK;
s_next = i_chacha_state[128 +: 128];
state_next = DATA;
end
end
DATA: begin
if (encrypt) begin
o_ciphertext = i_fifo_data ^ i_chacha_state;
end else begin
o_ciphertext = i_fifo_data;
end
o_post = i_fifo_data ^ i_chacha_state;
o_ciphertext_countm1 = i_fifo_countm1;
o_ciphertext_last = i_fifo_last;
o_ciphertext_valid = i_fifo_valid & i_chacha_valid;
if (i_ciphertext_ready & i_chacha_valid) begin
o_chacha_ready = '1;
o_fifo_rd_en = i_fifo_valid;
if (i_fifo_last) begin
state_next = IDLE;
o_metadata_read = '1;
end
end
end
endcase
end
endmodule

View File

@@ -18,7 +18,7 @@ module poly1305_ll_stage (
output logic [127:0] o_result
);
localparam [129:0] PRIME = (1 << 130) - 5;
localparam [129:0] PRIME = 130'h3fffffffffffffffffffffffffffffffb;
logic upconvert_ready;
logic upconvert_valid;
@@ -47,7 +47,7 @@ poly1305_width_convert u_width_convert(
logic [127:0] r_reg, s_reg, s_reg_2;
logic [127:0] r_reg_next, s_reg_next, s_reg_2_next;;
enum logic [2:0] {IDLE, SUM, MULT, MODULO_1, MODULO_2} state, state_next;
enum logic [2:0] {IDLE, SUM, LENGTHS, MULT, MODULO_1, MODULO_2} state, state_next;
enum logic [1:0] {OUTPUT_IDLE, OUTPUT_ADD, OUTPUT_OUT} output_state, output_state_next;
@@ -72,6 +72,9 @@ logic [130:0] final_mod;
logic start_output;
logic last_flag, last_flag_next;
logic length_flag, length_flag_next;
logic [63:0] data_len, data_len_next;
always_ff @(posedge i_clk) begin
if (i_rst) begin
@@ -79,7 +82,10 @@ always_ff @(posedge i_clk) begin
s_reg <= '0;
s_reg_2 <= '0;
data_len <= '0;
last_flag <= '0;
length_flag <= '0;
state <= IDLE;
output_state <= OUTPUT_IDLE;
@@ -88,6 +94,8 @@ always_ff @(posedge i_clk) begin
s_reg <= s_reg_next;
s_reg_2 <= s_reg_2_next;
data_len <= data_len_next;
mod2_upper <= mod2_upper_next;
mod2_lower <= mod2_lower_next;
@@ -97,6 +105,7 @@ always_ff @(posedge i_clk) begin
product <= product_next;
last_flag <= last_flag_next;
length_flag <= length_flag_next;
state <= state_next;
output_state <= output_state_next;
@@ -114,9 +123,12 @@ always_comb begin
r_reg_next = r_reg;
s_reg_next = s_reg;
data_len_next = data_len;
upconvert_ready = '0;
last_flag_next = last_flag;
length_flag_next = length_flag;
state_next = state;
@@ -138,6 +150,8 @@ always_comb begin
data_1extend = {3'b0, upconvert_data} | (131'b1 << (8*(1+upconvert_countm1)));
sum_next = data_1extend;
data_len_next = {60'd0, upconvert_countm1} + 1;
state_next = MULT;
r_reg_next = i_r;
@@ -152,6 +166,17 @@ always_comb begin
data_1extend = {3'b0, upconvert_data} | (131'b1 << (8*(1+upconvert_countm1)));
sum_next = h + data_1extend;
data_len_next = data_len + {60'd0, upconvert_countm1} + 1;
state_next = MULT;
end
LENGTHS: begin
length_flag_next = '1;
data_1extend = {3'b0, data_len, 64'b0} | (131'b1 << 128);
sum_next = h + data_1extend;
state_next = MULT;
end
@@ -180,10 +205,16 @@ always_comb begin
end
if (last_flag) begin
state_next = IDLE;
state_next = LENGTHS;
last_flag_next = '0;
end else if (length_flag) begin
length_flag_next = '0;
s_reg_2_next = s_reg;
start_output = '1;
data_len_next = '0;
state_next = IDLE;
end else begin
state_next = SUM;
end
@@ -197,6 +228,9 @@ always_comb begin
output_state_next = output_state;
o_result = '0;
o_valid = '0;
case (output_state)
OUTPUT_IDLE: begin
if (start_output) begin
@@ -210,20 +244,14 @@ always_comb begin
end
OUTPUT_OUT: begin
if (final_sum > {1'b0, PRIME}) begin
final_mod = final_sum - PRIME;
end else begin
final_mod = final_sum;
end
o_valid = '1;
o_result = final_mod[127:0];
o_result = final_sum[127:0];
output_state_next = OUTPUT_IDLE;
end
default: begin
output_state_next = OUTPUT_IDLE;
end
endcase
end

View File

@@ -0,0 +1,88 @@
module poly1305_width_convert_2(
input logic i_clk,
input logic i_rst,
input logic i_valid,
input logic [1:0] i_countm1,
output logic o_ready,
input logic [31:0] i_data,
input logic i_last,
output logic o_valid,
output logic [5:0] o_countm1,
input logic i_ready,
output logic [511:0] o_data,
output logic o_last
);
// this could definitely be parameterized,
// but that can be a future project for the intern
logic [479:0] width_convert_save, width_convert_save_next;
logic [3:0] width_convert_count, width_convert_count_next;
always_ff @(posedge i_clk) begin
if (i_rst) begin
width_convert_save <= '0;
width_convert_count <= '0;
end else begin
width_convert_count <= width_convert_count_next;
width_convert_save <= width_convert_save_next;
end
end
always_comb begin
width_convert_save_next = width_convert_save;
width_convert_count_next = width_convert_count;
o_valid = '0;
o_ready = '0;
o_last = '0;
o_countm1 = '1;
if (width_convert_count < 15) begin
o_data = {32'b0, width_convert_save};
o_data[32*width_convert_count +: 32] = i_data;
if (i_last && i_valid) begin
o_ready = i_ready;
o_valid = '1;
o_last = '1;
// wireguard packets are rounded up to 16 bytes
o_countm1 = width_convert_count*4 + {4'b0, i_countm1};
if (o_countm1[3:0] != 0) begin
o_countm1 = {o_countm1[5:4], 4'hf};
end
if (i_ready) begin
width_convert_count_next = '0;
width_convert_save_next = '0;
end
end else begin
width_convert_save_next[32*width_convert_count +: 32] = i_data;
if (i_valid & i_ready) begin
o_ready = '1;
width_convert_count_next = width_convert_count + 4'd1;
end
end
end else begin
o_ready = i_ready;
o_valid = i_valid;
o_data = {i_data, width_convert_save};
o_last = i_last;
// o_countm1 = 6'd63 + {4'b0, i_countm1};
o_countm1 = 6'd63;
if (i_valid && o_ready) begin
width_convert_count_next = '0;
width_convert_save_next = '0;
end
end
end
endmodule

View File

@@ -0,0 +1,63 @@
// this one converts from 512 back to 32
module poly1305_width_convert_3(
input logic i_clk,
input logic i_rst,
input logic i_valid,
input logic [5:0] i_countm1,
output logic o_ready,
input logic [511:0] i_data,
input logic i_last,
output logic o_valid,
output logic [1:0] o_countm1,
input logic i_ready,
output logic [31:0] o_data,
output logic o_last
);
logic [3:0] width_convert_count, width_convert_count_next;
always_ff @(posedge i_clk) begin
if (i_rst) begin
width_convert_count <= '0;
end else begin
width_convert_count <= width_convert_count_next;
end
end
always_comb begin
width_convert_count_next = width_convert_count;
o_valid = '0;
o_last = '0;
o_ready = '0;
o_data = i_data[width_convert_count*32 +: 32];
o_countm1 = '0;
o_valid = i_valid;
if (i_valid && i_ready) begin
width_convert_count_next = width_convert_count + 1;
o_countm1 = '1;
if (width_convert_count == 15) begin
o_ready = '1;
end
end
if (i_last && 6'(width_convert_count+1)*4 > i_countm1) begin
o_last = '1;
o_countm1 = (2)'(i_countm1 - width_convert_count*4);
if (i_valid && i_ready) begin
o_ready = '1;
width_convert_count_next = '0;
end
end
end
endmodule

View File

@@ -0,0 +1,114 @@
/*===============================================================================================================================
Module : Pipeline Skid Buffer
Description : Pipeline Skid Buffer is used as buffer in pipeline between two modules.
- Smallest pipeline buffer; implemented with just two registers.
- Simple valid-ready handshaking.
- Latency = 1 cycle.
- Configurable data width.
Developer : Mitu Raj, chip@chipmunklogic.com at Chipmunk Logic ™, https://chipmunklogic.com
Notes : -
License : Open-source.
Date : Mar-26-2022
===============================================================================================================================*/
/*-------------------------------------------------------------------------------------------------------------------------------
P I P E S K I D B U F F E R
-------------------------------------------------------------------------------------------------------------------------------*/
module pipe_skid_buffer #(
// Global Parameters
parameter DWIDTH = 8 // Data width
)
(
input logic clk , // Clock
input logic rstn , // Active-low synchronous reset
// Input Interface
input logic [DWIDTH-1 : 0] i_data , // Data in
input logic i_valid , // Data in valid
output logic o_ready , // Ready out
// Output Interface
output logic [DWIDTH-1 : 0] o_data , // Data out
output logic o_valid , // Data out valid
input logic i_ready // Ready in
) ;
/*-------------------------------------------------------------------------------------------------------------------------------
Local Parameters
-------------------------------------------------------------------------------------------------------------------------------*/
// State encoding
localparam PIPE = 1'b0 ;
localparam SKID = 1'b1 ;
/*-------------------------------------------------------------------------------------------------------------------------------
Internal Registers/Signals
-------------------------------------------------------------------------------------------------------------------------------*/
logic state_rg ; // State register
logic [DWIDTH-1 : 0] data_rg, sparebuff_rg ; // Data buffer, Spare buffer
logic valid_rg, ready_rg ; // Valid and Ready signals
logic ready ; // Pipeline ready signal
/*-------------------------------------------------------------------------------------------------------------------------------
Synchronous logic
-------------------------------------------------------------------------------------------------------------------------------*/
always @(posedge clk) begin
// Reset
if (!rstn) begin
// Internal Registers
state_rg <= PIPE ;
data_rg <= '0 ;
sparebuff_rg <= '0 ;
valid_rg <= 1'b0 ;
ready_rg <= 1'b0 ;
end
// Out of reset
else begin
case (state_rg)
/* Stage where data is piped out or stored to spare buffer */
PIPE : begin
// Pipe data out
if (ready) begin
data_rg <= i_data ;
valid_rg <= i_valid ;
ready_rg <= 1'b1 ;
end
// Pipeline stall, store input data to spare buffer (skid happened)
else if (i_valid) begin
sparebuff_rg <= i_data ;
ready_rg <= 1'b0 ;
state_rg <= SKID ;
end
end
/* Stage to wait after data skid happened */
SKID : begin
// Copy data from spare buffer to data buffer when downstream is ready, resume pipeline
if (i_ready) begin
data_rg <= sparebuff_rg ;
valid_rg <= 1'b1 ;
ready_rg <= 1'b1 ;
state_rg <= PIPE ;
end
end
endcase
end
end
/*-------------------------------------------------------------------------------------------------------------------------------
Continuous Assignments
-------------------------------------------------------------------------------------------------------------------------------*/
assign ready = i_ready || ~valid_rg ;
assign o_ready = ready_rg ;
assign o_data = data_rg ;
assign o_valid = valid_rg ;
endmodule
/*-------------------------------------------------------------------------------------------------------------------------------
P I P E S K I D B U F F E R
-------------------------------------------------------------------------------------------------------------------------------*/

View File

@@ -9,4 +9,12 @@ poly1305_friendly_modular_mult.sv
poly1305_stage.sv
poly1305_width_convert.sv
poly1305_ll_stage.sv
poly1305_width_convert_2.sv
poly1305_width_convert_3.sv
poly1305_ll_stage.sv
skidbuffer.sv
chacha20_poly1305_32_ll_core.sv
chacha20_poly1305_input_fsm.sv
chacha20_poly1305_mid_fsm.sv

1
common/sim/sub/fifo Submodule

Submodule common/sim/sub/fifo added at 9f35e490dc

0
mod_test.py Normal file
View File