authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-04-20 12:13:52+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-20 21:45:08+02:00
log98ddebc380fd12b91c00b90c049435117d03274b
tree8e6a56baef694647cdc3b83bacfa6c190be5e2d2
parent525aff6048adae9b1a17db53bfeb1b9a02c3571f

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

1 files changed, 13 insertions(+), 1 deletions(-)

lib/std/crypto/Certificate.zig+13-1
......@@ -580,7 +580,10 @@ pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!u64 {
580580 return error.CertificateTimeInvalid;
581581
582582 return Date.toSeconds(.{
583 .year = @as(u16, 2000) + try parseTimeDigits(bytes[0..2], 0, 99),
583 .year = blk: {
584 const year = try parseTimeDigits(bytes[0..2], 0, 99);
585 break :blk if (year < 50) @as(u16, 2000) + year else @as(u16, 1900) + year;
586 },
584587 .month = try parseTimeDigits(bytes[2..4], 1, 12),
585588 .day = try parseTimeDigits(bytes[4..6], 1, 31),
586589 .hour = try parseTimeDigits(bytes[6..8], 0, 23),
......@@ -670,6 +673,15 @@ pub fn parseTimeDigits(text: *const [2]u8, min: u8, max: u8) !u8 {
670673 return @intCast(result);
671674}
672675
676test "parseTime UTCTime year mapping per RFC 5280" {
677 const utc_time_id: der.Identifier = .{ .tag = .utc_time, .pc = .primitive, .class = .universal };
678 const elem = der.Element{ .identifier = utc_time_id, .slice = .{ .start = 0, .end = 13 } };
679 const cert49 = Certificate{ .buffer = "490101000000Z", .index = 0 };
680 try std.testing.expectEqual(@as(u64, 2493072000), try cert49.parseTime(elem));
681 const cert99 = Certificate{ .buffer = "990101000000Z", .index = 0 };
682 try std.testing.expectEqual(@as(u64, 915148800), try cert99.parseTime(elem));
683}
684
673685test parseTimeDigits {
674686 const expectEqual = std.testing.expectEqual;
675687 try expectEqual(@as(u8, 0), try parseTimeDigits("00", 0, 99));