Skip to content

Commit

Permalink
std.crypto.tls: rudimentary certificate parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewrk committed Dec 19, 2022
1 parent 40deee1 commit 3089041
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 1 deletion.
109 changes: 109 additions & 0 deletions lib/std/crypto/tls.zig
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,112 @@ pub inline fn int3(x: u24) [3]u8 {
@truncate(u8, x),
};
}

pub const Der = struct {
pub const Class = enum(u2) {
universal,
application,
context_specific,
private,
};

pub const PC = enum(u1) {
primitive,
constructed,
};

pub const Identifier = packed struct(u8) {
tag: Tag,
pc: PC,
class: Class,
};

pub const Tag = enum(u5) {
boolean = 1,
integer = 2,
bitstring = 3,
null = 5,
object_identifier = 6,
sequence = 16,
_,
};

pub const Oid = enum {
commonName,
countryName,
localityName,
stateOrProvinceName,
organizationName,
organizationalUnitName,
sha256WithRSAEncryption,
sha384WithRSAEncryption,
sha512WithRSAEncryption,
sha224WithRSAEncryption,

pub const map = std.ComptimeStringMap(Oid, .{
.{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
.{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
.{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
.{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
.{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
.{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
});
};

pub const Element = struct {
identifier: Identifier,
contents: []const u8,
};

pub const ParseElementError = error{CertificateHasFieldWithInvalidLength};

pub fn parseElement(bytes: []const u8, index: *usize) ParseElementError!Der.Element {
var i = index.*;
const identifier = @bitCast(Identifier, bytes[i]);
i += 1;
const size_byte = bytes[i];
i += 1;
if ((size_byte >> 7) == 0) {
const contents = bytes[i..][0..size_byte];
index.* = i + contents.len;
return .{
.identifier = identifier,
.contents = contents,
};
}

const len_size = @truncate(u7, size_byte);
if (len_size > @sizeOf(usize)) {
return error.CertificateHasFieldWithInvalidLength;
}

const end = i + len_size;
var long_form_size: usize = 0;
while (i < end) : (i += 1) {
long_form_size = (long_form_size << 8) | bytes[i];
}

const contents = bytes[i..][0..long_form_size];
index.* = i + contents.len;

return .{
.identifier = identifier,
.contents = contents,
};
}

pub const ParseObjectIdError = error{
CertificateHasUnrecognizedObjectId,
CertificateFieldHasWrongDataType,
} || ParseElementError;

pub fn parseObjectId(bytes: []const u8, index: *usize) ParseObjectIdError!Oid {
const oid_element = try parseElement(bytes, index);
if (oid_element.identifier.tag != .object_identifier) return error.CertificateFieldHasWrongDataType;
return Oid.map.get(oid_element.contents) orelse return error.CertificateHasUnrecognizedObjectId;
}
};
66 changes: 65 additions & 1 deletion lib/std/crypto/tls/Client.zig
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,71 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
while (hs_i < end_certs) {
const cert_size = mem.readIntBig(u24, handshake[hs_i..][0..3]);
hs_i += 3;
hs_i += cert_size;
const end_cert = hs_i + cert_size;

const certificate = try tls.Der.parseElement(handshake, &hs_i);
{
var cert_i: usize = 0;
const tbs_certificate = try tls.Der.parseElement(certificate.contents, &cert_i);
{
var tbs_i: usize = 0;
const version = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
const serial_number = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
const signature = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
const issuer = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
const validity = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
const subject = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
const subject_pub_key = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
const extensions = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);

// RFC 5280, section 4.1.2.3:
// "This field MUST contain the same algorithm identifier as
// the signatureAlgorithm field in the sequence Certificate."
_ = signature;

_ = issuer;
_ = validity;

std.debug.print("version: {any} '{}'\n", .{
version.identifier, std.fmt.fmtSliceHexLower(version.contents),
});

std.debug.print("serial_number: {any} {}\n", .{
serial_number.identifier,
std.fmt.fmtSliceHexLower(serial_number.contents),
});

std.debug.print("subject: {any} {}\n", .{
subject.identifier,
std.fmt.fmtSliceHexLower(subject.contents),
});

std.debug.print("subject pub key: {any} {}\n", .{
subject_pub_key.identifier,
std.fmt.fmtSliceHexLower(subject_pub_key.contents),
});

std.debug.print("extensions: {any} {}\n", .{
extensions.identifier,
std.fmt.fmtSliceHexLower(extensions.contents),
});
}
const signature_algorithm = try tls.Der.parseElement(certificate.contents, &cert_i);
const signature_value = try tls.Der.parseElement(certificate.contents, &cert_i);

{
var sa_i: usize = 0;
const algorithm = try tls.Der.parseObjectId(signature_algorithm.contents, &sa_i);
std.debug.print("cert has this signature algorithm: {any}\n", .{algorithm});
//const parameters = try tls.Der.parseElement(signature_algorithm.contents, &sa_i);
}

std.debug.print("signature_value: {any} {d} bytes\n", .{
signature_value.identifier, signature_value.contents.len,
});
}

hs_i = end_cert;
const total_ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);
hs_i += 2;
hs_i += total_ext_size;
Expand Down

0 comments on commit 3089041

Please sign in to comment.