///|
/// Big-endian byte reader over a `BytesView`, used by every packet parser.
pub struct Reader {
priv buf : BytesView
priv mut pos : Int32
} derive(Debug)
///|
pub fn Reader::new(buf : BytesView) -> Reader {
Reader::{ buf, pos: 0, }
}
///|
pub fn Reader::remaining(self : Reader) -> Int32 {
self.buf.length() - self.pos
}
///|
pub fn Reader::position(self : Reader) -> Int32 {
self.pos
}
///|
pub fn Reader::view(self : Reader) -> BytesView {
self.buf[self.pos:self.buf.length()]
}
///|
pub fn Reader::read_u8(self : Reader) -> Int32 raise {
guard self.remaining() >= 1 else {
@error.fail("packet: unexpected end of data (u8)")
}
let b = self.buf[self.pos].to_int()
self.pos = self.pos + 1
b
}
///|
pub fn Reader::read_u16(self : Reader) -> Int32 raise {
guard self.remaining() >= 2 else {
@error.fail("packet: unexpected end of data (u16)")
}
let v = (self.buf[self.pos].to_int() << 8) | self.buf[self.pos + 1].to_int()
self.pos = self.pos + 2
v
}
///|
/// Read a big-endian u32 into a 32-bit Int (F09, documented convention):
/// values ≥ 2³¹ arrive NEGATIVE. Length paths fail safe through the
/// `n >= 0` check in `read_bytes`, which reports the negative value as
/// an invalid length; timestamp paths carry the correct two's-complement
/// wire value, which `Writer::u32` reproduces byte-exactly on
/// re-serialization.
pub fn Reader::read_u32(self : Reader) -> Int32 raise {
guard self.remaining() >= 4 else {
@error.fail("packet: unexpected end of data (u32)")
}
let v = (self.buf[self.pos].to_int() << 24) |
(self.buf[self.pos + 1].to_int() << 16) |
(self.buf[self.pos + 2].to_int() << 8) |
self.buf[self.pos + 3].to_int()
self.pos = self.pos + 4
v
}
///|
pub fn Reader::read_bytes(self : Reader, n : Int32) -> Bytes raise {
guard n >= 0 else {
// A negative length is a malformed packet (often a u32 length ≥ 2^31
// read through the F09 two's-complement convention), not a shortfall.
@error.fail("packet: invalid negative length \{n}")
}
guard self.remaining() >= n else {
@error.fail("packet: unexpected end of data (bytes, want \{n})")
}
let out = self.buf[self.pos:self.pos + n].to_owned()
self.pos = self.pos + n
out
}
///|
pub fn Reader::read_rest(self : Reader) -> Bytes raise {
self.read_bytes(self.remaining())
}
///|
/// Defensive ceiling on an MPI's bit length (S16). RFC 9580 §3.2 imposes
/// no per-algorithm maximum on parsers (its only MUST — the length
/// consistency check — applies to v6 packets), so this is hardening
/// beyond the RFC: no defined OpenPGP algorithm carries MPIs wider than
/// 16384 bits, and capping here rejects malformed packets at the parse
/// layer instead of letting oversized MPIs flow into semantic code.
pub const MAX_MPI_BITS : Int32 = 16384
///|
/// Read an OpenPGP multiprecision integer (RFC 9580 §3.2).
pub fn Reader::read_mpi(self : Reader) -> Bytes raise {
let bits = self.read_u16()
guard bits <= MAX_MPI_BITS else {
@error.fail(
"packet: MPI bit length \{bits} exceeds the implementation maximum \{MAX_MPI_BITS}",
)
}
let nbytes = (bits + 7) / 8
self.read_bytes(nbytes)
}
///|
/// Big-endian byte writer for packet serialization. The backing buffer is
/// core's `Buffer` so `bytes()` can bulk-write views instead of pushing
/// byte by byte (F12).
pub struct Writer {
priv out : Buffer
} derive(Debug)
///|
pub fn Writer::new() -> Writer {
Writer::{ out: Buffer(size_hint=256), }
}
///|
pub fn Writer::length(self : Writer) -> Int32 {
self.out.length()
}
///|
pub fn Writer::u8(self : Writer, v : Int32) -> Unit {
self.out.write_byte(v.to_byte())
}
///|
pub fn Writer::u16(self : Writer, v : Int32) -> Unit {
self.out.write_byte((v >> 8).to_byte())
self.out.write_byte(v.to_byte())
}
///|
pub fn Writer::u32(self : Writer, v : Int32) -> Unit {
self.out.write_byte((v >> 24).to_byte())
self.out.write_byte((v >> 16).to_byte())
self.out.write_byte((v >> 8).to_byte())
self.out.write_byte(v.to_byte())
}
///|
pub fn Writer::bytes(self : Writer, data : BytesView) -> Unit {
self.out.write_bytes(data)
}
///|
pub fn Writer::mpi(self : Writer, data : BytesView) -> Unit {
// Count significant bits: strip leading zero octets first.
let mut start = 0
while start < data.length() && data[start] == b'\x00' {
start = start + 1
}
let stripped = data[start:data.length()]
let bits = if stripped.length() == 0 {
0
} else {
let lead = stripped[0].to_int()
let mut b = 8
while b > 1 {
if (lead & (1 << (b - 1))) != 0 {
break
}
b = b - 1
}
(stripped.length() - 1) * 8 + b
}
self.u16(bits)
self.bytes(stripped)
}
///|
pub fn Writer::to_bytes(self : Writer) -> Bytes {
self.out.to_bytes()
}
///|
pub extend Reader with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend Writer with @moonbitlang/core/debug.Debug::{to_repr}