///|
let magic = b"PAR2\x00PKT"
///|
let main_tag = b"PAR 2.0\x00Main\x00\x00\x00\x00"
///|
let desc_tag = b"PAR 2.0\x00FileDesc"
///|
let sums_tag = b"PAR 2.0\x00IFSC\x00\x00\x00\x00"
///|
let parity_tag = b"PAR 2.0\x00RecvSlic"
///|
let creator_tag = b"PAR 2.0\x00Creator\x00"
///|
priv struct Packet {
id : Bytes
tag : Bytes
body : Bytes
}
///|
fn joined(parts : Array[Bytes]) -> Bytes {
let buf = @buffer.Buffer()
for part in parts {
buf.write_bytes(part)
}
buf.to_bytes()
}
///|
fn write_packet(id : Bytes, tag : Bytes, body : Bytes) -> Bytes {
let content = joined([id, tag, body])
let buf = @buffer.Buffer()
buf.write_bytes(magic)
buf.write_uint64_le((64 + body.length()).to_uint64())
buf.write_bytes(md5(content))
buf.write_bytes(content)
buf.to_bytes()
}
///|
fn read_packets(raw : Bytes) -> Array[Packet] raise {
let out = []
let mut p = 0
while p < raw.length() {
if raw.length() - p < 64 || raw[p:p + 8].to_owned() != magic {
fail("invalid PAR2 packet header")
}
let len = le32(raw, p + 8).reinterpret_as_int()
if len < 64 ||
len % 4 != 0 ||
len > raw.length() - p ||
le32(raw, p + 12) != 0U {
fail("invalid packet length")
}
let end = p + len
if md5(raw[p + 32:end].to_owned()) != raw[p + 16:p + 32].to_owned() {
fail("packet checksum mismatch")
}
out.push({
id: raw[p + 32:p + 48].to_owned(),
tag: raw[p + 48:p + 64].to_owned(),
body: raw[p + 64:end].to_owned(),
})
p = end
}
out
}
///|
test "a flipped packet body fails validation" {
let id = Bytes::make(16, b'0')
let raw = write_packet(id, main_tag, b"abcd")
assert_eq(read_packets(raw)[0].body, b"abcd")
let changed = joined([raw[:raw.length() - 1].to_owned(), b"X"])
let ok = try {
ignore(read_packets(changed))
false
} catch {
_ => true
}
assert_true(ok)
}