Skip to content

Commit

Permalink
Merge pull request #30 from quentinlampin/sctp-checksum
Browse files Browse the repository at this point in the history
SCTP checksum compute
  • Loading branch information
quentinlampin authored Jan 9, 2025
2 parents 170102c + 1464f9c commit 9c199be
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 9 deletions.
17 changes: 16 additions & 1 deletion microschc/binary/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,21 @@ def __xor__(self, another: 'Buffer'):
bitwise_xor_buffer: Buffer = Buffer(content=bitwise_xor_content, length=self.length, padding=self.padding)
return bitwise_xor_buffer

def __invert__(self):
two_complement_content: bytes = b''
if self.padding == Padding.LEFT:
mask: int = (1 << (8 - self.padding_length)%8) - 1
two_complement_content += (~self.content[0] & mask).to_bytes(1, 'big')
for b in iter(self.content):
two_complement_content += (~b & 0xff).to_bytes(1, 'big')
else:
mask: int = (0xff << (self.padding_length)) & 0xff
for b in iter(self.content[:len(self.content)-1]):
two_complement_content += (~b & 0xff).to_bytes(1, 'big')
two_complement_content += (~self.content[len(self.content)-1] & mask).to_bytes(1, 'big')

two_complement: Buffer = Buffer(content=two_complement_content, length=self.length, padding=self.padding)
return two_complement


def __setitem__(self,items, values):
Expand Down Expand Up @@ -423,7 +438,7 @@ def __getitem__(self, items):
assert isinstance(items, (slice, int))

if isinstance(items, slice):
start_bit, stop_bit, _ = items.indices(self.length)
start_bit, stop_bit, stride = items.indices(self.length)

else:
start_bit = items
Expand Down
1 change: 1 addition & 0 deletions microschc/crypto/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .crc import CRC32C_TABLE, crc32c
66 changes: 66 additions & 0 deletions microschc/crypto/crc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from microschc.binary.buffer import Buffer


CRC32C_TABLE = [
0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F,
0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC,
0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27,
0x5E133C24, 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 0x9A879FA0,
0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC,
0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29,
0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E,
0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 0x30E349B1, 0xC288CAB2,
0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59,
0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC,
0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0,
0x67DAFA54, 0x95B17957, 0xCBA24573, 0x39C9C670, 0x2A993584,
0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC,
0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F,
0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4,
0x0F36E6F7, 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 0xEB1FCBAD,
0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1,
0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E, 0x90A324FA,
0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD,
0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B,
0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90,
0x563C5F93, 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 0x92A8FC17,
0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B,
0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F,
0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9,
0x97BAA1BA, 0x84EA524E, 0x7681D14D, 0x2892ED69, 0xDAF96E6A,
0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81,
0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06,
0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A,
0x1E6DCDEE, 0xEC064EED, 0xC38D26C4, 0x31E6A5C7, 0x22B65633,
0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914,
0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8,
0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643,
0x07198540, 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A,
0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06,
0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6, 0x88D28022,
0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A,
0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9,
0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052,
0xAD7D5351
]

def crc32c(buffer: Buffer, crc_init:int):
crc32c = crc_init
for c in buffer.chunks(length=8, padding=True):
b = c.value()
crc32c = (crc32c >> 8)^CRC32C_TABLE[(crc32c^b)&0xff]
crc32c_content:bytes = crc32c.to_bytes(4, 'big')
crc32c_buffer: Buffer = Buffer(content=crc32c_content, length=32)
return crc32c_buffer
4 changes: 3 additions & 1 deletion microschc/protocol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
from .ipv4 import IPv4ComputeFunctions, IPv4Fields
from .ipv6 import IPv6ComputeFunctions, IPv6Fields
from .udp import UDPComputeFunctions, UDPFields
from .sctp import SCTPComputeFunctions, SCTPFields

ComputeFunctions: Dict[str, Tuple[ComputeFunctionType, ComputeFunctionDependenciesType]] = {
IPv4Fields.TOTAL_LENGTH: IPv4ComputeFunctions[IPv4Fields.TOTAL_LENGTH],
IPv4Fields.HEADER_CHECKSUM: IPv4ComputeFunctions[IPv4Fields.HEADER_CHECKSUM],
IPv6Fields.PAYLOAD_LENGTH: IPv6ComputeFunctions[IPv6Fields.PAYLOAD_LENGTH],
UDPFields.LENGTH: UDPComputeFunctions[UDPFields.LENGTH],
UDPFields.CHECKSUM: UDPComputeFunctions[UDPFields.CHECKSUM]
UDPFields.CHECKSUM: UDPComputeFunctions[UDPFields.CHECKSUM],
SCTPFields.CHECKSUM: SCTPComputeFunctions[SCTPFields.CHECKSUM]
}


Expand Down
31 changes: 28 additions & 3 deletions microschc/protocol/sctp.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@


from enum import Enum
from functools import reduce

from microschc.crypto.crc import CRC32C_TABLE, crc32c
from microschc.protocol.registry import PARSERS, REGISTER_PARSER, ProtocolsIDs

SCTP_HEADER_ID = 'SCTP'

from enum import Enum
from typing import Dict, List, Tuple, Type
from microschc.binary.buffer import Buffer
from microschc.binary.buffer import Buffer, Padding
from microschc.parser import HeaderParser, ParserError
from microschc.protocol.compute import ComputeFunctionDependenciesType, ComputeFunctionType
from microschc.rfc8724 import FieldDescriptor, HeaderDescriptor

