diff --git a/lib/std/crypto/codecs/asn1/der.zig b/lib/std/crypto/codecs/asn1/der.zig index 4395f9f3b61b8856209198a79733d2d48ebbb5f5..41e1f769fd19fc3364bf14cf0d6b12cc10b7de6f 100644 --- 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 index 25a4139d6e687c4fbfa92a43a0cf3c126db2806c..3403c73047e21c6ee58c05741d2e1bbac49da3f6 100644 --- 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"); + const info = @typeInfo(T).int; + if (info.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; + 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 {