From bafad9644cf1d5218bbadf127ddc7d63de596740 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 20 Apr 2026 12:13:52 +0200 Subject: [PATCH] std.crypto.Certificate: fix UTCTime year interpretation UTCTime years in the range 50-99 must map to 1950-1999, but the parser unconditionally added 2000, producing dates 100 years in the future. This caused verify() to accept certificates whose validity actually expired decades ago. Change that to match what OpenSSL, BoringSSL, etc. do --- lib/std/crypto/Certificate.zig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/std/crypto/Certificate.zig b/lib/std/crypto/Certificate.zig index 1ba076e1fa..dc543205c0 100644 --- a/lib/std/crypto/Certificate.zig +++ b/lib/std/crypto/Certificate.zig @@ -580,7 +580,10 @@ pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!u64 { return error.CertificateTimeInvalid; return Date.toSeconds(.{ - .year = @as(u16, 2000) + try parseTimeDigits(bytes[0..2], 0, 99), + .year = blk: { + const year = try parseTimeDigits(bytes[0..2], 0, 99); + break :blk if (year < 50) @as(u16, 2000) + year else @as(u16, 1900) + year; + }, .month = try parseTimeDigits(bytes[2..4], 1, 12), .day = try parseTimeDigits(bytes[4..6], 1, 31), .hour = try parseTimeDigits(bytes[6..8], 0, 23), @@ -670,6 +673,15 @@ pub fn parseTimeDigits(text: *const [2]u8, min: u8, max: u8) !u8 { return @intCast(result); } +test "parseTime UTCTime year mapping per RFC 5280" { + const utc_time_id: der.Identifier = .{ .tag = .utc_time, .pc = .primitive, .class = .universal }; + const elem = der.Element{ .identifier = utc_time_id, .slice = .{ .start = 0, .end = 13 } }; + const cert49 = Certificate{ .buffer = "490101000000Z", .index = 0 }; + try std.testing.expectEqual(@as(u64, 2493072000), try cert49.parseTime(elem)); + const cert99 = Certificate{ .buffer = "990101000000Z", .index = 0 }; + try std.testing.expectEqual(@as(u64, 915148800), try cert99.parseTime(elem)); +} + test parseTimeDigits { const expectEqual = std.testing.expectEqual; try expectEqual(@as(u8, 0), try parseTimeDigits("00", 0, 99));