SCTP_HEADER_ID = 'SCTP'
Expand Down Expand Up @@ -604,9 +607,9 @@ def _parse_parameter(self, buffer: Buffer) -> Tuple[List[FieldDescriptor], int]:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Parameter Type | Parameter Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
\ \
\ \
/ Parameter Value /
\ \
\ \
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"""
fields: List[FieldDescriptor] = []
Expand All @@ -632,4 +635,26 @@ def _parse_parameter(self, buffer: Buffer) -> Tuple[List[FieldDescriptor], int]:
)
return fields, parameter_length_value + parameter_padding_length


def _compute_checksum(decompressed_fields: List[Tuple[str, Buffer]], rule_field_position: int) -> Buffer:
"""
Checksum algorithm is the Castagnoli CRC32C Checksum Algorithm (CRC32c).
"""
fields_values: List[Buffer] = [field_value for _, field_value in decompressed_fields]

sctp_checksum_position: int = rule_field_position
# - SCTP checksum is the 4th field of UDP
sctp_header_and_payload_fields: List[Buffer] = [field for field in fields_values[sctp_checksum_position-3:]]
sctp_header_and_payload: Buffer = reduce(lambda x, y: x+y, sctp_header_and_payload_fields)

crc_init: int = 0xffffffff
checksum = crc32c(buffer=sctp_header_and_payload, crc_init=crc_init)
checksum = ~checksum
checksum_buffer = reduce(lambda x,y : x+y, list(checksum.chunks(length=8))[::-1])
return checksum_buffer

SCTPComputeFunctions: Dict[str, Tuple[ComputeFunctionType, ComputeFunctionDependenciesType]] = {
SCTPFields.CHECKSUM: (_compute_checksum, { f.value for f in SCTPFields if f is not SCTPFields.CHECKSUM })
}

REGISTER_PARSER(protocol_id=ProtocolsIDs.SCTP, parser_class=SCTPParser)
21 changes: 19 additions & 2 deletions tests/binary/test_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,25 @@ def test_and():
buffer_1_and_1_left: Buffer = buffer_1 & buffer_1_pad_left
expected_1_and_1_left: Buffer = Buffer(content=b'\x08\x68', length=13, padding=Padding.RIGHT)
assert buffer_1_and_1_left == expected_1_and_1_left



def test_complement():
buffer: Buffer = Buffer(content=b'\x01\xff\xf0', length=24)
complement = ~buffer
expected_buffer = Buffer(content=b'\xfe\x00\x0f', length=24)
assert complement == expected_buffer

def test_complement_padding_left():
buffer: Buffer = Buffer(content=b'\x0f\xff\xf0', length=20)
complement = ~buffer
expected_buffer = Buffer(content=b'\x00\x00\x0f', length=20)
assert complement == expected_buffer

def test_complement_padding_right():
buffer: Buffer = Buffer(content=b'\x0f\xff\x80', length=20, padding=Padding.RIGHT)
complement = ~buffer
expected_buffer = Buffer(content=b'\xf0\x00\x70', length=20, padding=Padding.RIGHT)
assert complement == expected_buffer

def test_value():

buffer: Buffer = Buffer(content=b'\x00\x01', length=16)
Expand Down
Empty file added tests/crypto/test_crc.py
Empty file.
23 changes: 21 additions & 2 deletions tests/protocol/test_sctp.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Dict, List, Tuple
from microschc.protocol.sctp import SCTPParser, SCTPFields
from microschc.protocol.sctp import SCTPParser, SCTPFields, SCTPComputeFunctions
from microschc.parser.parser import HeaderDescriptor
from microschc.rfc8724 import FieldDescriptor
from microschc.binary.buffer import Buffer
Expand Down Expand Up @@ -1104,4 +1104,23 @@ def test_sctp_parser_parse_cookie_ack():
chunk_length_fd:FieldDescriptor = sctp_header_descriptor.fields[6]
assert chunk_length_fd.id == SCTPFields.CHUNK_LENGTH
assert chunk_length_fd.position == 0
assert chunk_length_fd.value == Buffer(content=b'\x00\x04', length=16)
assert chunk_length_fd.value == Buffer(content=b'\x00\x04', length=16)

def test_sctp_compute_checksum():

partially_reconstructed_content: bytes = bytes(
b'\x00\x07\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x20'
b'\x43\x23\x25\x44\x00\x00\xff\xff\x00\x11\x00\x11\x5c\xfe\x37\x9f'
b'\xc0\x00\x00\x04\x00\x0c\x00\x06\x00\x05\x00\x00'

)
expected_checksum: Buffer = Buffer(content=b'\x37\x61\xa7\x46', length=32)
parser:SCTPParser = SCTPParser()

partially_reconstructed_packet: Buffer = Buffer(content=partially_reconstructed_content, length=len(partially_reconstructed_content)*8)
sctp_header_descriptor: HeaderDescriptor = parser.parse(buffer=partially_reconstructed_packet)


decompressed_fields: List[Tuple[str, Buffer]] = [ (field.id,field.value) for field in sctp_header_descriptor.fields]
checksum_buffer: Buffer = SCTPComputeFunctions[SCTPFields.CHECKSUM][0](decompressed_fields, 3)
assert checksum_buffer == expected_checksum

0 comments on commit 9c199be

Please sign in to comment.