authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-05-03 17:29:44+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-05-28 16:28:27+02:00
logaf1f91cadf0a2b04ea099334fe2008e526d4080b
tree032fe6417b1bf85c2b00e69c445d93fb2987f6a1
parent87e0dc7dea6153d7b34c3b8d626316ca7d5ce787

std.crypto.codecs.asn1.der.Encoder: rewrite with prependBytes

Drop the writer wrapper since ArrayListReverse no longer exposes one, and use prependBytes consistently. Also rewrite length encoding so the high-bit length-of-length byte is placed last as expected by X.690. And always derive the leading-zero pad from the sign bit of the first encoded byte.

2 files changed, 103 insertions(+), 40 deletions(-)

lib/std/crypto/codecs/asn1.zig+2-2
...@@ -327,8 +327,8 @@ pub const BitString = struct {...@@ -327,8 +327,8 @@ pub const BitString = struct {
327 }327 }
328328
329 pub fn encodeDer(self: BitString, encoder: *der.Encoder) !void {329 pub fn encodeDer(self: BitString, encoder: *der.Encoder) !void {
330 try encoder.writer().writeAll(self.bytes);330 try encoder.prependBytes(self.bytes);
331 try encoder.writer().writeByte(self.right_padding);331 try encoder.prependBytes(&.{self.right_padding});
332 try encoder.length(self.bytes.len + 1);332 try encoder.length(self.bytes.len + 1);
333 try encoder.tag(asn1_tag);333 try encoder.tag(asn1_tag);
334 }334 }
lib/std/crypto/codecs/asn1/der/Encoder.zig+101-38
...@@ -24,6 +24,7 @@ pub fn any(self: *Encoder, val: anytype) !void {...@@ -24,6 +24,7 @@ pub fn any(self: *Encoder, val: anytype) !void {
24fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {24fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
25 const T = @TypeOf(val);25 const T = @TypeOf(val);
26 if (std.meta.hasFn(T, "encodeDer")) return try val.encodeDer(self);26 if (std.meta.hasFn(T, "encodeDer")) return try val.encodeDer(self);
27 const outer_field_tag = self.field_tag;
27 const start = self.buffer.data.len;28 const start = self.buffer.data.len;
28 const merged_tag = self.mergedTag(tag_);29 const merged_tag = self.mergedTag(tag_);
2930
...@@ -42,8 +43,9 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {...@@ -42,8 +43,9 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
42 const is_default = if (f_attrs.@"comptime") false else if (f_attrs.defaultValue(f_type)) |default_val| brk: {43 const is_default = if (f_attrs.@"comptime") false else if (f_attrs.defaultValue(f_type)) |default_val| brk: {
43 break :brk std.mem.eql(u8, std.mem.asBytes(&default_val), std.mem.asBytes(&field_val));44 break :brk std.mem.eql(u8, std.mem.asBytes(&default_val), std.mem.asBytes(&field_val));
44 } else false;45 } else false;
46 const is_null_optional = if (@typeInfo(f.type) == .optional) field_val == null else false;
4547
46 if (!is_default) {48 if (!is_default and !is_null_optional) {
47 const start2 = self.buffer.data.len;49 const start2 = self.buffer.data.len;
48 self.field_tag = field_tag;50 self.field_tag = field_tag;
49 // will merge with self.field_tag.51 // will merge with self.field_tag.
...@@ -58,6 +60,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {...@@ -58,6 +60,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
58 }60 }
59 }61 }
60 }62 }
63 self.field_tag = outer_field_tag;
61 },64 },
62 .bool => try self.buffer.prependSlice(&[_]u8{if (val) 0xff else 0}),65 .bool => try self.buffer.prependSlice(&[_]u8{if (val) 0xff else 0}),
63 .int => try self.int(T, val),66 .int => try self.int(T, val),
...@@ -68,7 +71,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {...@@ -68,7 +71,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
68 try self.int(e.tag_type, @intFromEnum(val));71 try self.int(e.tag_type, @intFromEnum(val));
69 }72 }
70 },73 },
71 .optional => if (val) |v| return try self.anyTag(tag_, v),74 .optional => if (val) |v| return try self.anyTag(tag_, v) else return,
72 .null => {},75 .null => {},
73 else => @compileError("cannot encode type " ++ @typeName(T)),76 else => @compileError("cannot encode type " ++ @typeName(T)),
74 }77 }
...@@ -80,7 +83,8 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {...@@ -80,7 +83,8 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
80/// Encode a tag.83/// Encode a tag.
81pub fn tag(self: *Encoder, tag_: Tag) !void {84pub fn tag(self: *Encoder, tag_: Tag) !void {
82 const t = self.mergedTag(tag_);85 const t = self.mergedTag(tag_);
83 try t.encode(self.writer());86 var buf: [Tag.max_encoded_len]u8 = undefined;
87 try self.buffer.prependSlice(t.encodeToSlice(&buf));
84}88}
8589
86fn mergedTag(self: *Encoder, tag_: Tag) Tag {90fn mergedTag(self: *Encoder, tag_: Tag) Tag {
...@@ -96,19 +100,14 @@ fn mergedTag(self: *Encoder, tag_: Tag) Tag {...@@ -96,19 +100,14 @@ fn mergedTag(self: *Encoder, tag_: Tag) Tag {
96100
97/// Encode a length.101/// Encode a length.
98pub fn length(self: *Encoder, len: usize) !void {102pub fn length(self: *Encoder, len: usize) !void {
99 const writer_ = self.writer();103 if (len < 128) return self.buffer.prependSlice(&.{@intCast(len)});
100 if (len < 128) {104 const len32 = std.math.cast(u32, len) orelse return error.InvalidLength;
101 try writer_.writeInt(u8, @intCast(len), .big);105 var buf: [@sizeOf(u32) + 1]u8 = undefined;
102 return;106 std.mem.writeInt(u32, buf[1..], len32, .big);
103 }107 var first: usize = 1;
104 inline for ([_]type{ u8, u16, u32 }) |T| {108 while (buf[first] == 0) first += 1;
105 if (len < std.math.maxInt(T)) {109 buf[first - 1] = @intCast((buf.len - first) | 0x80);
106 try writer_.writeInt(T, @intCast(len), .big);110 return self.buffer.prependSlice(buf[first - 1 ..]);
107 try writer_.writeInt(u8, @sizeOf(T) | 0x80, .big);
108 return;
109 }
110 }
111 return error.InvalidLength;
112}111}
113112
114/// Encode a tag and length-prefixed bytes.113/// Encode a tag and length-prefixed bytes.
...@@ -118,28 +117,23 @@ pub fn tagBytes(self: *Encoder, tag_: Tag, bytes: []const u8) !void {...@@ -118,28 +117,23 @@ pub fn tagBytes(self: *Encoder, tag_: Tag, bytes: []const u8) !void {
118 try self.tag(tag_);117 try self.tag(tag_);
119}118}
120119
121/// Warning: This writer writes backwards. `fn print` will NOT work as expected.120/// Write raw bytes. The encoder builds its output back-to-front, so chained
122pub fn writer(self: *Encoder) ArrayListReverse.Writer {121/// calls should be made in reverse of the desired on-wire order.
123 return self.buffer.writer();122pub fn prependBytes(self: *Encoder, bytes: []const u8) !void {
123 return self.buffer.prependSlice(bytes);
124}124}
125125
126fn int(self: *Encoder, comptime T: type, value: T) !void {126fn int(self: *Encoder, comptime T: type, value: T) !void {
127 const big = std.mem.nativeTo(T, value, .big);127 const info = @typeInfo(T).int;
128 const big_bytes = std.mem.asBytes(&big);128 const Unsigned = @Int(.unsigned, info.bits);
129129 const pad: u8 = if (info.signedness == .signed and value < 0) 0xff else 0;
130 const bits_needed = @bitSizeOf(T) - @clz(value);130 var buf: [@sizeOf(Unsigned) + 1]u8 = undefined;
131 const needs_padding: u1 = if (value == 0)131 buf[0] = pad;
132 1132 std.mem.writeInt(Unsigned, buf[1..], @bitCast(value), .big);
133 else if (bits_needed > 8) brk: {133
134 const RightShift = @Int(.unsigned, @bitSizeOf(@TypeOf(bits_needed)) - 1);134 var first: usize = 0;
135 const right_shift: RightShift = @intCast(bits_needed - 9);135 while (first + 1 < buf.len and buf[first] == pad and (buf[first + 1] ^ pad) & 0x80 == 0) first += 1;
136 break :brk if (value >> right_shift == 0x1ff) 1 else 0;136 try self.buffer.prependSlice(buf[first..]);
137 } else 0;
138 const bytes_needed = try std.math.divCeil(usize, bits_needed, 8) + needs_padding;
139
140 const writer_ = self.writer();
141 for (0..bytes_needed - needs_padding) |i| try writer_.writeByte(big_bytes[big_bytes.len - i - 1]);
142 if (needs_padding == 1) try writer_.writeByte(0);
143}137}
144138
145test int {139test int {
...@@ -148,15 +142,84 @@ test int {...@@ -148,15 +142,84 @@ test int {
148 defer encoder.deinit();142 defer encoder.deinit();
149143
150 try encoder.int(u8, 0);144 try encoder.int(u8, 0);
151 try std.testing.expectEqualSlices(u8, &[_]u8{0}, encoder.buffer.data);145 try std.testing.expectEqualSlices(u8, &.{0}, encoder.buffer.data);
152146
153 encoder.buffer.clearAndFree();147 encoder.buffer.clearAndFree();
154 try encoder.int(u16, 0x00ff);148 try encoder.int(u16, 0x00ff);
155 try std.testing.expectEqualSlices(u8, &[_]u8{0xff}, encoder.buffer.data);149 try std.testing.expectEqualSlices(u8, &.{ 0, 0xff }, encoder.buffer.data);
156150
157 encoder.buffer.clearAndFree();151 encoder.buffer.clearAndFree();
158 try encoder.int(u32, 0xffff);152 try encoder.int(u32, 0xffff);
159 try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0xff, 0xff }, encoder.buffer.data);153 try std.testing.expectEqualSlices(u8, &.{ 0, 0xff, 0xff }, encoder.buffer.data);
154
155 encoder.buffer.clearAndFree();
156 try encoder.int(u32, 0x01020304);
157 try std.testing.expectEqualSlices(u8, &.{ 0x01, 0x02, 0x03, 0x04 }, encoder.buffer.data);
158
159 encoder.buffer.clearAndFree();
160 try encoder.int(u8, 127);
161 try std.testing.expectEqualSlices(u8, &.{0x7f}, encoder.buffer.data);
162
163 encoder.buffer.clearAndFree();
164 try encoder.int(u16, 128);
165 try std.testing.expectEqualSlices(u8, &.{ 0, 0x80 }, encoder.buffer.data);
166
167 encoder.buffer.clearAndFree();
168 try encoder.int(u16, 256);
169 try std.testing.expectEqualSlices(u8, &.{ 0x01, 0x00 }, encoder.buffer.data);
170
171 encoder.buffer.clearAndFree();
172 try encoder.int(u8, 128);
173 try std.testing.expectEqualSlices(u8, &.{ 0, 0x80 }, encoder.buffer.data);
174
175 encoder.buffer.clearAndFree();
176 try encoder.int(u8, 255);
177 try std.testing.expectEqualSlices(u8, &.{ 0, 0xff }, encoder.buffer.data);
178
179 encoder.buffer.clearAndFree();
180 try encoder.int(u16, 0x8000);
181 try std.testing.expectEqualSlices(u8, &.{ 0, 0x80, 0 }, encoder.buffer.data);
182
183 encoder.buffer.clearAndFree();
184 try encoder.int(i8, -1);
185 try std.testing.expectEqualSlices(u8, &.{0xff}, encoder.buffer.data);
186
187 encoder.buffer.clearAndFree();
188 try encoder.int(i8, -128);
189 try std.testing.expectEqualSlices(u8, &.{0x80}, encoder.buffer.data);
190
191 encoder.buffer.clearAndFree();
192 try encoder.int(i16, -129);
193 try std.testing.expectEqualSlices(u8, &.{ 0xff, 0x7f }, encoder.buffer.data);
194}
195
196test length {
197 const allocator = std.testing.allocator;
198 var encoder = Encoder.init(allocator);
199 defer encoder.deinit();
200
201 try encoder.length(127);
202 try std.testing.expectEqualSlices(u8, &.{0x7f}, encoder.buffer.data);
203
204 encoder.buffer.clearAndFree();
205 try encoder.length(128);
206 try std.testing.expectEqualSlices(u8, &.{ 0x81, 0x80 }, encoder.buffer.data);
207
208 encoder.buffer.clearAndFree();
209 try encoder.length(255);
210 try std.testing.expectEqualSlices(u8, &.{ 0x81, 0xff }, encoder.buffer.data);
211
212 encoder.buffer.clearAndFree();
213 try encoder.length(256);
214 try std.testing.expectEqualSlices(u8, &.{ 0x82, 0x01, 0x00 }, encoder.buffer.data);
215
216 encoder.buffer.clearAndFree();
217 try encoder.length(65535);
218 try std.testing.expectEqualSlices(u8, &.{ 0x82, 0xff, 0xff }, encoder.buffer.data);
219
220 encoder.buffer.clearAndFree();
221 try encoder.length(65536);
222 try std.testing.expectEqualSlices(u8, &.{ 0x83, 0x01, 0x00, 0x00 }, encoder.buffer.data);
160}223}
161224
162const std = @import("std");225const std = @import("std");