Skip to content

Commit c1a096d

Browse files
committed
std.crypto.tls: rudimentary certificate parsing
1 parent 1b34b27 commit c1a096d

File tree

2 files changed

+174
-1
lines changed

2 files changed

+174
-1
lines changed

lib/std/crypto/tls.zig

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,112 @@ pub inline fn int3(x: u24) [3]u8 {
349349
@truncate(u8, x),
350350
};
351351
}
352+
353+
pub const Der = struct {
354+
pub const Class = enum(u2) {
355+
universal,
356+
application,
357+
context_specific,
358+
private,
359+
};
360+
361+
pub const PC = enum(u1) {
362+
primitive,
363+
constructed,
364+
};
365+
366+
pub const Identifier = packed struct(u8) {
367+
tag: Tag,
368+
pc: PC,
369+
class: Class,
370+
};
371+
372+
pub const Tag = enum(u5) {
373+
boolean = 1,
374+
integer = 2,
375+
bitstring = 3,
376+
null = 5,
377+
object_identifier = 6,
378+
sequence = 16,
379+
_,
380+
};
381+
382+
pub const Oid = enum {
383+
commonName,
384+
countryName,
385+
localityName,
386+
stateOrProvinceName,
387+
organizationName,
388+
organizationalUnitName,
389+
sha256WithRSAEncryption,
390+
sha384WithRSAEncryption,
391+
sha512WithRSAEncryption,
392+
sha224WithRSAEncryption,
393+
394+
pub const map = std.ComptimeStringMap(Oid, .{
395+
.{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
396+
.{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
397+
.{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
398+
.{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
399+
.{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
400+
.{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
401+
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
402+
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
403+
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
404+
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
405+
});
406+
};
407+
408+
pub const Element = struct {
409+
identifier: Identifier,
410+
contents: []const u8,
411+
};
412+
413+
pub const ParseElementError = error{CertificateHasFieldWithInvalidLength};
414+
415+
pub fn parseElement(bytes: []const u8, index: *usize) ParseElementError!Der.Element {
416+
var i = index.*;
417+
const identifier = @bitCast(Identifier, bytes[i]);
418+
i += 1;
419+
const size_byte = bytes[i];
420+
i += 1;
421+
if ((size_byte >> 7) == 0) {
422+
const contents = bytes[i..][0..size_byte];
423+
index.* = i + contents.len;
424+
return .{
425+
.identifier = identifier,
426+
.contents = contents,
427+
};
428+
}
429+
430+
const len_size = @truncate(u7, size_byte);
431+
if (len_size > @sizeOf(usize)) {
432+
return error.CertificateHasFieldWithInvalidLength;
433+
}
434+
435+
const end = i + len_size;
436+
var long_form_size: usize = 0;
437+
while (i < end) : (i += 1) {
438+
long_form_size = (long_form_size << 8) | bytes[i];
439+
}
440+
441+
const contents = bytes[i..][0..long_form_size];
442+
index.* = i + contents.len;
443+
444+
return .{
445+
.identifier = identifier,
446+
.contents = contents,
447+
};
448+
}
449+
450+
pub const ParseObjectIdError = error{
451+
CertificateHasUnrecognizedObjectId,
452+
CertificateFieldHasWrongDataType,
453+
} || ParseElementError;
454+
455+
pub fn parseObjectId(bytes: []const u8, index: *usize) ParseObjectIdError!Oid {
456+
const oid_element = try parseElement(bytes, index);
457+
if (oid_element.identifier.tag != .object_identifier) return error.CertificateFieldHasWrongDataType;
458+
return Oid.map.get(oid_element.contents) orelse return error.CertificateHasUnrecognizedObjectId;
459+
}
460+
};

lib/std/crypto/tls/Client.zig

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,71 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
402402
while (hs_i < end_certs) {
403403
const cert_size = mem.readIntBig(u24, handshake[hs_i..][0..3]);
404404
hs_i += 3;
405-
hs_i += cert_size;
405+
const end_cert = hs_i + cert_size;
406+
407+
const certificate = try tls.Der.parseElement(handshake, &hs_i);
408+
{
409+
var cert_i: usize = 0;
410+
const tbs_certificate = try tls.Der.parseElement(certificate.contents, &cert_i);
411+
{
412+
var tbs_i: usize = 0;
413+
const version = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
414+
const serial_number = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
415+
const signature = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
416+
const issuer = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
417+
const validity = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
418+
const subject = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
419+
const subject_pub_key = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
420+
const extensions = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
421+
422+
// RFC 5280, section 4.1.2.3:
423+
// "This field MUST contain the same algorithm identifier as
424+
// the signatureAlgorithm field in the sequence Certificate."
425+
_ = signature;
426+
427+
_ = issuer;
428+
_ = validity;
429+
430+
std.debug.print("version: {any} '{}'\n", .{
431+
version.identifier, std.fmt.fmtSliceHexLower(version.contents),
432+
});
433+
434+
std.debug.print("serial_number: {any} {}\n", .{
435+
serial_number.identifier,
436+
std.fmt.fmtSliceHexLower(serial_number.contents),
437+
});
438+
439+
std.debug.print("subject: {any} {}\n", .{
440+
subject.identifier,
441+
std.fmt.fmtSliceHexLower(subject.contents),
442+
});
443+
444+
std.debug.print("subject pub key: {any} {}\n", .{
445+
subject_pub_key.identifier,
446+
std.fmt.fmtSliceHexLower(subject_pub_key.contents),
447+
});
448+
449+
std.debug.print("extensions: {any} {}\n", .{
450+
extensions.identifier,
451+
std.fmt.fmtSliceHexLower(extensions.contents),
452+
});
453+
}
454+
const signature_algorithm = try tls.Der.parseElement(certificate.contents, &cert_i);
455+
const signature_value = try tls.Der.parseElement(certificate.contents, &cert_i);
456+
457+
{
458+
var sa_i: usize = 0;
459+
const algorithm = try tls.Der.parseObjectId(signature_algorithm.contents, &sa_i);
460+
std.debug.print("cert has this signature algorithm: {any}\n", .{algorithm});
461+
//const parameters = try tls.Der.parseElement(signature_algorithm.contents, &sa_i);
462+
}
463+
464+
std.debug.print("signature_value: {any} {d} bytes\n", .{
465+
signature_value.identifier, signature_value.contents.len,
466+
});
467+
}
468+
469+
hs_i = end_cert;
406470
const total_ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);
407471
hs_i += 2;
408472
hs_i += total_ext_size;

0 commit comments

Comments
 (0)