zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 761f8d2f8a244b01f303a0a03bfda9b56c06e18f (tree)
parent 5ab3a21e390384672bc58171c9985d2d1d11b46e
Author: Frank Denis <github@pureftpd.org>
Date:   Sun,  3 May 2026 17:48:20 +0200

der.Decoder: fix int slice and sign-extension

Diffstat:
Mlib/std/crypto/codecs/asn1/der.zig | 17+++++++++++++++++
Mlib/std/crypto/codecs/asn1/der/Decoder.zig | 34+++++++++++++++++++++++-----------
2 files changed, 40 insertions(+), 11 deletions(-)

diff --git a/lib/std/crypto/codecs/asn1/der.zig b/lib/std/crypto/codecs/asn1/der.zig @@ -49,6 +49,23 @@ test decode { try std.testing.expectEqualDeep(test_case.value, decoded); } +test "integer round trip across signed and unsigned boundaries" { + const allocator = std.testing.allocator; + inline for (.{ u8, u16, u32, i8, i16, i32 }) |T| { + const cases = comptime blk: { + const min = std.math.minInt(T); + const max = std.math.maxInt(T); + break :blk [_]T{ 0, 1, max, min, @divTrunc(max, 2), @divTrunc(min, 2) }; + }; + for (cases) |value| { + const buf = try encode(allocator, value); + defer allocator.free(buf); + const decoded = try decode(T, buf); + try std.testing.expectEqual(value, decoded); + } + } +} + test { _ = Decoder; _ = Encoder; diff --git a/lib/std/crypto/codecs/asn1/der/Decoder.zig b/lib/std/crypto/codecs/asn1/der/Decoder.zig @@ -111,21 +111,23 @@ pub fn view(self: Decoder, elem: Element) []const u8 { } fn int(comptime T: type, value: []const u8) error{ NonCanonical, LargeValue }!T { - if (@typeInfo(T).int.bits % 8 != 0) @compileError("T must be byte aligned"); - - var bytes = value; - if (bytes.len >= 2) { - if (bytes[0] == 0) { - if (@clz(bytes[1]) > 0) return error.NonCanonical; - bytes.ptr += 1; - } - if (bytes[0] == 0xff and @clz(bytes[1]) == 0) return error.NonCanonical; + const info = @typeInfo(T).int; + if (info.bits % 8 != 0) @compileError("T must be byte aligned"); + + if (value.len == 0) return error.NonCanonical; + if (value.len >= 2) { + if (value[0] == 0x00 and value[1] & 0x80 == 0) return error.NonCanonical; + if (value[0] == 0xff and value[1] & 0x80 != 0) return error.NonCanonical; } + const had_sign_byte = value.len >= 2 and value[0] == 0x00; + const bytes = if (had_sign_byte) value[1..] else value; if (bytes.len > @sizeOf(T)) return error.LargeValue; - if (@sizeOf(T) == 1) return @bitCast(bytes[0]); - return std.mem.readVarInt(T, bytes, .big); + const sign_extend = info.signedness == .signed and !had_sign_byte and bytes[0] & 0x80 != 0; + var buf: [@sizeOf(T)]u8 = @splat(if (sign_extend) 0xff else 0); + @memcpy(buf[buf.len - bytes.len ..], bytes); + return std.mem.readInt(T, &buf, .big); } test int { @@ -136,6 +138,16 @@ test int { const big = [_]u8{ 0xef, 0xff }; try expectError(error.LargeValue, int(u8, &big)); try expectEqual(0xefff, int(u16, &big)); + + try expectEqual(@as(u16, 255), try int(u16, &.{ 0x00, 0xff })); + try expectEqual(@as(u16, 0x8000), try int(u16, &.{ 0x00, 0x80, 0x00 })); + + try expectEqual(@as(i8, -1), try int(i8, &.{0xff})); + try expectEqual(@as(i16, -1), try int(i16, &.{0xff})); + try expectEqual(@as(i16, -128), try int(i16, &.{0x80})); + try expectEqual(@as(i16, -129), try int(i16, &.{ 0xff, 0x7f })); + try expectEqual(@as(i16, 255), try int(i16, &.{ 0x00, 0xff })); + try expectEqual(@as(i32, 0x7fffffff), try int(i32, &.{ 0x7f, 0xff, 0xff, 0xff })); } test Decoder {