mirror of
https://github.com/ziglang/zig.git
synced 2024-11-13 23:52:57 +00:00
std.crypto.tls: implement TLSv1.2
This commit is contained in:
parent
ee9f00d673
commit
c2a779ae79
@ -151,7 +151,9 @@ pub const Ed25519 = struct {
|
||||
a: Curve,
|
||||
expected_r: Curve,
|
||||
|
||||
fn init(sig: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {
|
||||
pub const InitError = NonCanonicalError || EncodingError || IdentityElementError;
|
||||
|
||||
fn init(sig: Signature, public_key: PublicKey) InitError!Verifier {
|
||||
const r = sig.r;
|
||||
const s = sig.s;
|
||||
try Curve.scalar.rejectNonCanonical(s);
|
||||
@ -173,8 +175,11 @@ pub const Ed25519 = struct {
|
||||
self.h.update(msg);
|
||||
}
|
||||
|
||||
pub const VerifyError = WeakPublicKeyError || IdentityElementError ||
|
||||
SignatureVerificationError;
|
||||
|
||||
/// Verify that the signature is valid for the entire message.
|
||||
pub fn verify(self: *Verifier) (SignatureVerificationError || WeakPublicKeyError || IdentityElementError)!void {
|
||||
pub fn verify(self: *Verifier) VerifyError!void {
|
||||
var hram64: [Sha512.digest_length]u8 = undefined;
|
||||
self.h.final(&hram64);
|
||||
const hram = Curve.scalar.reduce64(hram64);
|
||||
@ -197,10 +202,10 @@ pub const Ed25519 = struct {
|
||||
s: CompressedScalar,
|
||||
|
||||
/// Return the raw signature (r, s) in little-endian format.
|
||||
pub fn toBytes(self: Signature) [encoded_length]u8 {
|
||||
pub fn toBytes(sig: Signature) [encoded_length]u8 {
|
||||
var bytes: [encoded_length]u8 = undefined;
|
||||
bytes[0..Curve.encoded_length].* = self.r;
|
||||
bytes[Curve.encoded_length..].* = self.s;
|
||||
bytes[0..Curve.encoded_length].* = sig.r;
|
||||
bytes[Curve.encoded_length..].* = sig.s;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@ -214,17 +219,26 @@ pub const Ed25519 = struct {
|
||||
}
|
||||
|
||||
/// Create a Verifier for incremental verification of a signature.
|
||||
pub fn verifier(self: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {
|
||||
return Verifier.init(self, public_key);
|
||||
pub fn verifier(sig: Signature, public_key: PublicKey) Verifier.InitError!Verifier {
|
||||
return Verifier.init(sig, public_key);
|
||||
}
|
||||
|
||||
pub const VerifyError = Verifier.InitError || Verifier.VerifyError;
|
||||
|
||||
/// Verify the signature against a message and public key.
|
||||
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
||||
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
||||
pub fn verify(self: Signature, msg: []const u8, public_key: PublicKey) (IdentityElementError || NonCanonicalError || SignatureVerificationError || EncodingError || WeakPublicKeyError)!void {
|
||||
var st = try Verifier.init(self, public_key);
|
||||
st.update(msg);
|
||||
return st.verify();
|
||||
pub fn verify(sig: Signature, msg: []const u8, public_key: PublicKey) VerifyError!void {
|
||||
try sig.concatVerify(&.{msg}, public_key);
|
||||
}
|
||||
|
||||
/// Verify the signature against a concatenated message and public key.
|
||||
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
||||
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
||||
pub fn concatVerify(sig: Signature, msg: []const []const u8, public_key: PublicKey) VerifyError!void {
|
||||
var st = try Verifier.init(sig, public_key);
|
||||
for (msg) |part| st.update(part);
|
||||
try st.verify();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -20,18 +20,18 @@ pub const Algorithm = enum {
|
||||
curveEd25519,
|
||||
|
||||
pub const map = std.StaticStringMap(Algorithm).initComptime(.{
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
|
||||
.{ &[_]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 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
|
||||
.{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
|
||||
.{ &.{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
|
||||
});
|
||||
|
||||
pub fn Hash(comptime algorithm: Algorithm) type {
|
||||
@ -49,13 +49,15 @@ pub const Algorithm = enum {
|
||||
|
||||
pub const AlgorithmCategory = enum {
|
||||
rsaEncryption,
|
||||
rsassa_pss,
|
||||
X9_62_id_ecPublicKey,
|
||||
curveEd25519,
|
||||
|
||||
pub const map = std.StaticStringMap(AlgorithmCategory).initComptime(.{
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },
|
||||
.{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A }, .rsassa_pss },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },
|
||||
.{ &.{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
|
||||
});
|
||||
};
|
||||
|
||||
@ -74,18 +76,18 @@ pub const Attribute = enum {
|
||||
domainComponent,
|
||||
|
||||
pub const map = std.StaticStringMap(Attribute).initComptime(.{
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x05 }, .serialNumber },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x09 }, .streetAddress },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x11 }, .postalCode },
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
|
||||
.{ &[_]u8{ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }, .domainComponent },
|
||||
.{ &.{ 0x55, 0x04, 0x03 }, .commonName },
|
||||
.{ &.{ 0x55, 0x04, 0x05 }, .serialNumber },
|
||||
.{ &.{ 0x55, 0x04, 0x06 }, .countryName },
|
||||
.{ &.{ 0x55, 0x04, 0x07 }, .localityName },
|
||||
.{ &.{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
|
||||
.{ &.{ 0x55, 0x04, 0x09 }, .streetAddress },
|
||||
.{ &.{ 0x55, 0x04, 0x0A }, .organizationName },
|
||||
.{ &.{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
|
||||
.{ &.{ 0x55, 0x04, 0x11 }, .postalCode },
|
||||
.{ &.{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
|
||||
.{ &.{ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }, .domainComponent },
|
||||
});
|
||||
};
|
||||
|
||||
@ -95,9 +97,9 @@ pub const NamedCurve = enum {
|
||||
X9_62_prime256v1,
|
||||
|
||||
pub const map = std.StaticStringMap(NamedCurve).initComptime(.{
|
||||
.{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 },
|
||||
.{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 },
|
||||
.{ &.{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 },
|
||||
.{ &.{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 },
|
||||
});
|
||||
|
||||
pub fn Curve(comptime curve: NamedCurve) type {
|
||||
@ -131,28 +133,28 @@ pub const ExtensionId = enum {
|
||||
netscape_comment,
|
||||
|
||||
pub const map = std.StaticStringMap(ExtensionId).initComptime(.{
|
||||
.{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x01 }, .authority_key_identifier },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x07 }, .subject_alt_name },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x0E }, .subject_key_identifier },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x0F }, .key_usage },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x0A }, .basic_constraints },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x10 }, .private_key_usage_period },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x11 }, .subject_alt_name },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x12 }, .issuer_alt_name },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x13 }, .basic_constraints },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x14 }, .crl_number },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x1F }, .crl_distribution_points },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x20 }, .certificate_policies },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x23 }, .authority_key_identifier },
|
||||
.{ &[_]u8{ 0x55, 0x1D, 0x25 }, .ext_key_usage },
|
||||
.{ &[_]u8{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01 }, .msCertsrvCAVersion },
|
||||
.{ &[_]u8{ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }, .info_access },
|
||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF6, 0x7D, 0x07, 0x41, 0x00 }, .entrustVersInfo },
|
||||
.{ &[_]u8{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02 }, .enroll_certtype },
|
||||
.{ &[_]u8{ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c }, .pe_logotype },
|
||||
.{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 }, .netscape_cert_type },
|
||||
.{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d }, .netscape_comment },
|
||||
.{ &.{ 0x55, 0x04, 0x03 }, .commonName },
|
||||
.{ &.{ 0x55, 0x1D, 0x01 }, .authority_key_identifier },
|
||||
.{ &.{ 0x55, 0x1D, 0x07 }, .subject_alt_name },
|
||||
.{ &.{ 0x55, 0x1D, 0x0E }, .subject_key_identifier },
|
||||
.{ &.{ 0x55, 0x1D, 0x0F }, .key_usage },
|
||||
.{ &.{ 0x55, 0x1D, 0x0A }, .basic_constraints },
|
||||
.{ &.{ 0x55, 0x1D, 0x10 }, .private_key_usage_period },
|
||||
.{ &.{ 0x55, 0x1D, 0x11 }, .subject_alt_name },
|
||||
.{ &.{ 0x55, 0x1D, 0x12 }, .issuer_alt_name },
|
||||
.{ &.{ 0x55, 0x1D, 0x13 }, .basic_constraints },
|
||||
.{ &.{ 0x55, 0x1D, 0x14 }, .crl_number },
|
||||
.{ &.{ 0x55, 0x1D, 0x1F }, .crl_distribution_points },
|
||||
.{ &.{ 0x55, 0x1D, 0x20 }, .certificate_policies },
|
||||
.{ &.{ 0x55, 0x1D, 0x23 }, .authority_key_identifier },
|
||||
.{ &.{ 0x55, 0x1D, 0x25 }, .ext_key_usage },
|
||||
.{ &.{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01 }, .msCertsrvCAVersion },
|
||||
.{ &.{ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }, .info_access },
|
||||
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF6, 0x7D, 0x07, 0x41, 0x00 }, .entrustVersInfo },
|
||||
.{ &.{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02 }, .enroll_certtype },
|
||||
.{ &.{ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c }, .pe_logotype },
|
||||
.{ &.{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 }, .netscape_cert_type },
|
||||
.{ &.{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d }, .netscape_comment },
|
||||
});
|
||||
};
|
||||
|
||||
@ -185,6 +187,7 @@ pub const Parsed = struct {
|
||||
|
||||
pub const PubKeyAlgo = union(AlgorithmCategory) {
|
||||
rsaEncryption: void,
|
||||
rsassa_pss: void,
|
||||
X9_62_id_ecPublicKey: NamedCurve,
|
||||
curveEd25519: void,
|
||||
};
|
||||
@ -386,7 +389,7 @@ test "Parsed.checkHostName" {
|
||||
try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.Ziglang.ORG"));
|
||||
}
|
||||
|
||||
pub const ParseError = der.Element.ParseElementError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
|
||||
pub const ParseError = der.Element.ParseError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
|
||||
|
||||
pub fn parse(cert: Certificate) ParseError!Parsed {
|
||||
const cert_bytes = cert.buffer;
|
||||
@ -413,13 +416,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {
|
||||
const pub_key_info = try der.Element.parse(cert_bytes, subject.slice.end);
|
||||
const pub_key_signature_algorithm = try der.Element.parse(cert_bytes, pub_key_info.slice.start);
|
||||
const pub_key_algo_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.start);
|
||||
const pub_key_algo_tag = try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem);
|
||||
var pub_key_algo: Parsed.PubKeyAlgo = undefined;
|
||||
switch (pub_key_algo_tag) {
|
||||
.rsaEncryption => {
|
||||
pub_key_algo = .{ .rsaEncryption = {} };
|
||||
},
|
||||
.X9_62_id_ecPublicKey => {
|
||||
const pub_key_algo: Parsed.PubKeyAlgo = switch (try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem)) {
|
||||
inline else => |tag| @unionInit(Parsed.PubKeyAlgo, @tagName(tag), {}),
|
||||
.X9_62_id_ecPublicKey => pub_key_algo: {
|
||||
// RFC 5480 Section 2.1.1.1 Named Curve
|
||||
// ECParameters ::= CHOICE {
|
||||
// namedCurve OBJECT IDENTIFIER
|
||||
@ -428,12 +427,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {
|
||||
// }
|
||||
const params_elem = try der.Element.parse(cert_bytes, pub_key_algo_elem.slice.end);
|
||||
const named_curve = try parseNamedCurve(cert_bytes, params_elem);
|
||||
pub_key_algo = .{ .X9_62_id_ecPublicKey = named_curve };
|
||||
break :pub_key_algo .{ .X9_62_id_ecPublicKey = named_curve };
|
||||
},
|
||||
.curveEd25519 => {
|
||||
pub_key_algo = .{ .curveEd25519 = {} };
|
||||
},
|
||||
}
|
||||
};
|
||||
const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end);
|
||||
const pub_key = try parseBitString(cert, pub_key_elem);
|
||||
|
||||
@ -731,7 +727,7 @@ pub fn parseVersion(bytes: []const u8, version_elem: der.Element) ParseVersionEr
|
||||
|
||||
fn verifyRsa(
|
||||
comptime Hash: type,
|
||||
message: []const u8,
|
||||
msg: []const u8,
|
||||
sig: []const u8,
|
||||
pub_key_algo: Parsed.PubKeyAlgo,
|
||||
pub_key: []const u8,
|
||||
@ -743,59 +739,14 @@ fn verifyRsa(
|
||||
if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;
|
||||
if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;
|
||||
|
||||
const hash_der = switch (Hash) {
|
||||
crypto.hash.Sha1 => [_]u8{
|
||||
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
|
||||
0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
|
||||
},
|
||||
crypto.hash.sha2.Sha224 => [_]u8{
|
||||
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
|
||||
0x00, 0x04, 0x1c,
|
||||
},
|
||||
crypto.hash.sha2.Sha256 => [_]u8{
|
||||
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
|
||||
0x00, 0x04, 0x20,
|
||||
},
|
||||
crypto.hash.sha2.Sha384 => [_]u8{
|
||||
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
|
||||
0x00, 0x04, 0x30,
|
||||
},
|
||||
crypto.hash.sha2.Sha512 => [_]u8{
|
||||
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
|
||||
0x00, 0x04, 0x40,
|
||||
},
|
||||
else => @compileError("unreachable"),
|
||||
};
|
||||
|
||||
var msg_hashed: [Hash.digest_length]u8 = undefined;
|
||||
Hash.hash(message, &msg_hashed, .{});
|
||||
|
||||
switch (modulus.len) {
|
||||
inline 128, 256, 384, 512 => |modulus_len| {
|
||||
const ps_len = modulus_len - (hash_der.len + msg_hashed.len) - 3;
|
||||
const em: [modulus_len]u8 =
|
||||
[2]u8{ 0, 1 } ++
|
||||
([1]u8{0xff} ** ps_len) ++
|
||||
[1]u8{0} ++
|
||||
hash_der ++
|
||||
msg_hashed;
|
||||
|
||||
const public_key = rsa.PublicKey.fromBytes(exponent, modulus) catch return error.CertificateSignatureInvalid;
|
||||
const em_dec = rsa.encrypt(modulus_len, sig[0..modulus_len].*, public_key) catch |err| switch (err) {
|
||||
error.MessageTooLong => unreachable,
|
||||
};
|
||||
|
||||
if (!mem.eql(u8, &em, &em_dec)) {
|
||||
const public_key = rsa.PublicKey.fromBytes(exponent, modulus) catch
|
||||
return error.CertificateSignatureInvalid;
|
||||
rsa.PKCS1v1_5Signature.verify(modulus_len, sig[0..modulus_len].*, msg, public_key, Hash) catch
|
||||
return error.CertificateSignatureInvalid;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
return error.CertificateSignatureUnsupportedBitCount;
|
||||
},
|
||||
else => return error.CertificateSignatureUnsupportedBitCount,
|
||||
}
|
||||
}
|
||||
|
||||
@ -908,9 +859,9 @@ pub const der = struct {
|
||||
pub const empty: Slice = .{ .start = 0, .end = 0 };
|
||||
};
|
||||
|
||||
pub const ParseElementError = error{CertificateFieldHasInvalidLength};
|
||||
pub const ParseError = error{CertificateFieldHasInvalidLength};
|
||||
|
||||
pub fn parse(bytes: []const u8, index: u32) ParseElementError!Element {
|
||||
pub fn parse(bytes: []const u8, index: u32) Element.ParseError!Element {
|
||||
var i = index;
|
||||
const identifier = @as(Identifier, @bitCast(bytes[i]));
|
||||
i += 1;
|
||||
@ -958,21 +909,41 @@ pub const rsa = struct {
|
||||
const Modulus = std.crypto.ff.Modulus(max_modulus_bits);
|
||||
const Fe = Modulus.Fe;
|
||||
|
||||
/// RFC 3447 8.1 RSASSA-PSS
|
||||
pub const PSSSignature = struct {
|
||||
pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
|
||||
var result = [1]u8{0} ** modulus_len;
|
||||
std.mem.copyForwards(u8, &result, msg);
|
||||
var result: [modulus_len]u8 = undefined;
|
||||
@memcpy(result[0..msg.len], msg);
|
||||
@memset(result[msg.len..], 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn verify(comptime modulus_len: usize, sig: [modulus_len]u8, msg: []const u8, public_key: PublicKey, comptime Hash: type) !void {
|
||||
pub const VerifyError = EncryptError || error{InvalidSignature};
|
||||
|
||||
pub fn verify(
|
||||
comptime modulus_len: usize,
|
||||
sig: [modulus_len]u8,
|
||||
msg: []const u8,
|
||||
public_key: PublicKey,
|
||||
comptime Hash: type,
|
||||
) VerifyError!void {
|
||||
try concatVerify(modulus_len, sig, &.{msg}, public_key, Hash);
|
||||
}
|
||||
|
||||
pub fn concatVerify(
|
||||
comptime modulus_len: usize,
|
||||
sig: [modulus_len]u8,
|
||||
msg: []const []const u8,
|
||||
public_key: PublicKey,
|
||||
comptime Hash: type,
|
||||
) VerifyError!void {
|
||||
const mod_bits = public_key.n.bits();
|
||||
const em_dec = try encrypt(modulus_len, sig, public_key);
|
||||
|
||||
EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash) catch unreachable;
|
||||
try EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash);
|
||||
}
|
||||
|
||||
fn EMSA_PSS_VERIFY(msg: []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type) !void {
|
||||
fn EMSA_PSS_VERIFY(msg: []const []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type) VerifyError!void {
|
||||
// 1. If the length of M is greater than the input limitation for
|
||||
// the hash function (2^61 - 1 octets for SHA-1), output
|
||||
// "inconsistent" and stop.
|
||||
@ -986,7 +957,11 @@ pub const rsa = struct {
|
||||
|
||||
// 2. Let mHash = Hash(M), an octet string of length hLen.
|
||||
var mHash: [Hash.digest_length]u8 = undefined;
|
||||
Hash.hash(msg, &mHash, .{});
|
||||
{
|
||||
var hasher: Hash = .init(.{});
|
||||
for (msg) |part| hasher.update(part);
|
||||
hasher.final(&mHash);
|
||||
}
|
||||
|
||||
// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
|
||||
if (emLen < Hash.digest_length + sLen + 2) {
|
||||
@ -1082,25 +1057,14 @@ pub const rsa = struct {
|
||||
}
|
||||
|
||||
fn MGF1(comptime Hash: type, out: []u8, seed: *const [Hash.digest_length]u8, len: usize) ![]u8 {
|
||||
var counter: usize = 0;
|
||||
var counter: u32 = 0;
|
||||
var idx: usize = 0;
|
||||
var c: [4]u8 = undefined;
|
||||
var hash: [Hash.digest_length + c.len]u8 = undefined;
|
||||
@memcpy(hash[0..Hash.digest_length], seed);
|
||||
var hashed: [Hash.digest_length]u8 = undefined;
|
||||
var hash = seed.* ++ @as([4]u8, undefined);
|
||||
|
||||
while (idx < len) {
|
||||
c[0] = @as(u8, @intCast((counter >> 24) & 0xFF));
|
||||
c[1] = @as(u8, @intCast((counter >> 16) & 0xFF));
|
||||
c[2] = @as(u8, @intCast((counter >> 8) & 0xFF));
|
||||
c[3] = @as(u8, @intCast(counter & 0xFF));
|
||||
|
||||
std.mem.copyForwards(u8, hash[seed.len..], &c);
|
||||
Hash.hash(&hash, &hashed, .{});
|
||||
|
||||
std.mem.copyForwards(u8, out[idx..], &hashed);
|
||||
idx += hashed.len;
|
||||
|
||||
std.mem.writeInt(u32, hash[seed.len..][0..4], counter, .big);
|
||||
Hash.hash(&hash, out[idx..][0..Hash.digest_length], .{});
|
||||
idx += Hash.digest_length;
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
@ -1108,11 +1072,128 @@ pub const rsa = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// RFC 3447 8.2 RSASSA-PKCS1-v1_5
|
||||
pub const PKCS1v1_5Signature = struct {
|
||||
pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
|
||||
var result: [modulus_len]u8 = undefined;
|
||||
@memcpy(result[0..msg.len], msg);
|
||||
@memset(result[msg.len..], 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
pub const VerifyError = EncryptError || error{InvalidSignature};
|
||||
|
||||
pub fn verify(
|
||||
comptime modulus_len: usize,
|
||||
sig: [modulus_len]u8,
|
||||
msg: []const u8,
|
||||
public_key: PublicKey,
|
||||
comptime Hash: type,
|
||||
) VerifyError!void {
|
||||
try concatVerify(modulus_len, sig, &.{msg}, public_key, Hash);
|
||||
}
|
||||
|
||||
pub fn concatVerify(
|
||||
comptime modulus_len: usize,
|
||||
sig: [modulus_len]u8,
|
||||
msg: []const []const u8,
|
||||
public_key: PublicKey,
|
||||
comptime Hash: type,
|
||||
) VerifyError!void {
|
||||
const em_dec = try encrypt(modulus_len, sig, public_key);
|
||||
const em = try EMSA_PKCS1_V1_5_ENCODE(msg, modulus_len, Hash);
|
||||
if (!std.mem.eql(u8, &em_dec, &em)) return error.InvalidSignature;
|
||||
}
|
||||
|
||||
fn EMSA_PKCS1_V1_5_ENCODE(msg: []const []const u8, comptime emLen: usize, comptime Hash: type) VerifyError![emLen]u8 {
|
||||
comptime var em_index = emLen;
|
||||
var em: [emLen]u8 = undefined;
|
||||
|
||||
// 1. Apply the hash function to the message M to produce a hash value
|
||||
// H:
|
||||
//
|
||||
// H = Hash(M).
|
||||
//
|
||||
// If the hash function outputs "message too long," output "message
|
||||
// too long" and stop.
|
||||
var hasher: Hash = .init(.{});
|
||||
for (msg) |part| hasher.update(part);
|
||||
em_index -= Hash.digest_length;
|
||||
hasher.final(em[em_index..]);
|
||||
|
||||
// 2. Encode the algorithm ID for the hash function and the hash value
|
||||
// into an ASN.1 value of type DigestInfo (see Appendix A.2.4) with
|
||||
// the Distinguished Encoding Rules (DER), where the type DigestInfo
|
||||
// has the syntax
|
||||
//
|
||||
// DigestInfo ::= SEQUENCE {
|
||||
// digestAlgorithm AlgorithmIdentifier,
|
||||
// digest OCTET STRING
|
||||
// }
|
||||
//
|
||||
// The first field identifies the hash function and the second
|
||||
// contains the hash value. Let T be the DER encoding of the
|
||||
// DigestInfo value (see the notes below) and let tLen be the length
|
||||
// in octets of T.
|
||||
const hash_der: []const u8 = &switch (Hash) {
|
||||
crypto.hash.Sha1 => .{
|
||||
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
|
||||
0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
|
||||
},
|
||||
crypto.hash.sha2.Sha224 => .{
|
||||
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
|
||||
0x00, 0x04, 0x1c,
|
||||
},
|
||||
crypto.hash.sha2.Sha256 => .{
|
||||
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
|
||||
0x00, 0x04, 0x20,
|
||||
},
|
||||
crypto.hash.sha2.Sha384 => .{
|
||||
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
|
||||
0x00, 0x04, 0x30,
|
||||
},
|
||||
crypto.hash.sha2.Sha512 => .{
|
||||
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
|
||||
0x00, 0x04, 0x40,
|
||||
},
|
||||
else => @compileError("unreachable"),
|
||||
};
|
||||
em_index -= hash_der.len;
|
||||
@memcpy(em[em_index..][0..hash_der.len], hash_der);
|
||||
|
||||
// 3. If emLen < tLen + 11, output "intended encoded message length too
|
||||
// short" and stop.
|
||||
|
||||
// 4. Generate an octet string PS consisting of emLen - tLen - 3 octets
|
||||
// with hexadecimal value 0xff. The length of PS will be at least 8
|
||||
// octets.
|
||||
em_index -= 1;
|
||||
@memset(em[2..em_index], 0xff);
|
||||
|
||||
// 5. Concatenate PS, the DER encoding T, and other padding to form the
|
||||
// encoded message EM as
|
||||
//
|
||||
// EM = 0x00 || 0x01 || PS || 0x00 || T.
|
||||
em[em_index] = 0x00;
|
||||
em[1] = 0x01;
|
||||
em[0] = 0x00;
|
||||
|
||||
// 6. Output EM.
|
||||
return em;
|
||||
}
|
||||
};
|
||||
|
||||
pub const PublicKey = struct {
|
||||
n: Modulus,
|
||||
e: Fe,
|
||||
|
||||
pub fn fromBytes(pub_bytes: []const u8, modulus_bytes: []const u8) !PublicKey {
|
||||
pub const FromBytesError = error{CertificatePublicKeyInvalid};
|
||||
|
||||
pub fn fromBytes(pub_bytes: []const u8, modulus_bytes: []const u8) FromBytesError!PublicKey {
|
||||
// Reject modulus below 512 bits.
|
||||
// 512-bit RSA was factored in 1999, so this limit barely means anything,
|
||||
// but establish some limit now to ratchet in what we can.
|
||||
@ -1137,7 +1218,9 @@ pub const rsa = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn parseDer(pub_key: []const u8) !struct { modulus: []const u8, exponent: []const u8 } {
|
||||
pub const ParseDerError = der.Element.ParseError || error{CertificateFieldHasWrongDataType};
|
||||
|
||||
pub fn parseDer(pub_key: []const u8) ParseDerError!struct { modulus: []const u8, exponent: []const u8 } {
|
||||
const pub_key_seq = try der.Element.parse(pub_key, 0);
|
||||
if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;
|
||||
const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);
|
||||
@ -1156,7 +1239,9 @@ pub const rsa = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey) ![modulus_len]u8 {
|
||||
const EncryptError = error{MessageTooLong};
|
||||
|
||||
fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey) EncryptError![modulus_len]u8 {
|
||||
const m = Fe.fromBytes(public_key.n, &msg, .big) catch return error.MessageTooLong;
|
||||
const e = public_key.n.powPublic(m, public_key.e) catch unreachable;
|
||||
var res: [modulus_len]u8 = undefined;
|
||||
|
@ -91,24 +91,33 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||
s: Curve.scalar.CompressedScalar,
|
||||
|
||||
/// Create a Verifier for incremental verification of a signature.
|
||||
pub fn verifier(self: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {
|
||||
return Verifier.init(self, public_key);
|
||||
pub fn verifier(sig: Signature, public_key: PublicKey) Verifier.InitError!Verifier {
|
||||
return Verifier.init(sig, public_key);
|
||||
}
|
||||
|
||||
pub const VerifyError = Verifier.InitError || Verifier.VerifyError;
|
||||
|
||||
/// Verify the signature against a message and public key.
|
||||
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
||||
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
||||
pub fn verify(self: Signature, msg: []const u8, public_key: PublicKey) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void {
|
||||
var st = try Verifier.init(self, public_key);
|
||||
st.update(msg);
|
||||
return st.verify();
|
||||
pub fn verify(sig: Signature, msg: []const u8, public_key: PublicKey) VerifyError!void {
|
||||
try sig.concatVerify(&.{msg}, public_key);
|
||||
}
|
||||
|
||||
/// Verify the signature against a concatenated message and public key.
|
||||
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
||||
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
||||
pub fn concatVerify(sig: Signature, msg: []const []const u8, public_key: PublicKey) VerifyError!void {
|
||||
var st = try Verifier.init(sig, public_key);
|
||||
for (msg) |part| st.update(part);
|
||||
try st.verify();
|
||||
}
|
||||
|
||||
/// Return the raw signature (r, s) in big-endian format.
|
||||
pub fn toBytes(self: Signature) [encoded_length]u8 {
|
||||
pub fn toBytes(sig: Signature) [encoded_length]u8 {
|
||||
var bytes: [encoded_length]u8 = undefined;
|
||||
@memcpy(bytes[0 .. encoded_length / 2], &self.r);
|
||||
@memcpy(bytes[encoded_length / 2 ..], &self.s);
|
||||
@memcpy(bytes[0 .. encoded_length / 2], &sig.r);
|
||||
@memcpy(bytes[encoded_length / 2 ..], &sig.s);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@ -124,23 +133,23 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||
/// Encode the signature using the DER format.
|
||||
/// The maximum length of the DER encoding is der_encoded_length_max.
|
||||
/// The function returns a slice, that can be shorter than der_encoded_length_max.
|
||||
pub fn toDer(self: Signature, buf: *[der_encoded_length_max]u8) []u8 {
|
||||
pub fn toDer(sig: Signature, buf: *[der_encoded_length_max]u8) []u8 {
|
||||
var fb = io.fixedBufferStream(buf);
|
||||
const w = fb.writer();
|
||||
const r_len = @as(u8, @intCast(self.r.len + (self.r[0] >> 7)));
|
||||
const s_len = @as(u8, @intCast(self.s.len + (self.s[0] >> 7)));
|
||||
const r_len = @as(u8, @intCast(sig.r.len + (sig.r[0] >> 7)));
|
||||
const s_len = @as(u8, @intCast(sig.s.len + (sig.s[0] >> 7)));
|
||||
const seq_len = @as(u8, @intCast(2 + r_len + 2 + s_len));
|
||||
w.writeAll(&[_]u8{ 0x30, seq_len }) catch unreachable;
|
||||
w.writeAll(&[_]u8{ 0x02, r_len }) catch unreachable;
|
||||
if (self.r[0] >> 7 != 0) {
|
||||
if (sig.r[0] >> 7 != 0) {
|
||||
w.writeByte(0x00) catch unreachable;
|
||||
}
|
||||
w.writeAll(&self.r) catch unreachable;
|
||||
w.writeAll(&sig.r) catch unreachable;
|
||||
w.writeAll(&[_]u8{ 0x02, s_len }) catch unreachable;
|
||||
if (self.s[0] >> 7 != 0) {
|
||||
if (sig.s[0] >> 7 != 0) {
|
||||
w.writeByte(0x00) catch unreachable;
|
||||
}
|
||||
w.writeAll(&self.s) catch unreachable;
|
||||
w.writeAll(&sig.s) catch unreachable;
|
||||
return fb.getWritten();
|
||||
}
|
||||
|
||||
@ -236,7 +245,9 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||
s: Curve.scalar.Scalar,
|
||||
public_key: PublicKey,
|
||||
|
||||
fn init(sig: Signature, public_key: PublicKey) (IdentityElementError || NonCanonicalError)!Verifier {
|
||||
pub const InitError = IdentityElementError || NonCanonicalError;
|
||||
|
||||
fn init(sig: Signature, public_key: PublicKey) InitError!Verifier {
|
||||
const r = try Curve.scalar.Scalar.fromBytes(sig.r, .big);
|
||||
const s = try Curve.scalar.Scalar.fromBytes(sig.s, .big);
|
||||
if (r.isZero() or s.isZero()) return error.IdentityElement;
|
||||
@ -254,8 +265,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||
self.h.update(data);
|
||||
}
|
||||
|
||||
pub const VerifyError = IdentityElementError || NonCanonicalError ||
|
||||
SignatureVerificationError;
|
||||
|
||||
/// Verify that the signature is valid for the entire message.
|
||||
pub fn verify(self: *Verifier) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void {
|
||||
pub fn verify(self: *Verifier) VerifyError!void {
|
||||
const ht = Curve.scalar.encoded_length;
|
||||
const h_len = @max(Hash.digest_length, ht);
|
||||
var h: [h_len]u8 = [_]u8{0} ** h_len;
|
||||
|
@ -54,6 +54,8 @@ pub const close_notify_alert = [_]u8{
|
||||
};
|
||||
|
||||
pub const ProtocolVersion = enum(u16) {
|
||||
tls_1_0 = 0x0301,
|
||||
tls_1_1 = 0x0302,
|
||||
tls_1_2 = 0x0303,
|
||||
tls_1_3 = 0x0304,
|
||||
_,
|
||||
@ -69,14 +71,18 @@ pub const ContentType = enum(u8) {
|
||||
};
|
||||
|
||||
pub const HandshakeType = enum(u8) {
|
||||
hello_request = 0,
|
||||
client_hello = 1,
|
||||
server_hello = 2,
|
||||
new_session_ticket = 4,
|
||||
end_of_early_data = 5,
|
||||
encrypted_extensions = 8,
|
||||
certificate = 11,
|
||||
server_key_exchange = 12,
|
||||
certificate_request = 13,
|
||||
server_hello_done = 14,
|
||||
certificate_verify = 15,
|
||||
client_key_exchange = 16,
|
||||
finished = 20,
|
||||
key_update = 24,
|
||||
message_hash = 254,
|
||||
@ -198,36 +204,36 @@ pub const AlertDescription = enum(u8) {
|
||||
_,
|
||||
|
||||
pub fn toError(alert: AlertDescription) Error!void {
|
||||
return switch (alert) {
|
||||
switch (alert) {
|
||||
.close_notify => {}, // not an error
|
||||
.unexpected_message => error.TlsAlertUnexpectedMessage,
|
||||
.bad_record_mac => error.TlsAlertBadRecordMac,
|
||||
.record_overflow => error.TlsAlertRecordOverflow,
|
||||
.handshake_failure => error.TlsAlertHandshakeFailure,
|
||||
.bad_certificate => error.TlsAlertBadCertificate,
|
||||
.unsupported_certificate => error.TlsAlertUnsupportedCertificate,
|
||||
.certificate_revoked => error.TlsAlertCertificateRevoked,
|
||||
.certificate_expired => error.TlsAlertCertificateExpired,
|
||||
.certificate_unknown => error.TlsAlertCertificateUnknown,
|
||||
.illegal_parameter => error.TlsAlertIllegalParameter,
|
||||
.unknown_ca => error.TlsAlertUnknownCa,
|
||||
.access_denied => error.TlsAlertAccessDenied,
|
||||
.decode_error => error.TlsAlertDecodeError,
|
||||
.decrypt_error => error.TlsAlertDecryptError,
|
||||
.protocol_version => error.TlsAlertProtocolVersion,
|
||||
.insufficient_security => error.TlsAlertInsufficientSecurity,
|
||||
.internal_error => error.TlsAlertInternalError,
|
||||
.inappropriate_fallback => error.TlsAlertInappropriateFallback,
|
||||
.unexpected_message => return error.TlsAlertUnexpectedMessage,
|
||||
.bad_record_mac => return error.TlsAlertBadRecordMac,
|
||||
.record_overflow => return error.TlsAlertRecordOverflow,
|
||||
.handshake_failure => return error.TlsAlertHandshakeFailure,
|
||||
.bad_certificate => return error.TlsAlertBadCertificate,
|
||||
.unsupported_certificate => return error.TlsAlertUnsupportedCertificate,
|
||||
.certificate_revoked => return error.TlsAlertCertificateRevoked,
|
||||
.certificate_expired => return error.TlsAlertCertificateExpired,
|
||||
.certificate_unknown => return error.TlsAlertCertificateUnknown,
|
||||
.illegal_parameter => return error.TlsAlertIllegalParameter,
|
||||
.unknown_ca => return error.TlsAlertUnknownCa,
|
||||
.access_denied => return error.TlsAlertAccessDenied,
|
||||
.decode_error => return error.TlsAlertDecodeError,
|
||||
.decrypt_error => return error.TlsAlertDecryptError,
|
||||
.protocol_version => return error.TlsAlertProtocolVersion,
|
||||
.insufficient_security => return error.TlsAlertInsufficientSecurity,
|
||||
.internal_error => return error.TlsAlertInternalError,
|
||||
.inappropriate_fallback => return error.TlsAlertInappropriateFallback,
|
||||
.user_canceled => {}, // not an error
|
||||
.missing_extension => error.TlsAlertMissingExtension,
|
||||
.unsupported_extension => error.TlsAlertUnsupportedExtension,
|
||||
.unrecognized_name => error.TlsAlertUnrecognizedName,
|
||||
.bad_certificate_status_response => error.TlsAlertBadCertificateStatusResponse,
|
||||
.unknown_psk_identity => error.TlsAlertUnknownPskIdentity,
|
||||
.certificate_required => error.TlsAlertCertificateRequired,
|
||||
.no_application_protocol => error.TlsAlertNoApplicationProtocol,
|
||||
_ => error.TlsAlertUnknown,
|
||||
};
|
||||
.missing_extension => return error.TlsAlertMissingExtension,
|
||||
.unsupported_extension => return error.TlsAlertUnsupportedExtension,
|
||||
.unrecognized_name => return error.TlsAlertUnrecognizedName,
|
||||
.bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse,
|
||||
.unknown_psk_identity => return error.TlsAlertUnknownPskIdentity,
|
||||
.certificate_required => return error.TlsAlertCertificateRequired,
|
||||
.no_application_protocol => return error.TlsAlertNoApplicationProtocol,
|
||||
_ => return error.TlsAlertUnknown,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -286,6 +292,20 @@ pub const NamedGroup = enum(u16) {
|
||||
};
|
||||
|
||||
pub const CipherSuite = enum(u16) {
|
||||
RSA_WITH_AES_128_CBC_SHA = 0x002F,
|
||||
DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
|
||||
RSA_WITH_AES_256_CBC_SHA = 0x0035,
|
||||
DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
|
||||
RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
|
||||
RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
|
||||
DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
|
||||
DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
|
||||
RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
|
||||
RSA_WITH_AES_256_GCM_SHA384 = 0x009D,
|
||||
DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
|
||||
DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F,
|
||||
EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
|
||||
|
||||
AES_128_GCM_SHA256 = 0x1301,
|
||||
AES_256_GCM_SHA384 = 0x1302,
|
||||
CHACHA20_POLY1305_SHA256 = 0x1303,
|
||||
@ -293,7 +313,98 @@ pub const CipherSuite = enum(u16) {
|
||||
AES_128_CCM_8_SHA256 = 0x1305,
|
||||
AEGIS_256_SHA512 = 0x1306,
|
||||
AEGIS_128L_SHA256 = 0x1307,
|
||||
|
||||
ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
|
||||
ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
|
||||
ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
|
||||
ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
|
||||
ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
|
||||
ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
|
||||
ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
|
||||
ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
|
||||
ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
|
||||
ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
|
||||
ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
|
||||
ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
|
||||
|
||||
ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8,
|
||||
ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9,
|
||||
DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA,
|
||||
|
||||
_,
|
||||
|
||||
pub const With = enum {
|
||||
AES_128_CBC_SHA,
|
||||
AES_256_CBC_SHA,
|
||||
AES_128_CBC_SHA256,
|
||||
AES_256_CBC_SHA256,
|
||||
AES_256_CBC_SHA384,
|
||||
|
||||
AES_128_GCM_SHA256,
|
||||
AES_256_GCM_SHA384,
|
||||
|
||||
CHACHA20_POLY1305_SHA256,
|
||||
|
||||
AES_128_CCM_SHA256,
|
||||
AES_128_CCM_8_SHA256,
|
||||
|
||||
AEGIS_256_SHA512,
|
||||
AEGIS_128L_SHA256,
|
||||
};
|
||||
|
||||
pub fn with(cipher_suite: CipherSuite) With {
|
||||
return switch (cipher_suite) {
|
||||
.RSA_WITH_AES_128_CBC_SHA,
|
||||
.DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
.ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
.ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
=> .AES_128_CBC_SHA,
|
||||
.RSA_WITH_AES_256_CBC_SHA,
|
||||
.DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
.ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
.ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
=> .AES_256_CBC_SHA,
|
||||
.RSA_WITH_AES_128_CBC_SHA256,
|
||||
.DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
.ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
.ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
=> .AES_128_CBC_SHA256,
|
||||
.RSA_WITH_AES_256_CBC_SHA256,
|
||||
.DHE_RSA_WITH_AES_256_CBC_SHA256,
|
||||
=> .AES_256_CBC_SHA256,
|
||||
.ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||
.ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||||
=> .AES_256_CBC_SHA384,
|
||||
|
||||
.RSA_WITH_AES_128_GCM_SHA256,
|
||||
.DHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
.AES_128_GCM_SHA256,
|
||||
.ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
.ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
=> .AES_128_GCM_SHA256,
|
||||
.RSA_WITH_AES_256_GCM_SHA384,
|
||||
.DHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
.AES_256_GCM_SHA384,
|
||||
.ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
.ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
=> .AES_256_GCM_SHA384,
|
||||
|
||||
.CHACHA20_POLY1305_SHA256,
|
||||
.ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
.ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
.DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
=> .CHACHA20_POLY1305_SHA256,
|
||||
|
||||
.AES_128_CCM_SHA256 => .AES_128_CCM_SHA256,
|
||||
.AES_128_CCM_8_SHA256 => .AES_128_CCM_8_SHA256,
|
||||
|
||||
.AEGIS_256_SHA512 => .AEGIS_256_SHA512,
|
||||
.AEGIS_128L_SHA256 => .AEGIS_128L_SHA256,
|
||||
|
||||
.EMPTY_RENEGOTIATION_INFO_SCSV => unreachable,
|
||||
_ => unreachable,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const CertificateType = enum(u8) {
|
||||
@ -308,58 +419,108 @@ pub const KeyUpdateRequest = enum(u8) {
|
||||
_,
|
||||
};
|
||||
|
||||
pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type) type {
|
||||
pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
|
||||
return struct {
|
||||
pub const AEAD = AeadType;
|
||||
pub const Hash = HashType;
|
||||
pub const Hmac = crypto.auth.hmac.Hmac(Hash);
|
||||
pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
|
||||
pub const A = ApplicationCipherT(AeadType, HashType, explicit_iv_length);
|
||||
|
||||
handshake_secret: [Hkdf.prk_length]u8,
|
||||
master_secret: [Hkdf.prk_length]u8,
|
||||
client_handshake_key: [AEAD.key_length]u8,
|
||||
server_handshake_key: [AEAD.key_length]u8,
|
||||
client_finished_key: [Hmac.key_length]u8,
|
||||
server_finished_key: [Hmac.key_length]u8,
|
||||
client_handshake_iv: [AEAD.nonce_length]u8,
|
||||
server_handshake_iv: [AEAD.nonce_length]u8,
|
||||
transcript_hash: Hash,
|
||||
transcript_hash: A.Hash,
|
||||
version: union {
|
||||
tls_1_2: struct {
|
||||
server_verify_data: [12]u8,
|
||||
app_cipher: A.Tls_1_2,
|
||||
},
|
||||
tls_1_3: struct {
|
||||
handshake_secret: [A.Hkdf.prk_length]u8,
|
||||
master_secret: [A.Hkdf.prk_length]u8,
|
||||
client_handshake_key: [A.AEAD.key_length]u8,
|
||||
server_handshake_key: [A.AEAD.key_length]u8,
|
||||
client_finished_key: [A.Hmac.key_length]u8,
|
||||
server_finished_key: [A.Hmac.key_length]u8,
|
||||
client_handshake_iv: [A.AEAD.nonce_length]u8,
|
||||
server_handshake_iv: [A.AEAD.nonce_length]u8,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub const HandshakeCipher = union(enum) {
|
||||
AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256),
|
||||
AES_256_GCM_SHA384: HandshakeCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384),
|
||||
CHACHA20_POLY1305_SHA256: HandshakeCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256),
|
||||
AEGIS_256_SHA512: HandshakeCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512),
|
||||
AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256),
|
||||
AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8),
|
||||
AES_256_GCM_SHA384: HandshakeCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8),
|
||||
CHACHA20_POLY1305_SHA256: HandshakeCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0),
|
||||
AEGIS_256_SHA512: HandshakeCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0),
|
||||
AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
|
||||
};
|
||||
|
||||
pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type) type {
|
||||
return struct {
|
||||
pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
|
||||
return union {
|
||||
pub const AEAD = AeadType;
|
||||
pub const Hash = HashType;
|
||||
pub const Hmac = crypto.auth.hmac.Hmac(Hash);
|
||||
pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
|
||||
|
||||
client_secret: [Hash.digest_length]u8,
|
||||
server_secret: [Hash.digest_length]u8,
|
||||
client_key: [AEAD.key_length]u8,
|
||||
server_key: [AEAD.key_length]u8,
|
||||
client_iv: [AEAD.nonce_length]u8,
|
||||
server_iv: [AEAD.nonce_length]u8,
|
||||
pub const enc_key_length = AEAD.key_length;
|
||||
pub const fixed_iv_length = AEAD.nonce_length - explicit_iv_length;
|
||||
pub const record_iv_length = explicit_iv_length;
|
||||
pub const mac_length = AEAD.tag_length;
|
||||
pub const mac_key_length = Hmac.key_length_min;
|
||||
|
||||
tls_1_2: Tls_1_2,
|
||||
tls_1_3: Tls_1_3,
|
||||
|
||||
pub const Tls_1_2 = extern struct {
|
||||
client_write_MAC_key: [mac_key_length]u8,
|
||||
server_write_MAC_key: [mac_key_length]u8,
|
||||
client_write_key: [enc_key_length]u8,
|
||||
server_write_key: [enc_key_length]u8,
|
||||
client_write_IV: [fixed_iv_length]u8,
|
||||
server_write_IV: [fixed_iv_length]u8,
|
||||
// non-standard entropy
|
||||
client_salt: [record_iv_length]u8,
|
||||
};
|
||||
|
||||
pub const Tls_1_3 = struct {
|
||||
client_secret: [Hash.digest_length]u8,
|
||||
server_secret: [Hash.digest_length]u8,
|
||||
client_key: [AEAD.key_length]u8,
|
||||
server_key: [AEAD.key_length]u8,
|
||||
client_iv: [AEAD.nonce_length]u8,
|
||||
server_iv: [AEAD.nonce_length]u8,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// Encryption parameters for application traffic.
|
||||
pub const ApplicationCipher = union(enum) {
|
||||
AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256),
|
||||
AES_256_GCM_SHA384: ApplicationCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384),
|
||||
CHACHA20_POLY1305_SHA256: ApplicationCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256),
|
||||
AEGIS_256_SHA512: ApplicationCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512),
|
||||
AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256),
|
||||
AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8),
|
||||
AES_256_GCM_SHA384: ApplicationCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8),
|
||||
CHACHA20_POLY1305_SHA256: ApplicationCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0),
|
||||
AEGIS_256_SHA512: ApplicationCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0),
|
||||
AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
|
||||
};
|
||||
|
||||
pub fn hmacExpandLabel(
|
||||
comptime Hmac: type,
|
||||
secret: []const u8,
|
||||
label_then_seed: []const []const u8,
|
||||
comptime len: usize,
|
||||
) [len]u8 {
|
||||
const initial_hmac: Hmac = .init(secret);
|
||||
var a: [Hmac.mac_length]u8 = undefined;
|
||||
var result: [std.mem.alignForwardAnyAlign(usize, len, Hmac.mac_length)]u8 = undefined;
|
||||
var index: usize = 0;
|
||||
while (index < result.len) : (index += Hmac.mac_length) {
|
||||
var a_hmac = initial_hmac;
|
||||
if (index > 0) a_hmac.update(&a) else for (label_then_seed) |part| a_hmac.update(part);
|
||||
a_hmac.final(&a);
|
||||
|
||||
var result_hmac = initial_hmac;
|
||||
result_hmac.update(&a);
|
||||
for (label_then_seed) |part| result_hmac.update(part);
|
||||
result_hmac.final(result[index..][0..Hmac.mac_length]);
|
||||
}
|
||||
return result[0..len].*;
|
||||
}
|
||||
|
||||
pub fn hkdfExpandLabel(
|
||||
comptime Hkdf: type,
|
||||
key: [Hkdf.prk_length]u8,
|
||||
@ -418,19 +579,16 @@ pub inline fn enum_array(comptime E: type, comptime tags: []const E) [2 + @sizeO
|
||||
return array(2, result);
|
||||
}
|
||||
|
||||
pub inline fn int2(x: u16) [2]u8 {
|
||||
return .{
|
||||
@as(u8, @truncate(x >> 8)),
|
||||
@as(u8, @truncate(x)),
|
||||
};
|
||||
pub inline fn int2(int: u16) [2]u8 {
|
||||
var arr: [2]u8 = undefined;
|
||||
std.mem.writeInt(u16, &arr, int, .big);
|
||||
return arr;
|
||||
}
|
||||
|
||||
pub inline fn int3(x: u24) [3]u8 {
|
||||
return .{
|
||||
@as(u8, @truncate(x >> 16)),
|
||||
@as(u8, @truncate(x >> 8)),
|
||||
@as(u8, @truncate(x)),
|
||||
};
|
||||
pub inline fn int3(int: u24) [3]u8 {
|
||||
var arr: [3]u8 = undefined;
|
||||
std.mem.writeInt(u24, &arr, int, .big);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// An abstraction to ensure that protocol-parsing code does not perform an
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -172,7 +172,13 @@ pub const HeadersParser = struct {
|
||||
const data_avail = r.next_chunk_length;
|
||||
|
||||
if (skip) {
|
||||
try conn.fill();
|
||||
conn.fill() catch |err| switch (err) {
|
||||
error.EndOfStream => {
|
||||
r.done = true;
|
||||
return 0;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const nread = @min(conn.peek().len, data_avail);
|
||||
conn.drop(@intCast(nread));
|
||||
@ -196,7 +202,13 @@ pub const HeadersParser = struct {
|
||||
}
|
||||
},
|
||||
.chunk_data_suffix, .chunk_data_suffix_r, .chunk_head_size, .chunk_head_ext, .chunk_head_r => {
|
||||
try conn.fill();
|
||||
conn.fill() catch |err| switch (err) {
|
||||
error.EndOfStream => {
|
||||
r.done = true;
|
||||
return 0;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const i = r.findChunkedLen(conn.peek());
|
||||
conn.drop(@intCast(i));
|
||||
@ -226,7 +238,13 @@ pub const HeadersParser = struct {
|
||||
const out_avail = buffer.len - out_index;
|
||||
|
||||
if (skip) {
|
||||
try conn.fill();
|
||||
conn.fill() catch |err| switch (err) {
|
||||
error.EndOfStream => {
|
||||
r.done = true;
|
||||
return 0;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const nread = @min(conn.peek().len, data_avail);
|
||||
conn.drop(@intCast(nread));
|
||||
|
Loading…
Reference in New Issue
Block a user