std.crypto.tls: rudimentary certificate parsing
This commit is contained in:
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user