| author | |
| committer | |
| log | 3237000d957617120e32e54498cac9afa23cbcd4 |
| tree | 03fc00c8b7738b38db7264c27f3021e8fdedf0e7 |
| parent | 5d7eca6669228cec762fc9063a7ea3cb52af357c |
2 files changed, 174 insertions(+), 1 deletions(-)
lib/std/crypto/tls.zig+109| ... | ... | @@ -349,3 +349,112 @@ pub inline fn int3(x: u24) [3]u8 { |
| 349 | 349 | @truncate(u8, x), |
| 350 | 350 | }; |
| 351 | 351 | } |
| 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+65-1| ... | ... | @@ -402,7 +402,71 @@ pub fn init(stream: net.Stream, host: []const u8) !Client { |
| 402 | 402 | while (hs_i < end_certs) { |
| 403 | 403 | const cert_size = mem.readIntBig(u24, handshake[hs_i..][0..3]); |
| 404 | 404 | 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; | |
| 406 | 470 | const total_ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]); |
| 407 | 471 | hs_i += 2; |
| 408 | 472 | hs_i += total_ext_size; |