217 lines
8.1 KiB
Markdown
217 lines
8.1 KiB
Markdown
# 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
|
|
|
|
and truncate to 128 bits
|
|
|
|
6ba4f66eb2dd47e52c64c8d4ced78624
|
|
|
|
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.
|
|
|
|
|
|
## Keepalive Packets
|
|
|
|
These packets have zero data. They will need to be handled separately.
|
|
|
|
After We can look at the UDP length to figure out if it is a keepalive packet,
|
|
this only serves to tell software if the connection has failed. We will
|
|
need to support sending and receiving these at some point, most likely in
|
|
hardware since they need to be authenticated, but they have no data.
|
|
perhaps some tuser flag can be passed in to bypass the crypto. We can
|
|
send in 16 bytes of zeroes but the user flag says to not actually send
|
|
this to poly1305, and the result can just be dropped after resetting some kind
|
|
of counter, or we can drop the data and just encapsulate the poly1305 tag.
|
|
|
|
## Padding is not always 16 bytes
|
|
|
|
The packets are 16 byte aligned UNLESS this would bring the packet over the MTU.
|
|
What this means in practice is that we might receive packets which are not 16
|
|
byte aligned. If we are decrypting a packet, the length must be what the packet
|
|
actually is and not rounded up. We also need to be careful when we are sending
|
|
out packets that we can't always go up to 1500 bytes.
|
|
|
|
(Might have to tell end users to set their MTU to 1300 or something).
|
|
|
|
If we are decrypting then, the width convert should not round up.
|
|
|
|
Or... we can just put that in a different module and not have random rules in the
|
|
crypto block itself.
|
|
|
|
This also breaks the poly1305, because it is expecting exactly 128 bits every time,
|
|
so it has a specific cycle for the lengths. BUT if the incoming data is not 16 byte
|
|
aligned, then the lengths will be added at the wrong time. So, based on the length
|
|
of the packet, we need to be adding the lengths at a varying position at the end
|
|
of the packet. It might be split over 2 cycles, depending on what the length is.
|
|
we can figure this out easily by simply taking the length of the packet (which
|
|
we have counted already) modulo 16, aka & 0xf. In this case, the length of the packet
|
|
is 1420, so the counter is 12. What this means is that the last packet is only
|
|
12 bytes, so we need to shift 4 bytes of the counter into the last data beat.
|
|
|
|
How do we know its the last data beat? the upconvert module gives us last. Technically
|
|
we calulate the total length based on this output. whatever.
|
|
|
|
So in the sum state, we set the last flag based on upconvert_last. If this is true,
|
|
we also need to check if upconvert_countm1 is 0xf, or if its smaller than that. if
|
|
its 15, then we do what we normally do, just 1 extend the existing data. If it is not,
|
|
then we need to do 15 - countm1 bytes of the length packet.
|
|
|
|
* padding2 -- the padding is up to 15 zero bytes, and it brings
|
|
the total length so far to an integral multiple of 16. If the
|
|
length of the ciphertext was already an integral multiple of 16
|
|
bytes, this field is zero-length.
|
|
|
|
wait... we should still be adding this padding ourselves...
|
|
|
|
this is actually an embarassingly easy fix.
|
|
|
|
|
|
1420 bytes of inner ip
|
|
|
|
16 bytes of wireguard header
|
|
16 bytes of wireguard signature
|
|
|
|
8 bytes of udp
|
|
|
|
20 bytes of ipv4
|
|
|
|
14 bytes of ethernet
|
|
|
|
so if you are just looking at wireguard itself, its max is 1420 + 32?
|
|
|
|
or, is it that we set the MTU to 1420 which is the max sized of the
|
|
encrypted packet, so we can't add any padding?
|
|
|
|
OK in the width expander we expand to 1420 max.
|
|
|
|
we could have a check at the begining that checks the
|
|
total length. If it is larger than 1420 then we can
|
|
send the packet to the CPU to fragment or do something with
|
|
But really all computers that are on the network should
|
|
have the MTU set to 1420 so that we can avoid this.
|
|
|
|
|
|
One thing we still need to support is keepalive packets. These have
|
|
no payload, but still need to be decrypted. The input FSM will take
|
|
in just the encrypted wireguard packet, without the wireguard header.
|
|
It will then strip off the tag and put it in a fifo for the output fsm
|
|
to read. If it does this to a keepalive packet, there will be no data.
|
|
If this happens, the input FSM will send in 64 bytes of dummy data,
|
|
all zeroes, along with a keepalive signal. this signal will be stored
|
|
along with the packet and when it is output to the output fsm, it will
|
|
know to ignore the actual data.
|
|
|
|
I think this can be done entirely outside of the crypto block.
|
|
|
|
OK so that tells us what we need to do next, the pre and postformat FSM.
|
|
|
|
preformat FSM removes the tag, postformat FSM adds the tag back for encyrpt,
|
|
or compares the tag with the expected tag for decrypt.
|
|
|
|
We need a metadata FIFO which tells us whether to encyrpt or decrypt.
|
|
|
|
ohhhh right. what I really wanted to do today was synthesize this block
|
|
and see what the timing looks like.
|
|
|
|
|
|
We pass timing at 67 MHz. Lets try to reduce the number of flop stages in
|
|
chacha to see if we can reduce the number of flops we use. |