@@ -79,6 +79,12 @@ pub const Parsed = struct {
7979 pub_key_algo : AlgorithmCategory ,
8080 pub_key_slice : Slice ,
8181 message_slice : Slice ,
82+ validity : Validity ,
83+
84+ pub const Validity = struct {
85+ not_before : u64 ,
86+ not_after : u64 ,
87+ };
8288
8389 pub const Slice = der .Element .Slice ;
8490
@@ -110,15 +116,20 @@ pub const Parsed = struct {
110116 return p .slice (p .message_slice );
111117 }
112118
119+ /// This function checks the time validity for the subject only. Checking
120+ /// the issuer's time validity is out of scope.
113121 pub fn verify (parsed_subject : Parsed , parsed_issuer : Parsed ) ! void {
114122 // Check that the subject's issuer name matches the issuer's
115123 // subject name.
116124 if (! mem .eql (u8 , parsed_subject .issuer (), parsed_issuer .subject ())) {
117125 return error .CertificateIssuerMismatch ;
118126 }
119127
120- // TODO check the time validity for the subject
121- // TODO check the time validity for the issuer
128+ const now_sec = std .time .timestamp ();
129+ if (now_sec < parsed_subject .validity .not_before )
130+ return error .CertificateNotYetValid ;
131+ if (now_sec > parsed_subject .validity .not_after )
132+ return error .CertificateExpired ;
122133
123134 switch (parsed_subject .signature_algorithm ) {
124135 inline .sha1WithRSAEncryption ,
@@ -157,6 +168,10 @@ pub fn parse(cert: Certificate) !Parsed {
157168 const tbs_signature = try der .parseElement (cert_bytes , serial_number .slice .end );
158169 const issuer = try der .parseElement (cert_bytes , tbs_signature .slice .end );
159170 const validity = try der .parseElement (cert_bytes , issuer .slice .end );
171+ const not_before = try der .parseElement (cert_bytes , validity .slice .start );
172+ const not_before_utc = try parseTime (cert , not_before );
173+ const not_after = try der .parseElement (cert_bytes , not_before .slice .end );
174+ const not_after_utc = try parseTime (cert , not_after );
160175 const subject = try der .parseElement (cert_bytes , validity .slice .end );
161176
162177 const pub_key_info = try der .parseElement (cert_bytes , subject .slice .end );
@@ -198,6 +213,10 @@ pub fn parse(cert: Certificate) !Parsed {
198213 .message_slice = .{ .start = certificate .slice .start , .end = tbs_certificate .slice .end },
199214 .pub_key_algo = pub_key_algo ,
200215 .pub_key_slice = pub_key ,
216+ .validity = .{
217+ .not_before = not_before_utc ,
218+ .not_after = not_after_utc ,
219+ },
201220 };
202221}
203222
@@ -208,7 +227,7 @@ pub fn verify(subject: Certificate, issuer: Certificate) !void {
208227}
209228
210229pub fn contents (cert : Certificate , elem : der.Element ) []const u8 {
211- return cert .buffer [elem .start .. elem .end ];
230+ return cert .buffer [elem .slice . start .. elem . slice .end ];
212231}
213232
214233pub fn parseBitString (cert : Certificate , elem : der.Element ) ! der.Element.Slice {
@@ -217,6 +236,133 @@ pub fn parseBitString(cert: Certificate, elem: der.Element) !der.Element.Slice {
217236 return .{ .start = elem .slice .start + 1 , .end = elem .slice .end };
218237}
219238
239+ /// Returns number of seconds since epoch.
240+ pub fn parseTime (cert : Certificate , elem : der.Element ) ! u64 {
241+ const bytes = cert .contents (elem );
242+ switch (elem .identifier .tag ) {
243+ .utc_time = > {
244+ // Example: "YYMMDD000000Z"
245+ if (bytes .len != 13 )
246+ return error .CertificateTimeInvalid ;
247+ if (bytes [12 ] != 'Z' )
248+ return error .CertificateTimeInvalid ;
249+
250+ return Date .toSeconds (.{
251+ .year = @as (u16 , 2000 ) + try parseTimeDigits (bytes [0.. 2].* , 0 , 99 ),
252+ .month = try parseTimeDigits (bytes [2.. 4].* , 1 , 12 ),
253+ .day = try parseTimeDigits (bytes [4.. 6].* , 1 , 31 ),
254+ .hour = try parseTimeDigits (bytes [6.. 8].* , 0 , 23 ),
255+ .minute = try parseTimeDigits (bytes [8.. 10].* , 0 , 59 ),
256+ .second = try parseTimeDigits (bytes [10.. 12].* , 0 , 59 ),
257+ });
258+ },
259+ .generalized_time = > {
260+ // Examples:
261+ // "19920521000000Z"
262+ // "19920622123421Z"
263+ // "19920722132100.3Z"
264+ if (bytes .len < 15 )
265+ return error .CertificateTimeInvalid ;
266+ return Date .toSeconds (.{
267+ .year = try parseYear4 (bytes [0.. 4]),
268+ .month = try parseTimeDigits (bytes [4.. 6].* , 1 , 12 ),
269+ .day = try parseTimeDigits (bytes [6.. 8].* , 1 , 31 ),
270+ .hour = try parseTimeDigits (bytes [8.. 10].* , 0 , 23 ),
271+ .minute = try parseTimeDigits (bytes [10.. 12].* , 0 , 59 ),
272+ .second = try parseTimeDigits (bytes [12.. 14].* , 0 , 59 ),
273+ });
274+ },
275+ else = > return error .CertificateFieldHasWrongDataType ,
276+ }
277+ }
278+
279+ const Date = struct {
280+ /// example: 1999
281+ year : u16 ,
282+ /// range: 1 to 12
283+ month : u8 ,
284+ /// range: 1 to 31
285+ day : u8 ,
286+ /// range: 0 to 59
287+ hour : u8 ,
288+ /// range: 0 to 59
289+ minute : u8 ,
290+ /// range: 0 to 59
291+ second : u8 ,
292+
293+ /// Convert to number of seconds since epoch.
294+ pub fn toSeconds (date : Date ) u64 {
295+ var sec : u64 = 0 ;
296+
297+ {
298+ var year : u16 = 1970 ;
299+ while (year < date .year ) : (year += 1 ) {
300+ const days : u64 = std .time .epoch .getDaysInYear (year );
301+ sec += days * std .time .epoch .secs_per_day ;
302+ }
303+ }
304+
305+ {
306+ const is_leap = std .time .epoch .isLeapYear (date .year );
307+ var month : u4 = 1 ;
308+ while (month < date .month ) : (month += 1 ) {
309+ const days : u64 = std .time .epoch .getDaysInMonth (
310+ @intToEnum (std .time .epoch .YearLeapKind , @boolToInt (is_leap )),
311+ @intToEnum (std .time .epoch .Month , month ),
312+ );
313+ sec += days * std .time .epoch .secs_per_day ;
314+ }
315+ }
316+
317+ sec += (date .day - 1 ) * @as (u64 , std .time .epoch .secs_per_day );
318+ sec += date .hour * @as (u64 , 60 * 60 );
319+ sec += date .minute * @as (u64 , 60 );
320+ sec += date .second ;
321+
322+ return sec ;
323+ }
324+ };
325+
326+ pub fn parseTimeDigits (nn : @Vector (2 , u8 ), min : u8 , max : u8 ) ! u8 {
327+ const zero : @Vector (2 , u8 ) = .{ '0' , '0' };
328+ const mm : @Vector (2 , u8 ) = .{ 10 , 1 };
329+ const result = @reduce (.Add , (nn -% zero ) *% mm );
330+ if (result < min ) return error .CertificateTimeInvalid ;
331+ if (result > max ) return error .CertificateTimeInvalid ;
332+ return result ;
333+ }
334+
335+ test parseTimeDigits {
336+ const expectEqual = std .testing .expectEqual ;
337+ try expectEqual (@as (u8 , 0 ), try parseTimeDigits ("00" .* , 0 , 99 ));
338+ try expectEqual (@as (u8 , 99 ), try parseTimeDigits ("99" .* , 0 , 99 ));
339+ try expectEqual (@as (u8 , 42 ), try parseTimeDigits ("42" .* , 0 , 99 ));
340+
341+ const expectError = std .testing .expectError ;
342+ try expectError (error .CertificateTimeInvalid , parseTimeDigits ("13" .* , 1 , 12 ));
343+ try expectError (error .CertificateTimeInvalid , parseTimeDigits ("00" .* , 1 , 12 ));
344+ }
345+
346+ pub fn parseYear4 (text : * const [4 ]u8 ) ! u16 {
347+ const nnnn : @Vector (4 , u16 ) = .{ text [0 ], text [1 ], text [2 ], text [3 ] };
348+ const zero : @Vector (4 , u16 ) = .{ '0' , '0' , '0' , '0' };
349+ const mmmm : @Vector (4 , u16 ) = .{ 1000 , 100 , 10 , 1 };
350+ const result = @reduce (.Add , (nnnn -% zero ) *% mmmm );
351+ if (result > 9999 ) return error .CertificateTimeInvalid ;
352+ return result ;
353+ }
354+
355+ test parseYear4 {
356+ const expectEqual = std .testing .expectEqual ;
357+ try expectEqual (@as (u16 , 0 ), try parseYear4 ("0000" ));
358+ try expectEqual (@as (u16 , 9999 ), try parseYear4 ("9999" ));
359+ try expectEqual (@as (u16 , 1988 ), try parseYear4 ("1988" ));
360+
361+ const expectError = std .testing .expectError ;
362+ try expectError (error .CertificateTimeInvalid , parseYear4 ("999b" ));
363+ try expectError (error .CertificateTimeInvalid , parseYear4 ("crap" ));
364+ }
365+
220366pub fn parseAlgorithm (bytes : []const u8 , element : der.Element ) ! Algorithm {
221367 if (element .identifier .tag != .object_identifier )
222368 return error .CertificateFieldHasWrongDataType ;
@@ -241,7 +387,13 @@ pub fn parseAttribute(bytes: []const u8, element: der.Element) !Attribute {
241387 return error .CertificateHasUnrecognizedAlgorithm ;
242388}
243389
244- fn verifyRsa (comptime Hash : type , message : []const u8 , sig : []const u8 , pub_key_algo : AlgorithmCategory , pub_key : []const u8 ) ! void {
390+ fn verifyRsa (
391+ comptime Hash : type ,
392+ message : []const u8 ,
393+ sig : []const u8 ,
394+ pub_key_algo : AlgorithmCategory ,
395+ pub_key : []const u8 ,
396+ ) ! void {
245397 if (pub_key_algo != .rsaEncryption ) return error .CertificateSignatureAlgorithmMismatch ;
246398 const pub_key_seq = try der .parseElement (pub_key , 0 );
247399 if (pub_key_seq .identifier .tag != .sequence ) return error .CertificateFieldHasWrongDataType ;
@@ -328,6 +480,10 @@ const mem = std.mem;
328480const der = std .crypto .der ;
329481const Certificate = @This ();
330482
483+ test {
484+ _ = Bundle ;
485+ }
486+
331487/// TODO: replace this with Frank's upcoming RSA implementation. the verify
332488/// function won't have the possibility of failure - it will either identify a
333489/// valid signature or an invalid signature.
0 commit comments