The current DER parser lacks:
Checks to prevent out-of-bounds reads by verifying slice lengths are indeed inside its byte slice.
Checks to prevent hash collision attacks by verifying fields are in their shortest canonical form.
Checks to prevent hash collision attacks by verifying no bytes are unparsed.
OID parsing for auditing (see audit the standard library TLS implementation with respect to RFC 8446 #14178 ).
Tests.
An API to easily expect an element.
pub const der = struct {
pub const Class = enum (u2 ) {
universal ,
application ,
context_specific ,
private ,
};
pub const PC = enum (u1 ) {
primitive ,
constructed ,
};
pub const Identifier = packed struct (u8 ) {
tag : Tag ,
pc : PC ,
class : Class ,
};
pub const Tag = enum (u5 ) {
boolean = 1 ,
integer = 2 ,
bitstring = 3 ,
octetstring = 4 ,
null = 5 ,
object_identifier = 6 ,
sequence = 16 ,
sequence_of = 17 ,
utc_time = 23 ,
generalized_time = 24 ,
_ ,
};
pub const Element = struct {
identifier : Identifier ,
slice : Slice ,
pub const Slice = struct {
start : u32 ,
end : u32 ,
pub const empty : Slice = .{ .start = 0 , .end = 0 };
};
pub const ParseElementError = error {CertificateFieldHasInvalidLength };
pub fn parse (bytes : []const u8 , index : u32 ) ParseElementError ! Element {
var i = index ;
const identifier = @as (Identifier , @bitCast (bytes [i ]));
i += 1 ;
const size_byte = bytes [i ];
i += 1 ;
if ((size_byte >> 7 ) == 0 ) {
return .{
.identifier = identifier ,
.slice = .{
.start = i ,
.end = i + size_byte ,
},
};
}
const len_size = @as (u7 , @truncate (size_byte ));
if (len_size > @sizeOf (u32 )) {
return error .CertificateFieldHasInvalidLength ;
}
const end_i = i + len_size ;
var long_form_size : u32 = 0 ;
while (i < end_i ) : (i += 1 ) {
long_form_size = (long_form_size << 8 ) | bytes [i ];
}
return .{
.identifier = identifier ,
.slice = .{
.start = i ,
.end = i + long_form_size ,
},
};
}
};
};
The current DER parser lacks:
zig/lib/std/crypto/Certificate.zig
Lines 864 to 946 in 8af59d1