///|
pub fn parse_certificate(
data : BytesView,
) -> Result[TlsCertificate, ParseError] {
if data.length() < 5 {
return Err(IncompleteRecord(expected=5, available=data.length()))
}
if data[0].to_int() != 0x16 {
return Err(NotTlsHandshake)
}
guard read_u16(data, 1) is Some(_) else {
return Err(BadLength(field="record.version", offset=1))
}
guard read_u16_int(data, 3) is Some(record_len) else {
return Err(BadLength(field="record.length", offset=3))
}
if record_len < 0 {
return Err(BadLength(field="record.length", offset=3))
}
let record_end = 5 + record_len
if record_end < 5 || data.length() < record_end {
return Err(IncompleteRecord(expected=record_end, available=data.length()))
}
if record_len < 4 {
return Err(BadLength(field="handshake.header", offset=5))
}
if data[5].to_int() != 11 {
return Err(BadHandshakeType(data[5].to_int()))
}
guard read_u24_int(data, 6) is Some(handshake_len) else {
return Err(BadLength(field="handshake.length", offset=6))
}
let body_offset = 9
let body_end = body_offset + handshake_len
if handshake_len < 0 || body_end < body_offset || body_end > record_end {
return Err(BadLength(field="handshake.body", offset=body_offset))
}
// Certificates list length is 3 bytes
if body_end - body_offset < 3 {
return Err(BadLength(field="certificates.list_length", offset=body_offset))
}
guard read_u24_int(data, body_offset) is Some(certs_list_len) else {
return Err(BadLength(field="certificates.list_length", offset=body_offset))
}
let mut cursor = body_offset + 3
let list_end = cursor + certs_list_len
if certs_list_len < 0 || list_end < cursor || list_end > body_end {
return Err(BadLength(field="certificates.list_body", offset=cursor))
}
if list_end != body_end {
return Err(BadLength(field="certificates.trailing", offset=list_end))
}
let cert_lengths : Array[Int] = []
while cursor + 3 <= list_end {
guard read_u24_int(data, cursor) is Some(cert_len) else {
return Err(BadLength(field="certificate.length", offset=cursor))
}
cursor = cursor + 3
if cert_len < 0 || cursor + cert_len > list_end {
return Err(BadLength(field="certificate.body", offset=cursor))
}
cert_lengths.push(cert_len)
cursor = cursor + cert_len
}
if cursor != list_end {
return Err(BadLength(field="certificates.trailing", offset=cursor))
}
Ok({ cert_lengths, })
}