authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-11-08 02:01:52-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-11-08 02:01:52-05:00
loge5f5229fd6f9d0fe684ab32cce8f2b18e02c115b
treec9fb5a5324d741042de3c581d8719bb2b27c889a
parentee9f00d673f2bccddc2751c328758a2820d2bb70
parent9373abf7f77c37094f9ba6ca68287d8a06ebafa0
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21872 from jacobly0/tlsv1.2

std.crypto.tls: implement TLSv1.2

8 files changed, 1814 insertions(+), 941 deletions(-)

lib/std/crypto/25519/ed25519.zig+17-10
...@@ -151,7 +151,9 @@ pub const Ed25519 = struct {...@@ -151,7 +151,9 @@ pub const Ed25519 = struct {
151 a: Curve,151 a: Curve,
152 expected_r: Curve,152 expected_r: Curve,
153153
154 fn init(sig: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {154 pub const InitError = NonCanonicalError || EncodingError || IdentityElementError;
155
156 fn init(sig: Signature, public_key: PublicKey) InitError!Verifier {
155 const r = sig.r;157 const r = sig.r;
156 const s = sig.s;158 const s = sig.s;
157 try Curve.scalar.rejectNonCanonical(s);159 try Curve.scalar.rejectNonCanonical(s);
...@@ -173,8 +175,11 @@ pub const Ed25519 = struct {...@@ -173,8 +175,11 @@ pub const Ed25519 = struct {
173 self.h.update(msg);175 self.h.update(msg);
174 }176 }
175177
178 pub const VerifyError = WeakPublicKeyError || IdentityElementError ||
179 SignatureVerificationError;
180
176 /// Verify that the signature is valid for the entire message.181 /// Verify that the signature is valid for the entire message.
177 pub fn verify(self: *Verifier) (SignatureVerificationError || WeakPublicKeyError || IdentityElementError)!void {182 pub fn verify(self: *Verifier) VerifyError!void {
178 var hram64: [Sha512.digest_length]u8 = undefined;183 var hram64: [Sha512.digest_length]u8 = undefined;
179 self.h.final(&hram64);184 self.h.final(&hram64);
180 const hram = Curve.scalar.reduce64(hram64);185 const hram = Curve.scalar.reduce64(hram64);
...@@ -197,10 +202,10 @@ pub const Ed25519 = struct {...@@ -197,10 +202,10 @@ pub const Ed25519 = struct {
197 s: CompressedScalar,202 s: CompressedScalar,
198203
199 /// Return the raw signature (r, s) in little-endian format.204 /// Return the raw signature (r, s) in little-endian format.
200 pub fn toBytes(self: Signature) [encoded_length]u8 {205 pub fn toBytes(sig: Signature) [encoded_length]u8 {
201 var bytes: [encoded_length]u8 = undefined;206 var bytes: [encoded_length]u8 = undefined;
202 bytes[0..Curve.encoded_length].* = self.r;207 bytes[0..Curve.encoded_length].* = sig.r;
203 bytes[Curve.encoded_length..].* = self.s;208 bytes[Curve.encoded_length..].* = sig.s;
204 return bytes;209 return bytes;
205 }210 }
206211
...@@ -214,17 +219,19 @@ pub const Ed25519 = struct {...@@ -214,17 +219,19 @@ pub const Ed25519 = struct {
214 }219 }
215220
216 /// Create a Verifier for incremental verification of a signature.221 /// Create a Verifier for incremental verification of a signature.
217 pub fn verifier(self: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {222 pub fn verifier(sig: Signature, public_key: PublicKey) Verifier.InitError!Verifier {
218 return Verifier.init(self, public_key);223 return Verifier.init(sig, public_key);
219 }224 }
220225
226 pub const VerifyError = Verifier.InitError || Verifier.VerifyError;
227
221 /// Verify the signature against a message and public key.228 /// Verify the signature against a message and public key.
222 /// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,229 /// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
223 /// or SignatureVerificationError if the signature is invalid for the given message and key.230 /// or SignatureVerificationError if the signature is invalid for the given message and key.
224 pub fn verify(self: Signature, msg: []const u8, public_key: PublicKey) (IdentityElementError || NonCanonicalError || SignatureVerificationError || EncodingError || WeakPublicKeyError)!void {231 pub fn verify(sig: Signature, msg: []const u8, public_key: PublicKey) VerifyError!void {
225 var st = try Verifier.init(self, public_key);232 var st = try sig.verifier(public_key);
226 st.update(msg);233 st.update(msg);
227 return st.verify();234 try st.verify();
228 }235 }
229 };236 };
230237
lib/std/crypto/Certificate.zig+227-142
...@@ -20,18 +20,18 @@ pub const Algorithm = enum {...@@ -20,18 +20,18 @@ pub const Algorithm = enum {
20 curveEd25519,20 curveEd25519,
2121
22 pub const map = std.StaticStringMap(Algorithm).initComptime(.{22 pub const map = std.StaticStringMap(Algorithm).initComptime(.{
23 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },23 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
24 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },24 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
25 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },25 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
26 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },26 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
27 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },27 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
28 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },28 .{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },
29 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },29 .{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
30 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },30 .{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
31 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },31 .{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
32 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },32 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
33 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },33 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
34 .{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 },34 .{ &.{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
35 });35 });
3636
37 pub fn Hash(comptime algorithm: Algorithm) type {37 pub fn Hash(comptime algorithm: Algorithm) type {
...@@ -49,13 +49,15 @@ pub const Algorithm = enum {...@@ -49,13 +49,15 @@ pub const Algorithm = enum {
4949
50pub const AlgorithmCategory = enum {50pub const AlgorithmCategory = enum {
51 rsaEncryption,51 rsaEncryption,
52 rsassa_pss,
52 X9_62_id_ecPublicKey,53 X9_62_id_ecPublicKey,
53 curveEd25519,54 curveEd25519,
5455
55 pub const map = std.StaticStringMap(AlgorithmCategory).initComptime(.{56 pub const map = std.StaticStringMap(AlgorithmCategory).initComptime(.{
56 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },57 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
57 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },58 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A }, .rsassa_pss },
58 .{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 },59 .{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },
60 .{ &.{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
59 });61 });
60};62};
6163
...@@ -74,18 +76,18 @@ pub const Attribute = enum {...@@ -74,18 +76,18 @@ pub const Attribute = enum {
74 domainComponent,76 domainComponent,
7577
76 pub const map = std.StaticStringMap(Attribute).initComptime(.{78 pub const map = std.StaticStringMap(Attribute).initComptime(.{
77 .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },79 .{ &.{ 0x55, 0x04, 0x03 }, .commonName },
78 .{ &[_]u8{ 0x55, 0x04, 0x05 }, .serialNumber },80 .{ &.{ 0x55, 0x04, 0x05 }, .serialNumber },
79 .{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },81 .{ &.{ 0x55, 0x04, 0x06 }, .countryName },
80 .{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },82 .{ &.{ 0x55, 0x04, 0x07 }, .localityName },
81 .{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },83 .{ &.{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
82 .{ &[_]u8{ 0x55, 0x04, 0x09 }, .streetAddress },84 .{ &.{ 0x55, 0x04, 0x09 }, .streetAddress },
83 .{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },85 .{ &.{ 0x55, 0x04, 0x0A }, .organizationName },
84 .{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },86 .{ &.{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
85 .{ &[_]u8{ 0x55, 0x04, 0x11 }, .postalCode },87 .{ &.{ 0x55, 0x04, 0x11 }, .postalCode },
86 .{ &[_]u8{ 0x55, 0x04, 0x61 }, .organizationIdentifier },88 .{ &.{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
87 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },89 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
88 .{ &[_]u8{ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }, .domainComponent },90 .{ &.{ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }, .domainComponent },
89 });91 });
90};92};
9193
...@@ -95,9 +97,9 @@ pub const NamedCurve = enum {...@@ -95,9 +97,9 @@ pub const NamedCurve = enum {
95 X9_62_prime256v1,97 X9_62_prime256v1,
9698
97 pub const map = std.StaticStringMap(NamedCurve).initComptime(.{99 pub const map = std.StaticStringMap(NamedCurve).initComptime(.{
98 .{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 },100 .{ &.{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 },
99 .{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 },101 .{ &.{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 },
100 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 },102 .{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 },
101 });103 });
102104
103 pub fn Curve(comptime curve: NamedCurve) type {105 pub fn Curve(comptime curve: NamedCurve) type {
...@@ -131,28 +133,28 @@ pub const ExtensionId = enum {...@@ -131,28 +133,28 @@ pub const ExtensionId = enum {
131 netscape_comment,133 netscape_comment,
132134
133 pub const map = std.StaticStringMap(ExtensionId).initComptime(.{135 pub const map = std.StaticStringMap(ExtensionId).initComptime(.{
134 .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },136 .{ &.{ 0x55, 0x04, 0x03 }, .commonName },
135 .{ &[_]u8{ 0x55, 0x1D, 0x01 }, .authority_key_identifier },137 .{ &.{ 0x55, 0x1D, 0x01 }, .authority_key_identifier },
136 .{ &[_]u8{ 0x55, 0x1D, 0x07 }, .subject_alt_name },138 .{ &.{ 0x55, 0x1D, 0x07 }, .subject_alt_name },
137 .{ &[_]u8{ 0x55, 0x1D, 0x0E }, .subject_key_identifier },139 .{ &.{ 0x55, 0x1D, 0x0E }, .subject_key_identifier },
138 .{ &[_]u8{ 0x55, 0x1D, 0x0F }, .key_usage },140 .{ &.{ 0x55, 0x1D, 0x0F }, .key_usage },
139 .{ &[_]u8{ 0x55, 0x1D, 0x0A }, .basic_constraints },141 .{ &.{ 0x55, 0x1D, 0x0A }, .basic_constraints },
140 .{ &[_]u8{ 0x55, 0x1D, 0x10 }, .private_key_usage_period },142 .{ &.{ 0x55, 0x1D, 0x10 }, .private_key_usage_period },
141 .{ &[_]u8{ 0x55, 0x1D, 0x11 }, .subject_alt_name },143 .{ &.{ 0x55, 0x1D, 0x11 }, .subject_alt_name },
142 .{ &[_]u8{ 0x55, 0x1D, 0x12 }, .issuer_alt_name },144 .{ &.{ 0x55, 0x1D, 0x12 }, .issuer_alt_name },
143 .{ &[_]u8{ 0x55, 0x1D, 0x13 }, .basic_constraints },145 .{ &.{ 0x55, 0x1D, 0x13 }, .basic_constraints },
144 .{ &[_]u8{ 0x55, 0x1D, 0x14 }, .crl_number },146 .{ &.{ 0x55, 0x1D, 0x14 }, .crl_number },
145 .{ &[_]u8{ 0x55, 0x1D, 0x1F }, .crl_distribution_points },147 .{ &.{ 0x55, 0x1D, 0x1F }, .crl_distribution_points },
146 .{ &[_]u8{ 0x55, 0x1D, 0x20 }, .certificate_policies },148 .{ &.{ 0x55, 0x1D, 0x20 }, .certificate_policies },
147 .{ &[_]u8{ 0x55, 0x1D, 0x23 }, .authority_key_identifier },149 .{ &.{ 0x55, 0x1D, 0x23 }, .authority_key_identifier },
148 .{ &[_]u8{ 0x55, 0x1D, 0x25 }, .ext_key_usage },150 .{ &.{ 0x55, 0x1D, 0x25 }, .ext_key_usage },
149 .{ &[_]u8{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01 }, .msCertsrvCAVersion },151 .{ &.{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01 }, .msCertsrvCAVersion },
150 .{ &[_]u8{ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }, .info_access },152 .{ &.{ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }, .info_access },
151 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF6, 0x7D, 0x07, 0x41, 0x00 }, .entrustVersInfo },153 .{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF6, 0x7D, 0x07, 0x41, 0x00 }, .entrustVersInfo },
152 .{ &[_]u8{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02 }, .enroll_certtype },154 .{ &.{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02 }, .enroll_certtype },
153 .{ &[_]u8{ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c }, .pe_logotype },155 .{ &.{ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c }, .pe_logotype },
154 .{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 }, .netscape_cert_type },156 .{ &.{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 }, .netscape_cert_type },
155 .{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d }, .netscape_comment },157 .{ &.{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d }, .netscape_comment },
156 });158 });
157};159};
158160
...@@ -185,6 +187,7 @@ pub const Parsed = struct {...@@ -185,6 +187,7 @@ pub const Parsed = struct {
185187
186 pub const PubKeyAlgo = union(AlgorithmCategory) {188 pub const PubKeyAlgo = union(AlgorithmCategory) {
187 rsaEncryption: void,189 rsaEncryption: void,
190 rsassa_pss: void,
188 X9_62_id_ecPublicKey: NamedCurve,191 X9_62_id_ecPublicKey: NamedCurve,
189 curveEd25519: void,192 curveEd25519: void,
190 };193 };
...@@ -386,7 +389,7 @@ test "Parsed.checkHostName" {...@@ -386,7 +389,7 @@ test "Parsed.checkHostName" {
386 try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.Ziglang.ORG"));389 try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.Ziglang.ORG"));
387}390}
388391
389pub const ParseError = der.Element.ParseElementError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;392pub const ParseError = der.Element.ParseError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
390393
391pub fn parse(cert: Certificate) ParseError!Parsed {394pub fn parse(cert: Certificate) ParseError!Parsed {
392 const cert_bytes = cert.buffer;395 const cert_bytes = cert.buffer;
...@@ -413,13 +416,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {...@@ -413,13 +416,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {
413 const pub_key_info = try der.Element.parse(cert_bytes, subject.slice.end);416 const pub_key_info = try der.Element.parse(cert_bytes, subject.slice.end);
414 const pub_key_signature_algorithm = try der.Element.parse(cert_bytes, pub_key_info.slice.start);417 const pub_key_signature_algorithm = try der.Element.parse(cert_bytes, pub_key_info.slice.start);
415 const pub_key_algo_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.start);418 const pub_key_algo_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.start);
416 const pub_key_algo_tag = try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem);419 const pub_key_algo: Parsed.PubKeyAlgo = switch (try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem)) {
417 var pub_key_algo: Parsed.PubKeyAlgo = undefined;420 inline else => |tag| @unionInit(Parsed.PubKeyAlgo, @tagName(tag), {}),
418 switch (pub_key_algo_tag) {421 .X9_62_id_ecPublicKey => pub_key_algo: {
419 .rsaEncryption => {
420 pub_key_algo = .{ .rsaEncryption = {} };
421 },
422 .X9_62_id_ecPublicKey => {
423 // RFC 5480 Section 2.1.1.1 Named Curve422 // RFC 5480 Section 2.1.1.1 Named Curve
424 // ECParameters ::= CHOICE {423 // ECParameters ::= CHOICE {
425 // namedCurve OBJECT IDENTIFIER424 // namedCurve OBJECT IDENTIFIER
...@@ -428,12 +427,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {...@@ -428,12 +427,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {
428 // }427 // }
429 const params_elem = try der.Element.parse(cert_bytes, pub_key_algo_elem.slice.end);428 const params_elem = try der.Element.parse(cert_bytes, pub_key_algo_elem.slice.end);
430 const named_curve = try parseNamedCurve(cert_bytes, params_elem);429 const named_curve = try parseNamedCurve(cert_bytes, params_elem);
431 pub_key_algo = .{ .X9_62_id_ecPublicKey = named_curve };430 break :pub_key_algo .{ .X9_62_id_ecPublicKey = named_curve };
432 },
433 .curveEd25519 => {
434 pub_key_algo = .{ .curveEd25519 = {} };
435 },431 },
436 }432 };
437 const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end);433 const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end);
438 const pub_key = try parseBitString(cert, pub_key_elem);434 const pub_key = try parseBitString(cert, pub_key_elem);
439435
...@@ -731,7 +727,7 @@ pub fn parseVersion(bytes: []const u8, version_elem: der.Element) ParseVersionEr...@@ -731,7 +727,7 @@ pub fn parseVersion(bytes: []const u8, version_elem: der.Element) ParseVersionEr
731727
732fn verifyRsa(728fn verifyRsa(
733 comptime Hash: type,729 comptime Hash: type,
734 message: []const u8,730 msg: []const u8,
735 sig: []const u8,731 sig: []const u8,
736 pub_key_algo: Parsed.PubKeyAlgo,732 pub_key_algo: Parsed.PubKeyAlgo,
737 pub_key: []const u8,733 pub_key: []const u8,
...@@ -743,59 +739,14 @@ fn verifyRsa(...@@ -743,59 +739,14 @@ fn verifyRsa(
743 if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;739 if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;
744 if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;740 if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;
745741
746 const hash_der = switch (Hash) {
747 crypto.hash.Sha1 => [_]u8{
748 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
749 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
750 },
751 crypto.hash.sha2.Sha224 => [_]u8{
752 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
753 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
754 0x00, 0x04, 0x1c,
755 },
756 crypto.hash.sha2.Sha256 => [_]u8{
757 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
758 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
759 0x00, 0x04, 0x20,
760 },
761 crypto.hash.sha2.Sha384 => [_]u8{
762 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
763 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
764 0x00, 0x04, 0x30,
765 },
766 crypto.hash.sha2.Sha512 => [_]u8{
767 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
768 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
769 0x00, 0x04, 0x40,
770 },
771 else => @compileError("unreachable"),
772 };
773
774 var msg_hashed: [Hash.digest_length]u8 = undefined;
775 Hash.hash(message, &msg_hashed, .{});
776
777 switch (modulus.len) {742 switch (modulus.len) {
778 inline 128, 256, 384, 512 => |modulus_len| {743 inline 128, 256, 384, 512 => |modulus_len| {
779 const ps_len = modulus_len - (hash_der.len + msg_hashed.len) - 3;744 const public_key = rsa.PublicKey.fromBytes(exponent, modulus) catch
780 const em: [modulus_len]u8 =745 return error.CertificateSignatureInvalid;
781 [2]u8{ 0, 1 } ++746 rsa.PKCS1v1_5Signature.verify(modulus_len, sig[0..modulus_len].*, msg, public_key, Hash) catch
782 ([1]u8{0xff} ** ps_len) ++
783 [1]u8{0} ++
784 hash_der ++
785 msg_hashed;
786
787 const public_key = rsa.PublicKey.fromBytes(exponent, modulus) catch return error.CertificateSignatureInvalid;
788 const em_dec = rsa.encrypt(modulus_len, sig[0..modulus_len].*, public_key) catch |err| switch (err) {
789 error.MessageTooLong => unreachable,
790 };
791
792 if (!mem.eql(u8, &em, &em_dec)) {
793 return error.CertificateSignatureInvalid;747 return error.CertificateSignatureInvalid;
794 }
795 },
796 else => {
797 return error.CertificateSignatureUnsupportedBitCount;
798 },748 },
749 else => return error.CertificateSignatureUnsupportedBitCount,
799 }750 }
800}751}
801752
...@@ -908,9 +859,9 @@ pub const der = struct {...@@ -908,9 +859,9 @@ pub const der = struct {
908 pub const empty: Slice = .{ .start = 0, .end = 0 };859 pub const empty: Slice = .{ .start = 0, .end = 0 };
909 };860 };
910861
911 pub const ParseElementError = error{CertificateFieldHasInvalidLength};862 pub const ParseError = error{CertificateFieldHasInvalidLength};
912863
913 pub fn parse(bytes: []const u8, index: u32) ParseElementError!Element {864 pub fn parse(bytes: []const u8, index: u32) Element.ParseError!Element {
914 var i = index;865 var i = index;
915 const identifier = @as(Identifier, @bitCast(bytes[i]));866 const identifier = @as(Identifier, @bitCast(bytes[i]));
916 i += 1;867 i += 1;
...@@ -958,21 +909,41 @@ pub const rsa = struct {...@@ -958,21 +909,41 @@ pub const rsa = struct {
958 const Modulus = std.crypto.ff.Modulus(max_modulus_bits);909 const Modulus = std.crypto.ff.Modulus(max_modulus_bits);
959 const Fe = Modulus.Fe;910 const Fe = Modulus.Fe;
960911
912 /// RFC 3447 8.1 RSASSA-PSS
961 pub const PSSSignature = struct {913 pub const PSSSignature = struct {
962 pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {914 pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
963 var result = [1]u8{0} ** modulus_len;915 var result: [modulus_len]u8 = undefined;
964 std.mem.copyForwards(u8, &result, msg);916 @memcpy(result[0..msg.len], msg);
917 @memset(result[msg.len..], 0);
965 return result;918 return result;
966 }919 }
967920
968 pub fn verify(comptime modulus_len: usize, sig: [modulus_len]u8, msg: []const u8, public_key: PublicKey, comptime Hash: type) !void {921 pub const VerifyError = EncryptError || error{InvalidSignature};
922
923 pub fn verify(
924 comptime modulus_len: usize,
925 sig: [modulus_len]u8,
926 msg: []const u8,
927 public_key: PublicKey,
928 comptime Hash: type,
929 ) VerifyError!void {
930 try concatVerify(modulus_len, sig, &.{msg}, public_key, Hash);
931 }
932
933 pub fn concatVerify(
934 comptime modulus_len: usize,
935 sig: [modulus_len]u8,
936 msg: []const []const u8,
937 public_key: PublicKey,
938 comptime Hash: type,
939 ) VerifyError!void {
969 const mod_bits = public_key.n.bits();940 const mod_bits = public_key.n.bits();
970 const em_dec = try encrypt(modulus_len, sig, public_key);941 const em_dec = try encrypt(modulus_len, sig, public_key);
971942
972 EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash) catch unreachable;943 try EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash);
973 }944 }
974945
975 fn EMSA_PSS_VERIFY(msg: []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type) !void {946 fn EMSA_PSS_VERIFY(msg: []const []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type) VerifyError!void {
976 // 1. If the length of M is greater than the input limitation for947 // 1. If the length of M is greater than the input limitation for
977 // the hash function (2^61 - 1 octets for SHA-1), output948 // the hash function (2^61 - 1 octets for SHA-1), output
978 // "inconsistent" and stop.949 // "inconsistent" and stop.
...@@ -986,7 +957,11 @@ pub const rsa = struct {...@@ -986,7 +957,11 @@ pub const rsa = struct {
986957
987 // 2. Let mHash = Hash(M), an octet string of length hLen.958 // 2. Let mHash = Hash(M), an octet string of length hLen.
988 var mHash: [Hash.digest_length]u8 = undefined;959 var mHash: [Hash.digest_length]u8 = undefined;
989 Hash.hash(msg, &mHash, .{});960 {
961 var hasher: Hash = .init(.{});
962 for (msg) |part| hasher.update(part);
963 hasher.final(&mHash);
964 }
990965
991 // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.966 // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
992 if (emLen < Hash.digest_length + sLen + 2) {967 if (emLen < Hash.digest_length + sLen + 2) {
...@@ -1082,25 +1057,14 @@ pub const rsa = struct {...@@ -1082,25 +1057,14 @@ pub const rsa = struct {
1082 }1057 }
10831058
1084 fn MGF1(comptime Hash: type, out: []u8, seed: *const [Hash.digest_length]u8, len: usize) ![]u8 {1059 fn MGF1(comptime Hash: type, out: []u8, seed: *const [Hash.digest_length]u8, len: usize) ![]u8 {
1085 var counter: usize = 0;1060 var counter: u32 = 0;
1086 var idx: usize = 0;1061 var idx: usize = 0;
1087 var c: [4]u8 = undefined;1062 var hash = seed.* ++ @as([4]u8, undefined);
1088 var hash: [Hash.digest_length + c.len]u8 = undefined;
1089 @memcpy(hash[0..Hash.digest_length], seed);
1090 var hashed: [Hash.digest_length]u8 = undefined;
10911063
1092 while (idx < len) {1064 while (idx < len) {
1093 c[0] = @as(u8, @intCast((counter >> 24) & 0xFF));1065 std.mem.writeInt(u32, hash[seed.len..][0..4], counter, .big);
1094 c[1] = @as(u8, @intCast((counter >> 16) & 0xFF));1066 Hash.hash(&hash, out[idx..][0..Hash.digest_length], .{});
1095 c[2] = @as(u8, @intCast((counter >> 8) & 0xFF));1067 idx += Hash.digest_length;
1096 c[3] = @as(u8, @intCast(counter & 0xFF));
1097
1098 std.mem.copyForwards(u8, hash[seed.len..], &c);
1099 Hash.hash(&hash, &hashed, .{});
1100
1101 std.mem.copyForwards(u8, out[idx..], &hashed);
1102 idx += hashed.len;
1103
1104 counter += 1;1068 counter += 1;
1105 }1069 }
11061070
...@@ -1108,11 +1072,128 @@ pub const rsa = struct {...@@ -1108,11 +1072,128 @@ pub const rsa = struct {
1108 }1072 }
1109 };1073 };
11101074
1075 /// RFC 3447 8.2 RSASSA-PKCS1-v1_5
1076 pub const PKCS1v1_5Signature = struct {
1077 pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
1078 var result: [modulus_len]u8 = undefined;
1079 @memcpy(result[0..msg.len], msg);
1080 @memset(result[msg.len..], 0);
1081 return result;
1082 }
1083
1084 pub const VerifyError = EncryptError || error{InvalidSignature};
1085
1086 pub fn verify(
1087 comptime modulus_len: usize,
1088 sig: [modulus_len]u8,
1089 msg: []const u8,
1090 public_key: PublicKey,
1091 comptime Hash: type,
1092 ) VerifyError!void {
1093 try concatVerify(modulus_len, sig, &.{msg}, public_key, Hash);
1094 }
1095
1096 pub fn concatVerify(
1097 comptime modulus_len: usize,
1098 sig: [modulus_len]u8,
1099 msg: []const []const u8,
1100 public_key: PublicKey,
1101 comptime Hash: type,
1102 ) VerifyError!void {
1103 const em_dec = try encrypt(modulus_len, sig, public_key);
1104 const em = try EMSA_PKCS1_V1_5_ENCODE(msg, modulus_len, Hash);
1105 if (!std.mem.eql(u8, &em_dec, &em)) return error.InvalidSignature;
1106 }
1107
1108 fn EMSA_PKCS1_V1_5_ENCODE(msg: []const []const u8, comptime emLen: usize, comptime Hash: type) VerifyError![emLen]u8 {
1109 comptime var em_index = emLen;
1110 var em: [emLen]u8 = undefined;
1111
1112 // 1. Apply the hash function to the message M to produce a hash value
1113 // H:
1114 //
1115 // H = Hash(M).
1116 //
1117 // If the hash function outputs "message too long," output "message
1118 // too long" and stop.
1119 var hasher: Hash = .init(.{});
1120 for (msg) |part| hasher.update(part);
1121 em_index -= Hash.digest_length;
1122 hasher.final(em[em_index..]);
1123
1124 // 2. Encode the algorithm ID for the hash function and the hash value
1125 // into an ASN.1 value of type DigestInfo (see Appendix A.2.4) with
1126 // the Distinguished Encoding Rules (DER), where the type DigestInfo
1127 // has the syntax
1128 //
1129 // DigestInfo ::= SEQUENCE {
1130 // digestAlgorithm AlgorithmIdentifier,
1131 // digest OCTET STRING
1132 // }
1133 //
1134 // The first field identifies the hash function and the second
1135 // contains the hash value. Let T be the DER encoding of the
1136 // DigestInfo value (see the notes below) and let tLen be the length
1137 // in octets of T.
1138 const hash_der: []const u8 = &switch (Hash) {
1139 crypto.hash.Sha1 => .{
1140 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
1141 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
1142 },
1143 crypto.hash.sha2.Sha224 => .{
1144 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
1145 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
1146 0x00, 0x04, 0x1c,
1147 },
1148 crypto.hash.sha2.Sha256 => .{
1149 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
1150 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
1151 0x00, 0x04, 0x20,
1152 },
1153 crypto.hash.sha2.Sha384 => .{
1154 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
1155 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
1156 0x00, 0x04, 0x30,
1157 },
1158 crypto.hash.sha2.Sha512 => .{
1159 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
1160 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
1161 0x00, 0x04, 0x40,
1162 },
1163 else => @compileError("unreachable"),
1164 };
1165 em_index -= hash_der.len;
1166 @memcpy(em[em_index..][0..hash_der.len], hash_der);
1167
1168 // 3. If emLen < tLen + 11, output "intended encoded message length too
1169 // short" and stop.
1170
1171 // 4. Generate an octet string PS consisting of emLen - tLen - 3 octets
1172 // with hexadecimal value 0xff. The length of PS will be at least 8
1173 // octets.
1174 em_index -= 1;
1175 @memset(em[2..em_index], 0xff);
1176
1177 // 5. Concatenate PS, the DER encoding T, and other padding to form the
1178 // encoded message EM as
1179 //
1180 // EM = 0x00 || 0x01 || PS || 0x00 || T.
1181 em[em_index] = 0x00;
1182 em[1] = 0x01;
1183 em[0] = 0x00;
1184
1185 // 6. Output EM.
1186 return em;
1187 }
1188 };
1189
1111 pub const PublicKey = struct {1190 pub const PublicKey = struct {
1112 n: Modulus,1191 n: Modulus,
1113 e: Fe,1192 e: Fe,
11141193
1115 pub fn fromBytes(pub_bytes: []const u8, modulus_bytes: []const u8) !PublicKey {1194 pub const FromBytesError = error{CertificatePublicKeyInvalid};
1195
1196 pub fn fromBytes(pub_bytes: []const u8, modulus_bytes: []const u8) FromBytesError!PublicKey {
1116 // Reject modulus below 512 bits.1197 // Reject modulus below 512 bits.
1117 // 512-bit RSA was factored in 1999, so this limit barely means anything,1198 // 512-bit RSA was factored in 1999, so this limit barely means anything,
1118 // but establish some limit now to ratchet in what we can.1199 // but establish some limit now to ratchet in what we can.
...@@ -1137,7 +1218,9 @@ pub const rsa = struct {...@@ -1137,7 +1218,9 @@ pub const rsa = struct {
1137 };1218 };
1138 }1219 }
11391220
1140 pub fn parseDer(pub_key: []const u8) !struct { modulus: []const u8, exponent: []const u8 } {1221 pub const ParseDerError = der.Element.ParseError || error{CertificateFieldHasWrongDataType};
1222
1223 pub fn parseDer(pub_key: []const u8) ParseDerError!struct { modulus: []const u8, exponent: []const u8 } {
1141 const pub_key_seq = try der.Element.parse(pub_key, 0);1224 const pub_key_seq = try der.Element.parse(pub_key, 0);
1142 if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;1225 if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;
1143 const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);1226 const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);
...@@ -1156,7 +1239,9 @@ pub const rsa = struct {...@@ -1156,7 +1239,9 @@ pub const rsa = struct {
1156 }1239 }
1157 };1240 };
11581241
1159 fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey) ![modulus_len]u8 {1242 const EncryptError = error{MessageTooLong};
1243
1244 fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey) EncryptError![modulus_len]u8 {
1160 const m = Fe.fromBytes(public_key.n, &msg, .big) catch return error.MessageTooLong;1245 const m = Fe.fromBytes(public_key.n, &msg, .big) catch return error.MessageTooLong;
1161 const e = public_key.n.powPublic(m, public_key.e) catch unreachable;1246 const e = public_key.n.powPublic(m, public_key.e) catch unreachable;
1162 var res: [modulus_len]u8 = undefined;1247 var res: [modulus_len]u8 = undefined;
lib/std/crypto/ecdsa.zig+24-17
...@@ -91,24 +91,26 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -91,24 +91,26 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
91 s: Curve.scalar.CompressedScalar,91 s: Curve.scalar.CompressedScalar,
9292
93 /// Create a Verifier for incremental verification of a signature.93 /// Create a Verifier for incremental verification of a signature.
94 pub fn verifier(self: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {94 pub fn verifier(sig: Signature, public_key: PublicKey) Verifier.InitError!Verifier {
95 return Verifier.init(self, public_key);95 return Verifier.init(sig, public_key);
96 }96 }
9797
98 pub const VerifyError = Verifier.InitError || Verifier.VerifyError;
99
98 /// Verify the signature against a message and public key.100 /// Verify the signature against a message and public key.
99 /// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,101 /// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
100 /// or SignatureVerificationError if the signature is invalid for the given message and key.102 /// or SignatureVerificationError if the signature is invalid for the given message and key.
101 pub fn verify(self: Signature, msg: []const u8, public_key: PublicKey) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void {103 pub fn verify(sig: Signature, msg: []const u8, public_key: PublicKey) VerifyError!void {
102 var st = try Verifier.init(self, public_key);104 var st = try sig.verifier(public_key);
103 st.update(msg);105 st.update(msg);
104 return st.verify();106 try st.verify();
105 }107 }
106108
107 /// Return the raw signature (r, s) in big-endian format.109 /// Return the raw signature (r, s) in big-endian format.
108 pub fn toBytes(self: Signature) [encoded_length]u8 {110 pub fn toBytes(sig: Signature) [encoded_length]u8 {
109 var bytes: [encoded_length]u8 = undefined;111 var bytes: [encoded_length]u8 = undefined;
110 @memcpy(bytes[0 .. encoded_length / 2], &self.r);112 @memcpy(bytes[0 .. encoded_length / 2], &sig.r);
111 @memcpy(bytes[encoded_length / 2 ..], &self.s);113 @memcpy(bytes[encoded_length / 2 ..], &sig.s);
112 return bytes;114 return bytes;
113 }115 }
114116
...@@ -124,23 +126,23 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -124,23 +126,23 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
124 /// Encode the signature using the DER format.126 /// Encode the signature using the DER format.
125 /// The maximum length of the DER encoding is der_encoded_length_max.127 /// The maximum length of the DER encoding is der_encoded_length_max.
126 /// The function returns a slice, that can be shorter than der_encoded_length_max.128 /// The function returns a slice, that can be shorter than der_encoded_length_max.
127 pub fn toDer(self: Signature, buf: *[der_encoded_length_max]u8) []u8 {129 pub fn toDer(sig: Signature, buf: *[der_encoded_length_max]u8) []u8 {
128 var fb = io.fixedBufferStream(buf);130 var fb = io.fixedBufferStream(buf);
129 const w = fb.writer();131 const w = fb.writer();
130 const r_len = @as(u8, @intCast(self.r.len + (self.r[0] >> 7)));132 const r_len = @as(u8, @intCast(sig.r.len + (sig.r[0] >> 7)));
131 const s_len = @as(u8, @intCast(self.s.len + (self.s[0] >> 7)));133 const s_len = @as(u8, @intCast(sig.s.len + (sig.s[0] >> 7)));
132 const seq_len = @as(u8, @intCast(2 + r_len + 2 + s_len));134 const seq_len = @as(u8, @intCast(2 + r_len + 2 + s_len));
133 w.writeAll(&[_]u8{ 0x30, seq_len }) catch unreachable;135 w.writeAll(&[_]u8{ 0x30, seq_len }) catch unreachable;
134 w.writeAll(&[_]u8{ 0x02, r_len }) catch unreachable;136 w.writeAll(&[_]u8{ 0x02, r_len }) catch unreachable;
135 if (self.r[0] >> 7 != 0) {137 if (sig.r[0] >> 7 != 0) {
136 w.writeByte(0x00) catch unreachable;138 w.writeByte(0x00) catch unreachable;
137 }139 }
138 w.writeAll(&self.r) catch unreachable;140 w.writeAll(&sig.r) catch unreachable;
139 w.writeAll(&[_]u8{ 0x02, s_len }) catch unreachable;141 w.writeAll(&[_]u8{ 0x02, s_len }) catch unreachable;
140 if (self.s[0] >> 7 != 0) {142 if (sig.s[0] >> 7 != 0) {
141 w.writeByte(0x00) catch unreachable;143 w.writeByte(0x00) catch unreachable;
142 }144 }
143 w.writeAll(&self.s) catch unreachable;145 w.writeAll(&sig.s) catch unreachable;
144 return fb.getWritten();146 return fb.getWritten();
145 }147 }
146148
...@@ -236,7 +238,9 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -236,7 +238,9 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
236 s: Curve.scalar.Scalar,238 s: Curve.scalar.Scalar,
237 public_key: PublicKey,239 public_key: PublicKey,
238240
239 fn init(sig: Signature, public_key: PublicKey) (IdentityElementError || NonCanonicalError)!Verifier {241 pub const InitError = IdentityElementError || NonCanonicalError;
242
243 fn init(sig: Signature, public_key: PublicKey) InitError!Verifier {
240 const r = try Curve.scalar.Scalar.fromBytes(sig.r, .big);244 const r = try Curve.scalar.Scalar.fromBytes(sig.r, .big);
241 const s = try Curve.scalar.Scalar.fromBytes(sig.s, .big);245 const s = try Curve.scalar.Scalar.fromBytes(sig.s, .big);
242 if (r.isZero() or s.isZero()) return error.IdentityElement;246 if (r.isZero() or s.isZero()) return error.IdentityElement;
...@@ -254,8 +258,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -254,8 +258,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
254 self.h.update(data);258 self.h.update(data);
255 }259 }
256260
261 pub const VerifyError = IdentityElementError || NonCanonicalError ||
262 SignatureVerificationError;
263
257 /// Verify that the signature is valid for the entire message.264 /// Verify that the signature is valid for the entire message.
258 pub fn verify(self: *Verifier) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void {265 pub fn verify(self: *Verifier) VerifyError!void {
259 const ht = Curve.scalar.encoded_length;266 const ht = Curve.scalar.encoded_length;
260 const h_len = @max(Hash.digest_length, ht);267 const h_len = @max(Hash.digest_length, ht);
261 var h: [h_len]u8 = [_]u8{0} ** h_len;268 var h: [h_len]u8 = [_]u8{0} ** h_len;
lib/std/crypto/tls.zig+278-89
...@@ -54,6 +54,8 @@ pub const close_notify_alert = [_]u8{...@@ -54,6 +54,8 @@ pub const close_notify_alert = [_]u8{
54};54};
5555
56pub const ProtocolVersion = enum(u16) {56pub const ProtocolVersion = enum(u16) {
57 tls_1_0 = 0x0301,
58 tls_1_1 = 0x0302,
57 tls_1_2 = 0x0303,59 tls_1_2 = 0x0303,
58 tls_1_3 = 0x0304,60 tls_1_3 = 0x0304,
59 _,61 _,
...@@ -69,14 +71,18 @@ pub const ContentType = enum(u8) {...@@ -69,14 +71,18 @@ pub const ContentType = enum(u8) {
69};71};
7072
71pub const HandshakeType = enum(u8) {73pub const HandshakeType = enum(u8) {
74 hello_request = 0,
72 client_hello = 1,75 client_hello = 1,
73 server_hello = 2,76 server_hello = 2,
74 new_session_ticket = 4,77 new_session_ticket = 4,
75 end_of_early_data = 5,78 end_of_early_data = 5,
76 encrypted_extensions = 8,79 encrypted_extensions = 8,
77 certificate = 11,80 certificate = 11,
81 server_key_exchange = 12,
78 certificate_request = 13,82 certificate_request = 13,
83 server_hello_done = 14,
79 certificate_verify = 15,84 certificate_verify = 15,
85 client_key_exchange = 16,
80 finished = 20,86 finished = 20,
81 key_update = 24,87 key_update = 24,
82 message_hash = 254,88 message_hash = 254,
...@@ -198,36 +204,36 @@ pub const AlertDescription = enum(u8) {...@@ -198,36 +204,36 @@ pub const AlertDescription = enum(u8) {
198 _,204 _,
199205
200 pub fn toError(alert: AlertDescription) Error!void {206 pub fn toError(alert: AlertDescription) Error!void {
201 return switch (alert) {207 switch (alert) {
202 .close_notify => {}, // not an error208 .close_notify => {}, // not an error
203 .unexpected_message => error.TlsAlertUnexpectedMessage,209 .unexpected_message => return error.TlsAlertUnexpectedMessage,
204 .bad_record_mac => error.TlsAlertBadRecordMac,210 .bad_record_mac => return error.TlsAlertBadRecordMac,
205 .record_overflow => error.TlsAlertRecordOverflow,211 .record_overflow => return error.TlsAlertRecordOverflow,
206 .handshake_failure => error.TlsAlertHandshakeFailure,212 .handshake_failure => return error.TlsAlertHandshakeFailure,
207 .bad_certificate => error.TlsAlertBadCertificate,213 .bad_certificate => return error.TlsAlertBadCertificate,
208 .unsupported_certificate => error.TlsAlertUnsupportedCertificate,214 .unsupported_certificate => return error.TlsAlertUnsupportedCertificate,
209 .certificate_revoked => error.TlsAlertCertificateRevoked,215 .certificate_revoked => return error.TlsAlertCertificateRevoked,
210 .certificate_expired => error.TlsAlertCertificateExpired,216 .certificate_expired => return error.TlsAlertCertificateExpired,
211 .certificate_unknown => error.TlsAlertCertificateUnknown,217 .certificate_unknown => return error.TlsAlertCertificateUnknown,
212 .illegal_parameter => error.TlsAlertIllegalParameter,218 .illegal_parameter => return error.TlsAlertIllegalParameter,
213 .unknown_ca => error.TlsAlertUnknownCa,219 .unknown_ca => return error.TlsAlertUnknownCa,
214 .access_denied => error.TlsAlertAccessDenied,220 .access_denied => return error.TlsAlertAccessDenied,
215 .decode_error => error.TlsAlertDecodeError,221 .decode_error => return error.TlsAlertDecodeError,
216 .decrypt_error => error.TlsAlertDecryptError,222 .decrypt_error => return error.TlsAlertDecryptError,
217 .protocol_version => error.TlsAlertProtocolVersion,223 .protocol_version => return error.TlsAlertProtocolVersion,
218 .insufficient_security => error.TlsAlertInsufficientSecurity,224 .insufficient_security => return error.TlsAlertInsufficientSecurity,
219 .internal_error => error.TlsAlertInternalError,225 .internal_error => return error.TlsAlertInternalError,
220 .inappropriate_fallback => error.TlsAlertInappropriateFallback,226 .inappropriate_fallback => return error.TlsAlertInappropriateFallback,
221 .user_canceled => {}, // not an error227 .user_canceled => {}, // not an error
222 .missing_extension => error.TlsAlertMissingExtension,228 .missing_extension => return error.TlsAlertMissingExtension,
223 .unsupported_extension => error.TlsAlertUnsupportedExtension,229 .unsupported_extension => return error.TlsAlertUnsupportedExtension,
224 .unrecognized_name => error.TlsAlertUnrecognizedName,230 .unrecognized_name => return error.TlsAlertUnrecognizedName,
225 .bad_certificate_status_response => error.TlsAlertBadCertificateStatusResponse,231 .bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse,
226 .unknown_psk_identity => error.TlsAlertUnknownPskIdentity,232 .unknown_psk_identity => return error.TlsAlertUnknownPskIdentity,
227 .certificate_required => error.TlsAlertCertificateRequired,233 .certificate_required => return error.TlsAlertCertificateRequired,
228 .no_application_protocol => error.TlsAlertNoApplicationProtocol,234 .no_application_protocol => return error.TlsAlertNoApplicationProtocol,
229 _ => error.TlsAlertUnknown,235 _ => return error.TlsAlertUnknown,
230 };236 }
231 }237 }
232};238};
233239
...@@ -260,6 +266,17 @@ pub const SignatureScheme = enum(u16) {...@@ -260,6 +266,17 @@ pub const SignatureScheme = enum(u16) {
260 rsa_pkcs1_sha1 = 0x0201,266 rsa_pkcs1_sha1 = 0x0201,
261 ecdsa_sha1 = 0x0203,267 ecdsa_sha1 = 0x0203,
262268
269 ecdsa_brainpoolP256r1tls13_sha256 = 0x081a,
270 ecdsa_brainpoolP384r1tls13_sha384 = 0x081b,
271 ecdsa_brainpoolP512r1tls13_sha512 = 0x081c,
272
273 rsa_sha224 = 0x0301,
274 dsa_sha224 = 0x0302,
275 ecdsa_sha224 = 0x0303,
276 dsa_sha256 = 0x0402,
277 dsa_sha384 = 0x0502,
278 dsa_sha512 = 0x0602,
279
263 _,280 _,
264};281};
265282
...@@ -285,7 +302,27 @@ pub const NamedGroup = enum(u16) {...@@ -285,7 +302,27 @@ pub const NamedGroup = enum(u16) {
285 _,302 _,
286};303};
287304
305pub const PskKeyExchangeMode = enum(u8) {
306 psk_ke = 0,
307 psk_dhe_ke = 1,
308 _,
309};
310
288pub const CipherSuite = enum(u16) {311pub const CipherSuite = enum(u16) {
312 RSA_WITH_AES_128_CBC_SHA = 0x002F,
313 DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
314 RSA_WITH_AES_256_CBC_SHA = 0x0035,
315 DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
316 RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
317 RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
318 DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
319 DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
320 RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
321 RSA_WITH_AES_256_GCM_SHA384 = 0x009D,
322 DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
323 DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F,
324 EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
325
289 AES_128_GCM_SHA256 = 0x1301,326 AES_128_GCM_SHA256 = 0x1301,
290 AES_256_GCM_SHA384 = 0x1302,327 AES_256_GCM_SHA384 = 0x1302,
291 CHACHA20_POLY1305_SHA256 = 0x1303,328 CHACHA20_POLY1305_SHA256 = 0x1303,
...@@ -293,6 +330,102 @@ pub const CipherSuite = enum(u16) {...@@ -293,6 +330,102 @@ pub const CipherSuite = enum(u16) {
293 AES_128_CCM_8_SHA256 = 0x1305,330 AES_128_CCM_8_SHA256 = 0x1305,
294 AEGIS_256_SHA512 = 0x1306,331 AEGIS_256_SHA512 = 0x1306,
295 AEGIS_128L_SHA256 = 0x1307,332 AEGIS_128L_SHA256 = 0x1307,
333
334 ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
335 ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
336 ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
337 ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
338 ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
339 ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
340 ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
341 ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
342 ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
343 ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
344 ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
345 ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
346
347 ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8,
348 ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9,
349 DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA,
350
351 _,
352
353 pub const With = enum {
354 AES_128_CBC_SHA,
355 AES_256_CBC_SHA,
356 AES_128_CBC_SHA256,
357 AES_256_CBC_SHA256,
358 AES_256_CBC_SHA384,
359
360 AES_128_GCM_SHA256,
361 AES_256_GCM_SHA384,
362
363 CHACHA20_POLY1305_SHA256,
364
365 AES_128_CCM_SHA256,
366 AES_128_CCM_8_SHA256,
367
368 AEGIS_256_SHA512,
369 AEGIS_128L_SHA256,
370 };
371
372 pub fn with(cipher_suite: CipherSuite) With {
373 return switch (cipher_suite) {
374 .RSA_WITH_AES_128_CBC_SHA,
375 .DHE_RSA_WITH_AES_128_CBC_SHA,
376 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
377 .ECDHE_RSA_WITH_AES_128_CBC_SHA,
378 => .AES_128_CBC_SHA,
379 .RSA_WITH_AES_256_CBC_SHA,
380 .DHE_RSA_WITH_AES_256_CBC_SHA,
381 .ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
382 .ECDHE_RSA_WITH_AES_256_CBC_SHA,
383 => .AES_256_CBC_SHA,
384 .RSA_WITH_AES_128_CBC_SHA256,
385 .DHE_RSA_WITH_AES_128_CBC_SHA256,
386 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
387 .ECDHE_RSA_WITH_AES_128_CBC_SHA256,
388 => .AES_128_CBC_SHA256,
389 .RSA_WITH_AES_256_CBC_SHA256,
390 .DHE_RSA_WITH_AES_256_CBC_SHA256,
391 => .AES_256_CBC_SHA256,
392 .ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
393 .ECDHE_RSA_WITH_AES_256_CBC_SHA384,
394 => .AES_256_CBC_SHA384,
395
396 .RSA_WITH_AES_128_GCM_SHA256,
397 .DHE_RSA_WITH_AES_128_GCM_SHA256,
398 .AES_128_GCM_SHA256,
399 .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
400 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
401 => .AES_128_GCM_SHA256,
402 .RSA_WITH_AES_256_GCM_SHA384,
403 .DHE_RSA_WITH_AES_256_GCM_SHA384,
404 .AES_256_GCM_SHA384,
405 .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
406 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
407 => .AES_256_GCM_SHA384,
408
409 .CHACHA20_POLY1305_SHA256,
410 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
411 .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
412 .DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
413 => .CHACHA20_POLY1305_SHA256,
414
415 .AES_128_CCM_SHA256 => .AES_128_CCM_SHA256,
416 .AES_128_CCM_8_SHA256 => .AES_128_CCM_8_SHA256,
417
418 .AEGIS_256_SHA512 => .AEGIS_256_SHA512,
419 .AEGIS_128L_SHA256 => .AEGIS_128L_SHA256,
420
421 .EMPTY_RENEGOTIATION_INFO_SCSV => unreachable,
422 _ => unreachable,
423 };
424 }
425};
426
427pub const CompressionMethod = enum(u8) {
428 null = 0,
296 _,429 _,
297};430};
298431
...@@ -308,58 +441,114 @@ pub const KeyUpdateRequest = enum(u8) {...@@ -308,58 +441,114 @@ pub const KeyUpdateRequest = enum(u8) {
308 _,441 _,
309};442};
310443
311pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type) type {444pub const ChangeCipherSpecType = enum(u8) {
445 change_cipher_spec = 1,
446 _,
447};
448
449pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
312 return struct {450 return struct {
313 pub const AEAD = AeadType;451 pub const A = ApplicationCipherT(AeadType, HashType, explicit_iv_length);
314 pub const Hash = HashType;
315 pub const Hmac = crypto.auth.hmac.Hmac(Hash);
316 pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
317452
318 handshake_secret: [Hkdf.prk_length]u8,453 transcript_hash: A.Hash,
319 master_secret: [Hkdf.prk_length]u8,454 version: union {
320 client_handshake_key: [AEAD.key_length]u8,455 tls_1_2: struct {
321 server_handshake_key: [AEAD.key_length]u8,456 expected_server_verify_data: [A.verify_data_length]u8,
322 client_finished_key: [Hmac.key_length]u8,457 app_cipher: A.Tls_1_2,
323 server_finished_key: [Hmac.key_length]u8,458 },
324 client_handshake_iv: [AEAD.nonce_length]u8,459 tls_1_3: struct {
325 server_handshake_iv: [AEAD.nonce_length]u8,460 handshake_secret: [A.Hkdf.prk_length]u8,
326 transcript_hash: Hash,461 master_secret: [A.Hkdf.prk_length]u8,
462 client_handshake_key: [A.AEAD.key_length]u8,
463 server_handshake_key: [A.AEAD.key_length]u8,
464 client_finished_key: [A.Hmac.key_length]u8,
465 server_finished_key: [A.Hmac.key_length]u8,
466 client_handshake_iv: [A.AEAD.nonce_length]u8,
467 server_handshake_iv: [A.AEAD.nonce_length]u8,
468 },
469 },
327 };470 };
328}471}
329472
330pub const HandshakeCipher = union(enum) {473pub const HandshakeCipher = union(enum) {
331 AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256),474 AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8),
332 AES_256_GCM_SHA384: HandshakeCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384),475 AES_256_GCM_SHA384: HandshakeCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8),
333 CHACHA20_POLY1305_SHA256: HandshakeCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256),476 CHACHA20_POLY1305_SHA256: HandshakeCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0),
334 AEGIS_256_SHA512: HandshakeCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512),477 AEGIS_256_SHA512: HandshakeCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0),
335 AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256),478 AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
336};479};
337480
338pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type) type {481pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
339 return struct {482 return union {
340 pub const AEAD = AeadType;483 pub const AEAD = AeadType;
341 pub const Hash = HashType;484 pub const Hash = HashType;
342 pub const Hmac = crypto.auth.hmac.Hmac(Hash);485 pub const Hmac = crypto.auth.hmac.Hmac(Hash);
343 pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);486 pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
344487
345 client_secret: [Hash.digest_length]u8,488 pub const enc_key_length = AEAD.key_length;
346 server_secret: [Hash.digest_length]u8,489 pub const fixed_iv_length = AEAD.nonce_length - explicit_iv_length;
347 client_key: [AEAD.key_length]u8,490 pub const record_iv_length = explicit_iv_length;
348 server_key: [AEAD.key_length]u8,491 pub const mac_length = AEAD.tag_length;
349 client_iv: [AEAD.nonce_length]u8,492 pub const mac_key_length = Hmac.key_length_min;
350 server_iv: [AEAD.nonce_length]u8,493 pub const verify_data_length = 12;
494
495 tls_1_2: Tls_1_2,
496 tls_1_3: Tls_1_3,
497
498 pub const Tls_1_2 = extern struct {
499 client_write_MAC_key: [mac_key_length]u8,
500 server_write_MAC_key: [mac_key_length]u8,
501 client_write_key: [enc_key_length]u8,
502 server_write_key: [enc_key_length]u8,
503 client_write_IV: [fixed_iv_length]u8,
504 server_write_IV: [fixed_iv_length]u8,
505 // non-standard entropy
506 client_salt: [record_iv_length]u8,
507 };
508
509 pub const Tls_1_3 = struct {
510 client_secret: [Hash.digest_length]u8,
511 server_secret: [Hash.digest_length]u8,
512 client_key: [AEAD.key_length]u8,
513 server_key: [AEAD.key_length]u8,
514 client_iv: [AEAD.nonce_length]u8,
515 server_iv: [AEAD.nonce_length]u8,
516 };
351 };517 };
352}518}
353519
354/// Encryption parameters for application traffic.520/// Encryption parameters for application traffic.
355pub const ApplicationCipher = union(enum) {521pub const ApplicationCipher = union(enum) {
356 AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256),522 AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8),
357 AES_256_GCM_SHA384: ApplicationCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384),523 AES_256_GCM_SHA384: ApplicationCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8),
358 CHACHA20_POLY1305_SHA256: ApplicationCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256),524 CHACHA20_POLY1305_SHA256: ApplicationCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0),
359 AEGIS_256_SHA512: ApplicationCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512),525 AEGIS_256_SHA512: ApplicationCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0),
360 AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256),526 AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
361};527};
362528
529pub fn hmacExpandLabel(
530 comptime Hmac: type,
531 secret: []const u8,
532 label_then_seed: []const []const u8,
533 comptime len: usize,
534) [len]u8 {
535 const initial_hmac: Hmac = .init(secret);
536 var a: [Hmac.mac_length]u8 = undefined;
537 var result: [std.mem.alignForwardAnyAlign(usize, len, Hmac.mac_length)]u8 = undefined;
538 var index: usize = 0;
539 while (index < result.len) : (index += Hmac.mac_length) {
540 var a_hmac = initial_hmac;
541 if (index > 0) a_hmac.update(&a) else for (label_then_seed) |part| a_hmac.update(part);
542 a_hmac.final(&a);
543
544 var result_hmac = initial_hmac;
545 result_hmac.update(&a);
546 for (label_then_seed) |part| result_hmac.update(part);
547 result_hmac.final(result[index..][0..Hmac.mac_length]);
548 }
549 return result[0..len].*;
550}
551
363pub fn hkdfExpandLabel(552pub fn hkdfExpandLabel(
364 comptime Hkdf: type,553 comptime Hkdf: type,
365 key: [Hkdf.prk_length]u8,554 key: [Hkdf.prk_length]u8,
...@@ -399,38 +588,39 @@ pub fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8)...@@ -399,38 +588,39 @@ pub fn hmac(comptime Hmac: type, message: []const u8, key: [Hmac.key_length]u8)
399 return result;588 return result;
400}589}
401590
402pub inline fn extension(comptime et: ExtensionType, bytes: anytype) [2 + 2 + bytes.len]u8 {591pub inline fn extension(et: ExtensionType, bytes: anytype) [2 + 2 + bytes.len]u8 {
403 return int2(@intFromEnum(et)) ++ array(1, bytes);592 return int(u16, @intFromEnum(et)) ++ array(u16, u8, bytes);
404}
405
406pub inline fn array(comptime elem_size: comptime_int, bytes: anytype) [2 + bytes.len]u8 {
407 comptime assert(bytes.len % elem_size == 0);
408 return int2(bytes.len) ++ bytes;
409}593}
410594
411pub inline fn enum_array(comptime E: type, comptime tags: []const E) [2 + @sizeOf(E) * tags.len]u8 {595pub inline fn array(
412 assert(@sizeOf(E) == 2);596 comptime Len: type,
413 var result: [tags.len * 2]u8 = undefined;597 comptime Elem: type,
414 for (tags, 0..) |elem, i| {598 elems: anytype,
415 result[i * 2] = @as(u8, @truncate(@intFromEnum(elem) >> 8));599) [@divExact(@bitSizeOf(Len), 8) + @divExact(@bitSizeOf(Elem), 8) * elems.len]u8 {
416 result[i * 2 + 1] = @as(u8, @truncate(@intFromEnum(elem)));600 const len_size = @divExact(@bitSizeOf(Len), 8);
601 const elem_size = @divExact(@bitSizeOf(Elem), 8);
602 var arr: [len_size + elem_size * elems.len]u8 = undefined;
603 std.mem.writeInt(Len, arr[0..len_size], @intCast(elem_size * elems.len), .big);
604 const ElemInt = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(Elem) } });
605 for (0.., @as([elems.len]Elem, elems)) |index, elem| {
606 std.mem.writeInt(
607 ElemInt,
608 arr[len_size + elem_size * index ..][0..elem_size],
609 switch (@typeInfo(Elem)) {
610 .int => @as(Elem, elem),
611 .@"enum" => @intFromEnum(@as(Elem, elem)),
612 else => @bitCast(@as(Elem, elem)),
613 },
614 .big,
615 );
417 }616 }
418 return array(2, result);617 return arr;
419}618}
420619
421pub inline fn int2(x: u16) [2]u8 {620pub inline fn int(comptime Int: type, val: Int) [@divExact(@bitSizeOf(Int), 8)]u8 {
422 return .{621 var arr: [@divExact(@bitSizeOf(Int), 8)]u8 = undefined;
423 @as(u8, @truncate(x >> 8)),622 std.mem.writeInt(Int, &arr, val, .big);
424 @as(u8, @truncate(x)),623 return arr;
425 };
426}
427
428pub inline fn int3(x: u24) [3]u8 {
429 return .{
430 @as(u8, @truncate(x >> 16)),
431 @as(u8, @truncate(x >> 8)),
432 @as(u8, @truncate(x)),
433 };
434}624}
435625
436/// An abstraction to ensure that protocol-parsing code does not perform an626/// An abstraction to ensure that protocol-parsing code does not perform an
...@@ -512,9 +702,8 @@ pub const Decoder = struct {...@@ -512,9 +702,8 @@ pub const Decoder = struct {
512 else => @compileError("unsupported int type: " ++ @typeName(T)),702 else => @compileError("unsupported int type: " ++ @typeName(T)),
513 },703 },
514 .@"enum" => |info| {704 .@"enum" => |info| {
515 const int = d.decode(info.tag_type);
516 if (info.is_exhaustive) @compileError("exhaustive enum cannot be used");705 if (info.is_exhaustive) @compileError("exhaustive enum cannot be used");
517 return @as(T, @enumFromInt(int));706 return @enumFromInt(d.decode(info.tag_type));
518 },707 },
519 else => @compileError("unsupported type: " ++ @typeName(T)),708 else => @compileError("unsupported type: " ++ @typeName(T)),
520 }709 }
lib/std/crypto/tls/Client.zig+1214-673
...@@ -8,12 +8,12 @@ const assert = std.debug.assert;...@@ -8,12 +8,12 @@ const assert = std.debug.assert;
8const Certificate = std.crypto.Certificate;8const Certificate = std.crypto.Certificate;
99
10const max_ciphertext_len = tls.max_ciphertext_len;10const max_ciphertext_len = tls.max_ciphertext_len;
11const hmacExpandLabel = tls.hmacExpandLabel;
11const hkdfExpandLabel = tls.hkdfExpandLabel;12const hkdfExpandLabel = tls.hkdfExpandLabel;
12const int2 = tls.int2;13const int = tls.int;
13const int3 = tls.int3;
14const array = tls.array;14const array = tls.array;
15const enum_array = tls.enum_array;
1615
16tls_version: tls.ProtocolVersion,
17read_seq: u64,17read_seq: u64,
18write_seq: u64,18write_seq: u64,
19/// The starting index of cleartext bytes inside `partially_read_buffer`.19/// The starting index of cleartext bytes inside `partially_read_buffer`.
...@@ -33,7 +33,7 @@ received_close_notify: bool,...@@ -33,7 +33,7 @@ received_close_notify: bool,
33/// This makes the application vulnerable to truncation attacks unless the33/// This makes the application vulnerable to truncation attacks unless the
34/// application layer itself verifies that the amount of data received equals34/// application layer itself verifies that the amount of data received equals
35/// the amount of data expected, such as HTTP with the Content-Length header.35/// the amount of data expected, such as HTTP with the Content-Length header.
36allow_truncation_attacks: bool = false,36allow_truncation_attacks: bool,
37application_cipher: tls.ApplicationCipher,37application_cipher: tls.ApplicationCipher,
38/// The size is enough to contain exactly one TLSCiphertext record.38/// The size is enough to contain exactly one TLSCiphertext record.
39/// This buffer is segmented into four parts:39/// This buffer is segmented into four parts:
...@@ -44,6 +44,24 @@ application_cipher: tls.ApplicationCipher,...@@ -44,6 +44,24 @@ application_cipher: tls.ApplicationCipher,
44/// The fields `partial_cleartext_idx`, `partial_ciphertext_idx`, and44/// The fields `partial_cleartext_idx`, `partial_ciphertext_idx`, and
45/// `partial_ciphertext_end` describe the span of the segments.45/// `partial_ciphertext_end` describe the span of the segments.
46partially_read_buffer: [tls.max_ciphertext_record_len]u8,46partially_read_buffer: [tls.max_ciphertext_record_len]u8,
47/// If non-null, ssl secrets are logged to a file. Creating such a log file allows other
48/// programs with access to that file to decrypt all traffic over this connection.
49ssl_key_log: ?struct {
50 client_key_seq: u64,
51 server_key_seq: u64,
52 client_random: [32]u8,
53 file: std.fs.File,
54
55 fn clientCounter(key_log: *@This()) u64 {
56 defer key_log.client_key_seq += 1;
57 return key_log.client_key_seq;
58 }
59
60 fn serverCounter(key_log: *@This()) u64 {
61 defer key_log.server_key_seq += 1;
62 return key_log.server_key_seq;
63 }
64},
4765
48/// This is an example of the type that is needed by the read and write66/// This is an example of the type that is needed by the read and write
49/// functions. It can have any fields but it must at least have these67/// functions. It can have any fields but it must at least have these
...@@ -88,6 +106,32 @@ pub const StreamInterface = struct {...@@ -88,6 +106,32 @@ pub const StreamInterface = struct {
88 }106 }
89};107};
90108
109pub const Options = struct {
110 /// How to perform host verification of server certificates.
111 host: union(enum) {
112 /// No host verification is performed, which prevents a trusted connection from
113 /// being established.
114 no_verification,
115 /// Verify that the server certificate was issues for a given host.
116 explicit: []const u8,
117 },
118 /// How to verify the authenticity of server certificates.
119 ca: union(enum) {
120 /// No ca verification is performed, which prevents a trusted connection from
121 /// being established.
122 no_verification,
123 /// Verify that the server certificate is a valid self-signed certificate.
124 /// This provides no authorization guarantees, as anyone can create a
125 /// self-signed certificate.
126 self_signed,
127 /// Verify that the server certificate is authorized by a given ca bundle.
128 bundle: Certificate.Bundle,
129 },
130 /// If non-null, ssl secrets are logged to this file. Creating such a log file allows
131 /// other programs with access to that file to decrypt all traffic over this connection.
132 ssl_key_log_file: ?std.fs.File = null,
133};
134
91pub fn InitError(comptime Stream: type) type {135pub fn InitError(comptime Stream: type) type {
92 return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || tls.AlertDescription.Error || error{136 return std.mem.Allocator.Error || Stream.WriteError || Stream.ReadError || tls.AlertDescription.Error || error{
93 InsufficientEntropy,137 InsufficientEntropy,
...@@ -136,326 +180,186 @@ pub fn InitError(comptime Stream: type) type {...@@ -136,326 +180,186 @@ pub fn InitError(comptime Stream: type) type {
136 };180 };
137}181}
138182
139/// Initiates a TLS handshake and establishes a TLSv1.3 session with `stream`, which183/// Initiates a TLS handshake and establishes a TLSv1.2 or TLSv1.3 session with `stream`, which
140/// must conform to `StreamInterface`.184/// must conform to `StreamInterface`.
141///185///
142/// `host` is only borrowed during this function call.186/// `host` is only borrowed during this function call.
143pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) InitError(@TypeOf(stream))!Client {187pub fn init(stream: anytype, options: Options) InitError(@TypeOf(stream))!Client {
188 const host = switch (options.host) {
189 .no_verification => "",
190 .explicit => |host| host,
191 };
144 const host_len: u16 = @intCast(host.len);192 const host_len: u16 = @intCast(host.len);
145193
146 var random_buffer: [128]u8 = undefined;194 var random_buffer: [176]u8 = undefined;
147 crypto.random.bytes(&random_buffer);195 crypto.random.bytes(&random_buffer);
148 const hello_rand = random_buffer[0..32].*;196 const client_hello_rand = random_buffer[0..32].*;
197 var key_seq: u64 = 0;
198 var server_hello_rand: [32]u8 = undefined;
149 const legacy_session_id = random_buffer[32..64].*;199 const legacy_session_id = random_buffer[32..64].*;
150 const x25519_kp_seed = random_buffer[64..96].*;
151 const secp256r1_kp_seed = random_buffer[96..128].*;
152200
153 const x25519_kp = crypto.dh.X25519.KeyPair.create(x25519_kp_seed) catch |err| switch (err) {201 var key_share = KeyShare.init(random_buffer[64..176].*) catch |err| switch (err) {
154 // Only possible to happen if the private key is all zeroes.202 // Only possible to happen if the seed is all zeroes.
155 error.IdentityElement => return error.InsufficientEntropy,
156 };
157 const secp256r1_kp = crypto.sign.ecdsa.EcdsaP256Sha256.KeyPair.create(secp256r1_kp_seed) catch |err| switch (err) {
158 // Only possible to happen if the private key is all zeroes.
159 error.IdentityElement => return error.InsufficientEntropy,203 error.IdentityElement => return error.InsufficientEntropy,
160 };204 };
161 const ml_kem768_kp = crypto.kem.ml_kem.MLKem768.KeyPair.create(null) catch {};
162205
163 const extensions_payload =206 const extensions_payload = tls.extension(.supported_versions, array(u8, tls.ProtocolVersion, .{
164 tls.extension(.supported_versions, [_]u8{207 .tls_1_3,
165 0x02, // byte length of supported versions208 .tls_1_2,
166 0x03, 0x04, // TLS 1.3209 })) ++ tls.extension(.signature_algorithms, array(u16, tls.SignatureScheme, .{
167 }) ++ tls.extension(.signature_algorithms, enum_array(tls.SignatureScheme, &.{
168 .ecdsa_secp256r1_sha256,210 .ecdsa_secp256r1_sha256,
169 .ecdsa_secp384r1_sha384,211 .ecdsa_secp384r1_sha384,
212 .rsa_pkcs1_sha256,
213 .rsa_pkcs1_sha384,
214 .rsa_pkcs1_sha512,
170 .rsa_pss_rsae_sha256,215 .rsa_pss_rsae_sha256,
171 .rsa_pss_rsae_sha384,216 .rsa_pss_rsae_sha384,
172 .rsa_pss_rsae_sha512,217 .rsa_pss_rsae_sha512,
218 .rsa_pss_pss_sha256,
219 .rsa_pss_pss_sha384,
220 .rsa_pss_pss_sha512,
221 .rsa_pkcs1_sha1,
173 .ed25519,222 .ed25519,
174 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{223 })) ++ tls.extension(.supported_groups, array(u16, tls.NamedGroup, .{
175 .x25519_ml_kem768,224 .x25519_ml_kem768,
176 .secp256r1,225 .secp256r1,
226 .secp384r1,
177 .x25519,227 .x25519,
178 })) ++ tls.extension(228 })) ++ tls.extension(.psk_key_exchange_modes, array(u8, tls.PskKeyExchangeMode, .{
179 .key_share,229 .psk_dhe_ke,
180 array(1, int2(@intFromEnum(tls.NamedGroup.x25519)) ++230 })) ++ tls.extension(.key_share, array(
181 array(1, x25519_kp.public_key) ++231 u16,
182 int2(@intFromEnum(tls.NamedGroup.secp256r1)) ++232 u8,
183 array(1, secp256r1_kp.public_key.toUncompressedSec1()) ++233 int(u16, @intFromEnum(tls.NamedGroup.x25519_ml_kem768)) ++
184 int2(@intFromEnum(tls.NamedGroup.x25519_ml_kem768)) ++234 array(u16, u8, key_share.ml_kem768_kp.public_key.toBytes() ++ key_share.x25519_kp.public_key) ++
185 array(1, x25519_kp.public_key ++ ml_kem768_kp.public_key.toBytes())),235 int(u16, @intFromEnum(tls.NamedGroup.secp256r1)) ++
186 ) ++236 array(u16, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1()) ++
187 int2(@intFromEnum(tls.ExtensionType.server_name)) ++237 int(u16, @intFromEnum(tls.NamedGroup.secp384r1)) ++
188 int2(host_len + 5) ++ // byte length of this extension payload238 array(u16, u8, key_share.secp384r1_kp.public_key.toUncompressedSec1()) ++
189 int2(host_len + 3) ++ // server_name_list byte count239 int(u16, @intFromEnum(tls.NamedGroup.x25519)) ++
190 [1]u8{0x00} ++ // name_type240 array(u16, u8, key_share.x25519_kp.public_key),
191 int2(host_len);241 ));
242 const server_name_extension = int(u16, @intFromEnum(tls.ExtensionType.server_name)) ++
243 int(u16, 2 + 1 + 2 + host_len) ++ // byte length of this extension payload
244 int(u16, 1 + 2 + host_len) ++ // server_name_list byte count
245 .{0x00} ++ // name_type
246 int(u16, host_len);
247 const server_name_extension_len = switch (options.host) {
248 .no_verification => 0,
249 .explicit => server_name_extension.len + host_len,
250 };
192251
193 const extensions_header =252 const extensions_header =
194 int2(@intCast(extensions_payload.len + host_len)) ++253 int(u16, @intCast(extensions_payload.len + server_name_extension_len)) ++
195 extensions_payload;254 extensions_payload ++
196255 server_name_extension;
197 const legacy_compression_methods = 0x0100;
198256
199 const client_hello =257 const client_hello =
200 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++258 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
201 hello_rand ++259 client_hello_rand ++
202 [1]u8{32} ++ legacy_session_id ++260 [1]u8{32} ++ legacy_session_id ++
203 cipher_suites ++261 cipher_suites ++
204 int2(legacy_compression_methods) ++262 array(u8, tls.CompressionMethod, .{.null}) ++
205 extensions_header;263 extensions_header;
206264
207 const out_handshake =265 const out_handshake = .{@intFromEnum(tls.HandshakeType.client_hello)} ++
208 [_]u8{@intFromEnum(tls.HandshakeType.client_hello)} ++266 int(u24, @intCast(client_hello.len - server_name_extension.len + server_name_extension_len)) ++
209 int3(@intCast(client_hello.len + host_len)) ++
210 client_hello;267 client_hello;
211268
212 const plaintext_header = [_]u8{269 const cleartext_header_buf = .{@intFromEnum(tls.ContentType.handshake)} ++
213 @intFromEnum(tls.ContentType.handshake),270 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_0)) ++
214 0x03, 0x01, // legacy_record_version271 int(u16, @intCast(out_handshake.len - server_name_extension.len + server_name_extension_len)) ++
215 } ++ int2(@intCast(out_handshake.len + host_len)) ++ out_handshake;272 out_handshake;
273 const cleartext_header = switch (options.host) {
274 .no_verification => cleartext_header_buf[0 .. cleartext_header_buf.len - server_name_extension.len],
275 .explicit => &cleartext_header_buf,
276 };
216277
217 {278 {
218 var iovecs = [_]std.posix.iovec_const{279 var iovecs = [_]std.posix.iovec_const{
219 .{280 .{ .base = cleartext_header.ptr, .len = cleartext_header.len },
220 .base = &plaintext_header,281 .{ .base = host.ptr, .len = host.len },
221 .len = plaintext_header.len,
222 },
223 .{
224 .base = host.ptr,
225 .len = host.len,
226 },
227 };282 };
228 try stream.writevAll(&iovecs);283 try stream.writevAll(iovecs[0..if (host.len == 0) 1 else 2]);
229 }284 }
230285
231 const client_hello_bytes1 = plaintext_header[5..];286 var tls_version: tls.ProtocolVersion = undefined;
232287 // These are used for two purposes:
233 var handshake_cipher: tls.HandshakeCipher = undefined;
234 var handshake_buffer: [8000]u8 = undefined;
235 var d: tls.Decoder = .{ .buf = &handshake_buffer };
236 {
237 try d.readAtLeastOurAmt(stream, tls.record_header_len);
238 const ct = d.decode(tls.ContentType);
239 d.skip(2); // legacy_record_version
240 const record_len = d.decode(u16);
241 try d.readAtLeast(stream, record_len);
242 const server_hello_fragment = d.buf[d.idx..][0..record_len];
243 var ptd = try d.sub(record_len);
244 switch (ct) {
245 .alert => {
246 try ptd.ensure(2);
247 const level = ptd.decode(tls.AlertLevel);
248 const desc = ptd.decode(tls.AlertDescription);
249 _ = level;
250
251 // if this isn't a error alert, then it's a closure alert, which makes no sense in a handshake
252 try desc.toError();
253 // TODO: handle server-side closures
254 return error.TlsUnexpectedMessage;
255 },
256 .handshake => {
257 try ptd.ensure(4);
258 const handshake_type = ptd.decode(tls.HandshakeType);
259 if (handshake_type != .server_hello) return error.TlsUnexpectedMessage;
260 const length = ptd.decode(u24);
261 var hsd = try ptd.sub(length);
262 try hsd.ensure(2 + 32 + 1 + 32 + 2 + 1 + 2);
263 const legacy_version = hsd.decode(u16);
264 const random = hsd.array(32);
265 if (mem.eql(u8, random, &tls.hello_retry_request_sequence)) {
266 // This is a HelloRetryRequest message. This client implementation
267 // does not expect to get one.
268 return error.TlsUnexpectedMessage;
269 }
270 const legacy_session_id_echo_len = hsd.decode(u8);
271 if (legacy_session_id_echo_len != 32) return error.TlsIllegalParameter;
272 const legacy_session_id_echo = hsd.array(32);
273 if (!mem.eql(u8, legacy_session_id_echo, &legacy_session_id))
274 return error.TlsIllegalParameter;
275 const cipher_suite_tag = hsd.decode(tls.CipherSuite);
276 hsd.skip(1); // legacy_compression_method
277 const extensions_size = hsd.decode(u16);
278 var all_extd = try hsd.sub(extensions_size);
279 var supported_version: u16 = 0;
280 var shared_key: []const u8 = undefined;
281 var have_shared_key = false;
282 while (!all_extd.eof()) {
283 try all_extd.ensure(2 + 2);
284 const et = all_extd.decode(tls.ExtensionType);
285 const ext_size = all_extd.decode(u16);
286 var extd = try all_extd.sub(ext_size);
287 switch (et) {
288 .supported_versions => {
289 if (supported_version != 0) return error.TlsIllegalParameter;
290 try extd.ensure(2);
291 supported_version = extd.decode(u16);
292 },
293 .key_share => {
294 if (have_shared_key) return error.TlsIllegalParameter;
295 have_shared_key = true;
296 try extd.ensure(4);
297 const named_group = extd.decode(tls.NamedGroup);
298 const key_size = extd.decode(u16);
299 try extd.ensure(key_size);
300 switch (named_group) {
301 .x25519_ml_kem768 => {
302 const xksl = crypto.dh.X25519.public_length;
303 const hksl = xksl + crypto.kem.ml_kem.MLKem768.ciphertext_length;
304 if (key_size != hksl)
305 return error.TlsIllegalParameter;
306 const server_ks = extd.array(hksl);
307
308 shared_key = &((crypto.dh.X25519.scalarmult(
309 x25519_kp.secret_key,
310 server_ks[0..xksl].*,
311 ) catch return error.TlsDecryptFailure) ++ (ml_kem768_kp.secret_key.decaps(
312 server_ks[xksl..hksl],
313 ) catch return error.TlsDecryptFailure));
314 },
315 .x25519 => {
316 const ksl = crypto.dh.X25519.public_length;
317 if (key_size != ksl) return error.TlsIllegalParameter;
318 const server_pub_key = extd.array(ksl);
319
320 shared_key = &(crypto.dh.X25519.scalarmult(
321 x25519_kp.secret_key,
322 server_pub_key.*,
323 ) catch return error.TlsDecryptFailure);
324 },
325 .secp256r1 => {
326 const server_pub_key = extd.slice(key_size);
327
328 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;
329 const pk = PublicKey.fromSec1(server_pub_key) catch {
330 return error.TlsDecryptFailure;
331 };
332 const mul = pk.p.mulPublic(secp256r1_kp.secret_key.bytes, .big) catch {
333 return error.TlsDecryptFailure;
334 };
335 shared_key = &mul.affineCoordinates().x.toBytes(.big);
336 },
337 else => {
338 return error.TlsIllegalParameter;
339 },
340 }
341 },
342 else => {},
343 }
344 }
345 if (!have_shared_key) return error.TlsIllegalParameter;
346
347 const tls_version = if (supported_version == 0) legacy_version else supported_version;
348 if (tls_version != @intFromEnum(tls.ProtocolVersion.tls_1_3))
349 return error.TlsIllegalParameter;
350
351 switch (cipher_suite_tag) {
352 inline .AES_128_GCM_SHA256,
353 .AES_256_GCM_SHA384,
354 .CHACHA20_POLY1305_SHA256,
355 .AEGIS_256_SHA512,
356 .AEGIS_128L_SHA256,
357 => |tag| {
358 const P = std.meta.TagPayloadByName(tls.HandshakeCipher, @tagName(tag));
359 handshake_cipher = @unionInit(tls.HandshakeCipher, @tagName(tag), .{
360 .handshake_secret = undefined,
361 .master_secret = undefined,
362 .client_handshake_key = undefined,
363 .server_handshake_key = undefined,
364 .client_finished_key = undefined,
365 .server_finished_key = undefined,
366 .client_handshake_iv = undefined,
367 .server_handshake_iv = undefined,
368 .transcript_hash = P.Hash.init(.{}),
369 });
370 const p = &@field(handshake_cipher, @tagName(tag));
371 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1
372 p.transcript_hash.update(host); // Client Hello part 2
373 p.transcript_hash.update(server_hello_fragment);
374 const hello_hash = p.transcript_hash.peek();
375 const zeroes = [1]u8{0} ** P.Hash.digest_length;
376 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);
377 const empty_hash = tls.emptyHash(P.Hash);
378 const hs_derived_secret = hkdfExpandLabel(P.Hkdf, early_secret, "derived", &empty_hash, P.Hash.digest_length);
379 p.handshake_secret = P.Hkdf.extract(&hs_derived_secret, shared_key);
380 const ap_derived_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "derived", &empty_hash, P.Hash.digest_length);
381 p.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes);
382 const client_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length);
383 const server_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "s hs traffic", &hello_hash, P.Hash.digest_length);
384 p.client_finished_key = hkdfExpandLabel(P.Hkdf, client_secret, "finished", "", P.Hmac.key_length);
385 p.server_finished_key = hkdfExpandLabel(P.Hkdf, server_secret, "finished", "", P.Hmac.key_length);
386 p.client_handshake_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
387 p.server_handshake_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
388 p.client_handshake_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
389 p.server_handshake_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
390 },
391 else => {
392 return error.TlsIllegalParameter;
393 },
394 }
395 },
396 else => return error.TlsUnexpectedMessage,
397 }
398 }
399
400 // This is used for two purposes:
401 // * Detect whether a certificate is the first one presented, in which case288 // * Detect whether a certificate is the first one presented, in which case
402 // we need to verify the host name.289 // we need to verify the host name.
290 var cert_index: usize = 0;
403 // * Flip back and forth between the two cleartext buffers in order to keep291 // * Flip back and forth between the two cleartext buffers in order to keep
404 // the previous certificate in memory so that it can be verified by the292 // the previous certificate in memory so that it can be verified by the
405 // next one.293 // next one.
406 var cert_index: usize = 0;294 var cert_buf_index: usize = 0;
295 var write_seq: u64 = 0;
407 var read_seq: u64 = 0;296 var read_seq: u64 = 0;
408 var prev_cert: Certificate.Parsed = undefined;297 var prev_cert: Certificate.Parsed = undefined;
409 // Set to true once a trust chain has been established from the first298 const CipherState = enum {
410 // certificate to a root CA.299 /// No cipher is in use
300 cleartext,
301 /// Handshake cipher is in use
302 handshake,
303 /// Application cipher is in use
304 application,
305 };
306 var pending_cipher_state: CipherState = .cleartext;
307 var cipher_state = pending_cipher_state;
411 const HandshakeState = enum {308 const HandshakeState = enum {
309 /// In this state we expect only a server hello message.
310 hello,
412 /// In this state we expect only an encrypted_extensions message.311 /// In this state we expect only an encrypted_extensions message.
413 encrypted_extensions,312 encrypted_extensions,
414 /// In this state we expect certificate messages.313 /// In this state we expect certificate handshake messages.
415 certificate,314 certificate,
416 /// In this state we expect certificate or certificate_verify messages.315 /// In this state we expect certificate or certificate_verify messages.
417 /// certificate messages are ignored since the trust chain is already316 /// certificate messages are ignored since the trust chain is already
418 /// established.317 /// established.
419 trust_chain_established,318 trust_chain_established,
420 /// In this state, we expect only the finished message.319 /// In this state, we expect only the server_hello_done handshake message.
320 server_hello_done,
321 /// In this state, we expect only the finished handshake message.
421 finished,322 finished,
422 };323 };
423 var handshake_state: HandshakeState = .encrypted_extensions;324 var handshake_state: HandshakeState = .hello;
424 var cleartext_bufs: [2][8000]u8 = undefined;325 var handshake_cipher: tls.HandshakeCipher = undefined;
425 var main_cert_pub_key_algo: Certificate.AlgorithmCategory = undefined;326 var main_cert_pub_key: CertificatePublicKey = undefined;
426 var main_cert_pub_key_buf: [600]u8 = undefined;
427 var main_cert_pub_key_len: u16 = undefined;
428 const now_sec = std.time.timestamp();327 const now_sec = std.time.timestamp();
429328
430 while (true) {329 var cleartext_fragment_start: usize = 0;
330 var cleartext_fragment_end: usize = 0;
331 var cleartext_bufs: [2][tls.max_ciphertext_inner_record_len]u8 = undefined;
332 var handshake_buffer: [tls.max_ciphertext_record_len]u8 = undefined;
333 var d: tls.Decoder = .{ .buf = &handshake_buffer };
334 fragment: while (true) {
431 try d.readAtLeastOurAmt(stream, tls.record_header_len);335 try d.readAtLeastOurAmt(stream, tls.record_header_len);
432 const record_header = d.buf[d.idx..][0..5];336 const record_header = d.buf[d.idx..][0..tls.record_header_len];
433 const ct = d.decode(tls.ContentType);337 const record_ct = d.decode(tls.ContentType);
434 d.skip(2); // legacy_version338 d.skip(2); // legacy_version
435 const record_len = d.decode(u16);339 const record_len = d.decode(u16);
436 try d.readAtLeast(stream, record_len);340 try d.readAtLeast(stream, record_len);
437 var record_decoder = try d.sub(record_len);341 var record_decoder = try d.sub(record_len);
438 switch (ct) {342 var ctd, const ct = content: switch (cipher_state) {
439 .change_cipher_spec => {343 .cleartext => .{ record_decoder, record_ct },
440 try record_decoder.ensure(1);344 .handshake => {
441 if (record_decoder.decode(u8) != 0x01) return error.TlsIllegalParameter;345 std.debug.assert(tls_version == .tls_1_3);
442 },346 if (record_ct != .application_data) return error.TlsUnexpectedMessage;
443 .application_data => {347 try record_decoder.ensure(record_len);
444 const cleartext_buf = &cleartext_bufs[cert_index % 2];348 const cleartext_buf = &cleartext_bufs[cert_buf_index % 2];
445349 switch (handshake_cipher) {
446 const cleartext = switch (handshake_cipher) {350 inline else => |*p| {
447 inline else => |*p| c: {351 const pv = &p.version.tls_1_3;
448 const P = @TypeOf(p.*);352 const P = @TypeOf(p.*).A;
449 const ciphertext_len = record_len - P.AEAD.tag_length;353 if (record_len < P.AEAD.tag_length) return error.TlsRecordOverflow;
450 try record_decoder.ensure(ciphertext_len + P.AEAD.tag_length);354 const ciphertext = record_decoder.slice(record_len - P.AEAD.tag_length);
451 const ciphertext = record_decoder.slice(ciphertext_len);355 const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..];
452 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;356 if (ciphertext.len > cleartext_fragment_buf.len) return error.TlsRecordOverflow;
453 const cleartext = cleartext_buf[0..ciphertext.len];357 const cleartext = cleartext_fragment_buf[0..ciphertext.len];
454 const auth_tag = record_decoder.array(P.AEAD.tag_length).*;358 const auth_tag = record_decoder.array(P.AEAD.tag_length).*;
455 const nonce = if (builtin.zig_backend == .stage2_x86_64 and359 const nonce = if (builtin.zig_backend == .stage2_x86_64 and
456 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)360 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
457 nonce: {361 nonce: {
458 var nonce = p.server_handshake_iv;362 var nonce = pv.server_handshake_iv;
459 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);363 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
460 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ read_seq, .big);364 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ read_seq, .big);
461 break :nonce nonce;365 break :nonce nonce;
...@@ -463,268 +367,559 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -463,268 +367,559 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
463 const V = @Vector(P.AEAD.nonce_length, u8);367 const V = @Vector(P.AEAD.nonce_length, u8);
464 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);368 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
465 const operand: V = pad ++ @as([8]u8, @bitCast(big(read_seq)));369 const operand: V = pad ++ @as([8]u8, @bitCast(big(read_seq)));
466 break :nonce @as(V, p.server_handshake_iv) ^ operand;370 break :nonce @as(V, pv.server_handshake_iv) ^ operand;
467 };371 };
468 read_seq += 1;372 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch
469 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, p.server_handshake_key) catch
470 return error.TlsBadRecordMac;373 return error.TlsBadRecordMac;
471 break :c @constCast(mem.trimRight(u8, cleartext, "\x00"));374 cleartext_fragment_end += std.mem.trimRight(u8, cleartext, "\x00").len;
472 },375 },
473 };376 }
474377 read_seq += 1;
475 const inner_ct: tls.ContentType = @enumFromInt(cleartext[cleartext.len - 1]);378 cleartext_fragment_end -= 1;
476 if (inner_ct != .handshake) return error.TlsUnexpectedMessage;379 const ct: tls.ContentType = @enumFromInt(cleartext_buf[cleartext_fragment_end]);
380 if (ct != .handshake) return error.TlsUnexpectedMessage;
381 break :content .{ tls.Decoder.fromTheirSlice(@constCast(cleartext_buf[cleartext_fragment_start..cleartext_fragment_end])), ct };
382 },
383 .application => {
384 std.debug.assert(tls_version == .tls_1_2);
385 if (record_ct != .handshake) return error.TlsUnexpectedMessage;
386 try record_decoder.ensure(record_len);
387 const cleartext_buf = &cleartext_bufs[cert_buf_index % 2];
388 switch (handshake_cipher) {
389 inline else => |*p| {
390 const pv = &p.version.tls_1_2;
391 const P = @TypeOf(p.*).A;
392 if (record_len < P.record_iv_length + P.mac_length) return error.TlsRecordOverflow;
393 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
394 const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..];
395 if (message_len > cleartext_fragment_buf.len) return error.TlsRecordOverflow;
396 const cleartext = cleartext_fragment_buf[0..message_len];
397 const ad = std.mem.toBytes(big(read_seq)) ++
398 record_header[0 .. 1 + 2] ++
399 std.mem.toBytes(big(message_len));
400 const record_iv = record_decoder.array(P.record_iv_length).*;
401 const masked_read_seq = read_seq &
402 comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length);
403 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
404 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
405 nonce: {
406 var nonce = pv.app_cipher.server_write_IV ++ record_iv;
407 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
408 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ masked_read_seq, .big);
409 break :nonce nonce;
410 } else nonce: {
411 const V = @Vector(P.AEAD.nonce_length, u8);
412 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
413 const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq)));
414 break :nonce @as(V, pv.app_cipher.server_write_IV ++ record_iv) ^ operand;
415 };
416 const ciphertext = record_decoder.slice(message_len);
417 const auth_tag = record_decoder.array(P.mac_length);
418 P.AEAD.decrypt(cleartext, ciphertext, auth_tag.*, ad, nonce, pv.app_cipher.server_write_key) catch return error.TlsBadRecordMac;
419 cleartext_fragment_end += message_len;
420 },
421 }
422 read_seq += 1;
423 break :content .{ tls.Decoder.fromTheirSlice(cleartext_buf[cleartext_fragment_start..cleartext_fragment_end]), record_ct };
424 },
425 };
426 switch (ct) {
427 .alert => {
428 ctd.ensure(2) catch continue :fragment;
429 const level = ctd.decode(tls.AlertLevel);
430 const desc = ctd.decode(tls.AlertDescription);
431 _ = level;
477432
478 var ctd = tls.Decoder.fromTheirSlice(cleartext[0 .. cleartext.len - 1]);433 // if this isn't a error alert, then it's a closure alert, which makes no sense in a handshake
479 while (true) {434 try desc.toError();
480 try ctd.ensure(4);435 // TODO: handle server-side closures
481 const handshake_type = ctd.decode(tls.HandshakeType);436 return error.TlsUnexpectedMessage;
482 const handshake_len = ctd.decode(u24);437 },
483 var hsd = try ctd.sub(handshake_len);438 .change_cipher_spec => {
484 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];439 ctd.ensure(1) catch continue :fragment;
485 const handshake = ctd.buf[ctd.idx - handshake_len .. ctd.idx];440 if (ctd.decode(tls.ChangeCipherSpecType) != .change_cipher_spec) return error.TlsIllegalParameter;
486 switch (handshake_type) {441 cipher_state = pending_cipher_state;
487 .encrypted_extensions => {442 },
488 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;443 .handshake => while (true) {
489 handshake_state = .certificate;444 ctd.ensure(4) catch continue :fragment;
490 switch (handshake_cipher) {445 const handshake_type = ctd.decode(tls.HandshakeType);
491 inline else => |*p| p.transcript_hash.update(wrapped_handshake),446 const handshake_len = ctd.decode(u24);
492 }447 var hsd = ctd.sub(handshake_len) catch continue :fragment;
448 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];
449 switch (handshake_type) {
450 .server_hello => {
451 if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
452 if (handshake_state != .hello) return error.TlsUnexpectedMessage;
453 try hsd.ensure(2 + 32 + 1);
454 const legacy_version = hsd.decode(u16);
455 @memcpy(&server_hello_rand, hsd.array(32));
456 if (mem.eql(u8, &server_hello_rand, &tls.hello_retry_request_sequence)) {
457 // This is a HelloRetryRequest message. This client implementation
458 // does not expect to get one.
459 return error.TlsUnexpectedMessage;
460 }
461 const legacy_session_id_echo_len = hsd.decode(u8);
462 try hsd.ensure(legacy_session_id_echo_len + 2 + 1);
463 const legacy_session_id_echo = hsd.slice(legacy_session_id_echo_len);
464 const cipher_suite_tag = hsd.decode(tls.CipherSuite);
465 hsd.skip(1); // legacy_compression_method
466 var supported_version: ?u16 = null;
467 if (!hsd.eof()) {
493 try hsd.ensure(2);468 try hsd.ensure(2);
494 const total_ext_size = hsd.decode(u16);469 const extensions_size = hsd.decode(u16);
495 var all_extd = try hsd.sub(total_ext_size);470 var all_extd = try hsd.sub(extensions_size);
496 while (!all_extd.eof()) {471 while (!all_extd.eof()) {
497 try all_extd.ensure(4);472 try all_extd.ensure(2 + 2);
498 const et = all_extd.decode(tls.ExtensionType);473 const et = all_extd.decode(tls.ExtensionType);
499 const ext_size = all_extd.decode(u16);474 const ext_size = all_extd.decode(u16);
500 const extd = try all_extd.sub(ext_size);475 var extd = try all_extd.sub(ext_size);
501 _ = extd;
502 switch (et) {476 switch (et) {
503 .server_name => {},477 .supported_versions => {
478 if (supported_version) |_| return error.TlsIllegalParameter;
479 try extd.ensure(2);
480 supported_version = extd.decode(u16);
481 },
482 .key_share => {
483 if (key_share.getSharedSecret()) |_| return error.TlsIllegalParameter;
484 try extd.ensure(4);
485 const named_group = extd.decode(tls.NamedGroup);
486 const key_size = extd.decode(u16);
487 try extd.ensure(key_size);
488 try key_share.exchange(named_group, extd.slice(key_size));
489 },
504 else => {},490 else => {},
505 }491 }
506 }492 }
507 },493 }
508 .certificate => cert: {
509 switch (handshake_cipher) {
510 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
511 }
512 switch (handshake_state) {
513 .certificate => {},
514 .trust_chain_established => break :cert,
515 else => return error.TlsUnexpectedMessage,
516 }
517 try hsd.ensure(1 + 4);
518 const cert_req_ctx_len = hsd.decode(u8);
519 if (cert_req_ctx_len != 0) return error.TlsIllegalParameter;
520 const certs_size = hsd.decode(u24);
521 var certs_decoder = try hsd.sub(certs_size);
522 while (!certs_decoder.eof()) {
523 try certs_decoder.ensure(3);
524 const cert_size = certs_decoder.decode(u24);
525 const certd = try certs_decoder.sub(cert_size);
526
527 const subject_cert: Certificate = .{
528 .buffer = certd.buf,
529 .index = @intCast(certd.idx),
530 };
531 const subject = try subject_cert.parse();
532 if (cert_index == 0) {
533 // Verify the host on the first certificate.
534 try subject.verifyHostName(host);
535
536 // Keep track of the public key for the
537 // certificate_verify message later.
538 main_cert_pub_key_algo = subject.pub_key_algo;
539 const pub_key = subject.pubKey();
540 if (pub_key.len > main_cert_pub_key_buf.len)
541 return error.CertificatePublicKeyInvalid;
542 @memcpy(main_cert_pub_key_buf[0..pub_key.len], pub_key);
543 main_cert_pub_key_len = @intCast(pub_key.len);
544 } else {
545 try prev_cert.verify(subject, now_sec);
546 }
547494
548 if (ca_bundle.verify(subject, now_sec)) |_| {495 tls_version = @enumFromInt(supported_version orelse legacy_version);
549 handshake_state = .trust_chain_established;496 switch (tls_version) {
550 break :cert;497 .tls_1_3 => if (!mem.eql(u8, legacy_session_id_echo, &legacy_session_id)) return error.TlsIllegalParameter,
551 } else |err| switch (err) {498 .tls_1_2 => if (mem.eql(u8, server_hello_rand[24..31], "DOWNGRD") and
552 error.CertificateIssuerNotFound => {},499 server_hello_rand[31] >> 1 == 0x00) return error.TlsIllegalParameter,
553 else => |e| return e,500 else => return error.TlsIllegalParameter,
554 }501 }
555502
556 prev_cert = subject;503 switch (cipher_suite_tag) {
557 cert_index += 1;504 inline .AES_128_GCM_SHA256,
505 .AES_256_GCM_SHA384,
506 .CHACHA20_POLY1305_SHA256,
507 .AEGIS_256_SHA512,
508 .AEGIS_128L_SHA256,
509
510 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
511 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
512 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
513 => |tag| {
514 handshake_cipher = @unionInit(tls.HandshakeCipher, @tagName(tag.with()), .{
515 .transcript_hash = .init(.{}),
516 .version = undefined,
517 });
518 const p = &@field(handshake_cipher, @tagName(tag.with()));
519 p.transcript_hash.update(cleartext_header[tls.record_header_len..]); // Client Hello part 1
520 p.transcript_hash.update(host); // Client Hello part 2
521 p.transcript_hash.update(wrapped_handshake);
522 },
523
524 else => return error.TlsIllegalParameter,
525 }
526 switch (tls_version) {
527 .tls_1_3 => {
528 switch (cipher_suite_tag) {
529 inline .AES_128_GCM_SHA256,
530 .AES_256_GCM_SHA384,
531 .CHACHA20_POLY1305_SHA256,
532 .AEGIS_256_SHA512,
533 .AEGIS_128L_SHA256,
534 => |tag| {
535 const sk = key_share.getSharedSecret() orelse return error.TlsIllegalParameter;
536 const p = &@field(handshake_cipher, @tagName(tag.with()));
537 const P = @TypeOf(p.*).A;
538 const hello_hash = p.transcript_hash.peek();
539 const zeroes = [1]u8{0} ** P.Hash.digest_length;
540 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);
541 const empty_hash = tls.emptyHash(P.Hash);
542 p.version = .{ .tls_1_3 = undefined };
543 const pv = &p.version.tls_1_3;
544 const hs_derived_secret = hkdfExpandLabel(P.Hkdf, early_secret, "derived", &empty_hash, P.Hash.digest_length);
545 pv.handshake_secret = P.Hkdf.extract(&hs_derived_secret, sk);
546 const ap_derived_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "derived", &empty_hash, P.Hash.digest_length);
547 pv.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes);
548 const client_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length);
549 const server_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "s hs traffic", &hello_hash, P.Hash.digest_length);
550 if (options.ssl_key_log_file) |key_log_file| logSecrets(key_log_file, .{
551 .client_random = &client_hello_rand,
552 }, .{
553 .SERVER_HANDSHAKE_TRAFFIC_SECRET = &server_secret,
554 .CLIENT_HANDSHAKE_TRAFFIC_SECRET = &client_secret,
555 });
556 pv.client_finished_key = hkdfExpandLabel(P.Hkdf, client_secret, "finished", "", P.Hmac.key_length);
557 pv.server_finished_key = hkdfExpandLabel(P.Hkdf, server_secret, "finished", "", P.Hmac.key_length);
558 pv.client_handshake_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
559 pv.server_handshake_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
560 pv.client_handshake_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
561 pv.server_handshake_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
562 },
563 else => return error.TlsIllegalParameter,
564 }
565 pending_cipher_state = .handshake;
566 handshake_state = .encrypted_extensions;
567 },
568 .tls_1_2 => switch (cipher_suite_tag) {
569 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
570 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
571 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
572 => handshake_state = .certificate,
573 else => return error.TlsIllegalParameter,
574 },
575 else => return error.TlsIllegalParameter,
576 }
577 },
578 .encrypted_extensions => {
579 if (tls_version != .tls_1_3) return error.TlsUnexpectedMessage;
580 if (cipher_state != .handshake) return error.TlsUnexpectedMessage;
581 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;
582 switch (handshake_cipher) {
583 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
584 }
585 try hsd.ensure(2);
586 const total_ext_size = hsd.decode(u16);
587 var all_extd = try hsd.sub(total_ext_size);
588 while (!all_extd.eof()) {
589 try all_extd.ensure(4);
590 const et = all_extd.decode(tls.ExtensionType);
591 const ext_size = all_extd.decode(u16);
592 const extd = try all_extd.sub(ext_size);
593 _ = extd;
594 switch (et) {
595 .server_name => {},
596 else => {},
597 }
598 }
599 handshake_state = .certificate;
600 },
601 .certificate => cert: {
602 if (cipher_state == .application) return error.TlsUnexpectedMessage;
603 switch (handshake_state) {
604 .certificate => {},
605 .trust_chain_established => break :cert,
606 else => return error.TlsUnexpectedMessage,
607 }
608 switch (handshake_cipher) {
609 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
610 }
558611
612 switch (tls_version) {
613 .tls_1_3 => {
614 try hsd.ensure(1 + 3);
615 const cert_req_ctx_len = hsd.decode(u8);
616 if (cert_req_ctx_len != 0) return error.TlsIllegalParameter;
617 },
618 .tls_1_2 => try hsd.ensure(3),
619 else => unreachable,
620 }
621 const certs_size = hsd.decode(u24);
622 var certs_decoder = try hsd.sub(certs_size);
623 while (!certs_decoder.eof()) {
624 try certs_decoder.ensure(3);
625 const cert_size = certs_decoder.decode(u24);
626 const certd = try certs_decoder.sub(cert_size);
627
628 if (tls_version == .tls_1_3) {
559 try certs_decoder.ensure(2);629 try certs_decoder.ensure(2);
560 const total_ext_size = certs_decoder.decode(u16);630 const total_ext_size = certs_decoder.decode(u16);
561 const all_extd = try certs_decoder.sub(total_ext_size);631 const all_extd = try certs_decoder.sub(total_ext_size);
562 _ = all_extd;632 _ = all_extd;
563 }633 }
564 },
565 .certificate_verify => {
566 switch (handshake_state) {
567 .trust_chain_established => handshake_state = .finished,
568 .certificate => return error.TlsCertificateNotVerified,
569 else => return error.TlsUnexpectedMessage,
570 }
571634
572 try hsd.ensure(4);635 const subject_cert: Certificate = .{
573 const scheme = hsd.decode(tls.SignatureScheme);636 .buffer = certd.buf,
574 const sig_len = hsd.decode(u16);637 .index = @intCast(certd.idx),
575 try hsd.ensure(sig_len);
576 const encoded_sig = hsd.slice(sig_len);
577 const max_digest_len = 64;
578 var verify_buffer: [64 + 34 + max_digest_len]u8 =
579 ([1]u8{0x20} ** 64) ++
580 "TLS 1.3, server CertificateVerify\x00".* ++
581 @as([max_digest_len]u8, undefined);
582
583 const verify_bytes = switch (handshake_cipher) {
584 inline else => |*p| v: {
585 const transcript_digest = p.transcript_hash.peek();
586 verify_buffer[verify_buffer.len - max_digest_len ..][0..transcript_digest.len].* = transcript_digest;
587 p.transcript_hash.update(wrapped_handshake);
588 break :v verify_buffer[0 .. verify_buffer.len - max_digest_len + transcript_digest.len];
589 },
590 };638 };
591 const main_cert_pub_key = main_cert_pub_key_buf[0..main_cert_pub_key_len];639 const subject = try subject_cert.parse();
592640 if (cert_index == 0) {
593 switch (scheme) {641 // Verify the host on the first certificate.
594 inline .ecdsa_secp256r1_sha256,642 switch (options.host) {
595 .ecdsa_secp384r1_sha384,643 .no_verification => {},
596 => |comptime_scheme| {644 .explicit => try subject.verifyHostName(host),
597 if (main_cert_pub_key_algo != .X9_62_id_ecPublicKey)645 }
598 return error.TlsBadSignatureScheme;646
599 const Ecdsa = SchemeEcdsa(comptime_scheme);647 // Keep track of the public key for the
600 const sig = try Ecdsa.Signature.fromDer(encoded_sig);648 // certificate_verify message later.
601 const key = try Ecdsa.PublicKey.fromSec1(main_cert_pub_key);649 try main_cert_pub_key.init(subject.pub_key_algo, subject.pubKey());
602 try sig.verify(verify_bytes, key);650 } else {
603 },651 try prev_cert.verify(subject, now_sec);
604 inline .rsa_pss_rsae_sha256,652 }
605 .rsa_pss_rsae_sha384,653
606 .rsa_pss_rsae_sha512,654 switch (options.ca) {
607 => |comptime_scheme| {655 .no_verification => {
608 if (main_cert_pub_key_algo != .rsaEncryption)656 handshake_state = .trust_chain_established;
609 return error.TlsBadSignatureScheme;657 break :cert;
610
611 const Hash = SchemeHash(comptime_scheme);
612 const rsa = Certificate.rsa;
613 const components = try rsa.PublicKey.parseDer(main_cert_pub_key);
614 const exponent = components.exponent;
615 const modulus = components.modulus;
616 switch (modulus.len) {
617 inline 128, 256, 512 => |modulus_len| {
618 const key = try rsa.PublicKey.fromBytes(exponent, modulus);
619 const sig = rsa.PSSSignature.fromBytes(modulus_len, encoded_sig);
620 try rsa.PSSSignature.verify(modulus_len, sig, verify_bytes, key, Hash);
621 },
622 else => {
623 return error.TlsBadRsaSignatureBitCount;
624 },
625 }
626 },658 },
627 inline .ed25519 => |comptime_scheme| {659 .self_signed => {
628 if (main_cert_pub_key_algo != .curveEd25519) return error.TlsBadSignatureScheme;660 try subject.verify(subject, now_sec);
629 const Eddsa = SchemeEddsa(comptime_scheme);661 handshake_state = .trust_chain_established;
630 if (encoded_sig.len != Eddsa.Signature.encoded_length) return error.InvalidEncoding;662 break :cert;
631 const sig = Eddsa.Signature.fromBytes(encoded_sig[0..Eddsa.Signature.encoded_length].*);
632 if (main_cert_pub_key.len != Eddsa.PublicKey.encoded_length) return error.InvalidEncoding;
633 const key = try Eddsa.PublicKey.fromBytes(main_cert_pub_key[0..Eddsa.PublicKey.encoded_length].*);
634 try sig.verify(verify_bytes, key);
635 },663 },
636 else => {664 .bundle => |ca_bundle| if (ca_bundle.verify(subject, now_sec)) |_| {
637 return error.TlsBadSignatureScheme;665 handshake_state = .trust_chain_established;
666 break :cert;
667 } else |err| switch (err) {
668 error.CertificateIssuerNotFound => {},
669 else => |e| return e,
638 },670 },
639 }671 }
640 },672
641 .finished => {673 prev_cert = subject;
642 if (handshake_state != .finished) return error.TlsUnexpectedMessage;674 cert_index += 1;
643 // This message is to trick buggy proxies into behaving correctly.675 }
644 const client_change_cipher_spec_msg = [_]u8{676 cert_buf_index += 1;
645 @intFromEnum(tls.ContentType.change_cipher_spec),677 },
646 0x03, 0x03, // legacy protocol version678 .server_key_exchange => {
647 0x00, 0x01, // length679 if (tls_version != .tls_1_2) return error.TlsUnexpectedMessage;
648 0x01,680 if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
649 };681 switch (handshake_state) {
650 const app_cipher = switch (handshake_cipher) {682 .trust_chain_established => {},
651 inline else => |*p, tag| c: {683 .certificate => return error.TlsCertificateNotVerified,
652 const P = @TypeOf(p.*);684 else => return error.TlsUnexpectedMessage,
685 }
686
687 switch (handshake_cipher) {
688 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
689 }
690 try hsd.ensure(1 + 2 + 1);
691 const curve_type = hsd.decode(u8);
692 if (curve_type != 0x03) return error.TlsIllegalParameter; // named_curve
693 const named_group = hsd.decode(tls.NamedGroup);
694 const key_size = hsd.decode(u8);
695 try hsd.ensure(key_size);
696 const server_pub_key = hsd.slice(key_size);
697 try main_cert_pub_key.verifySignature(&hsd, &.{ &client_hello_rand, &server_hello_rand, hsd.buf[0..hsd.idx] });
698 try key_share.exchange(named_group, server_pub_key);
699 handshake_state = .server_hello_done;
700 },
701 .server_hello_done => {
702 if (tls_version != .tls_1_2) return error.TlsUnexpectedMessage;
703 if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
704 if (handshake_state != .server_hello_done) return error.TlsUnexpectedMessage;
705
706 const client_key_exchange_msg = .{@intFromEnum(tls.ContentType.handshake)} ++
707 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
708 array(u16, u8, .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++
709 array(u24, u8, array(u8, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1())));
710 const client_change_cipher_spec_msg = .{@intFromEnum(tls.ContentType.change_cipher_spec)} ++
711 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
712 array(u16, tls.ChangeCipherSpecType, .{.change_cipher_spec});
713 const pre_master_secret = key_share.getSharedSecret().?;
714 switch (handshake_cipher) {
715 inline else => |*p| {
716 const P = @TypeOf(p.*).A;
717 p.transcript_hash.update(wrapped_handshake);
718 p.transcript_hash.update(client_key_exchange_msg[tls.record_header_len..]);
719 const master_secret = hmacExpandLabel(P.Hmac, pre_master_secret, &.{
720 "master secret",
721 &client_hello_rand,
722 &server_hello_rand,
723 }, 48);
724 if (options.ssl_key_log_file) |key_log_file| logSecrets(key_log_file, .{
725 .client_random = &client_hello_rand,
726 }, .{
727 .CLIENT_RANDOM = &master_secret,
728 });
729 const key_block = hmacExpandLabel(
730 P.Hmac,
731 &master_secret,
732 &.{ "key expansion", &server_hello_rand, &client_hello_rand },
733 @sizeOf(P.Tls_1_2),
734 );
735 const client_verify_cleartext = .{@intFromEnum(tls.HandshakeType.finished)} ++
736 array(u24, u8, hmacExpandLabel(
737 P.Hmac,
738 &master_secret,
739 &.{ "client finished", &p.transcript_hash.peek() },
740 P.verify_data_length,
741 ));
742 p.transcript_hash.update(&client_verify_cleartext);
743 p.version = .{ .tls_1_2 = .{
744 .expected_server_verify_data = hmacExpandLabel(
745 P.Hmac,
746 &master_secret,
747 &.{ "server finished", &p.transcript_hash.finalResult() },
748 P.verify_data_length,
749 ),
750 .app_cipher = std.mem.bytesToValue(P.Tls_1_2, &key_block),
751 } };
752 const pv = &p.version.tls_1_2;
753 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
754 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
755 nonce: {
756 var nonce = pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt;
757 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
758 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ write_seq, .big);
759 break :nonce nonce;
760 } else nonce: {
761 const V = @Vector(P.AEAD.nonce_length, u8);
762 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
763 const operand: V = pad ++ @as([8]u8, @bitCast(big(write_seq)));
764 break :nonce @as(V, pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt) ^ operand;
765 };
766 var client_verify_msg = .{@intFromEnum(tls.ContentType.handshake)} ++
767 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
768 array(u16, u8, nonce[P.fixed_iv_length..].* ++
769 @as([client_verify_cleartext.len + P.mac_length]u8, undefined));
770 P.AEAD.encrypt(
771 client_verify_msg[client_verify_msg.len - P.mac_length -
772 client_verify_cleartext.len ..][0..client_verify_cleartext.len],
773 client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length],
774 &client_verify_cleartext,
775 std.mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int(u16, client_verify_cleartext.len),
776 nonce,
777 pv.app_cipher.client_write_key,
778 );
779 const all_msgs = client_key_exchange_msg ++ client_change_cipher_spec_msg ++ client_verify_msg;
780 var all_msgs_vec = [_]std.posix.iovec_const{
781 .{ .base = &all_msgs, .len = all_msgs.len },
782 };
783 try stream.writevAll(&all_msgs_vec);
784 },
785 }
786 write_seq += 1;
787 pending_cipher_state = .application;
788 handshake_state = .finished;
789 },
790 .certificate_verify => {
791 if (tls_version != .tls_1_3) return error.TlsUnexpectedMessage;
792 if (cipher_state != .handshake) return error.TlsUnexpectedMessage;
793 switch (handshake_state) {
794 .trust_chain_established => {},
795 .certificate => return error.TlsCertificateNotVerified,
796 else => return error.TlsUnexpectedMessage,
797 }
798 switch (handshake_cipher) {
799 inline else => |*p| {
800 try main_cert_pub_key.verifySignature(&hsd, &.{
801 " " ** 64 ++ "TLS 1.3, server CertificateVerify\x00",
802 &p.transcript_hash.peek(),
803 });
804 p.transcript_hash.update(wrapped_handshake);
805 },
806 }
807 handshake_state = .finished;
808 },
809 .finished => {
810 if (cipher_state == .cleartext) return error.TlsUnexpectedMessage;
811 if (handshake_state != .finished) return error.TlsUnexpectedMessage;
812 // This message is to trick buggy proxies into behaving correctly.
813 const client_change_cipher_spec_msg = .{@intFromEnum(tls.ContentType.change_cipher_spec)} ++
814 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
815 array(u16, tls.ChangeCipherSpecType, .{.change_cipher_spec});
816 const app_cipher = app_cipher: switch (handshake_cipher) {
817 inline else => |*p, tag| switch (tls_version) {
818 .tls_1_3 => {
819 const pv = &p.version.tls_1_3;
820 const P = @TypeOf(p.*).A;
821 try hsd.ensure(P.Hmac.mac_length);
653 const finished_digest = p.transcript_hash.peek();822 const finished_digest = p.transcript_hash.peek();
654 p.transcript_hash.update(wrapped_handshake);823 p.transcript_hash.update(wrapped_handshake);
655 const expected_server_verify_data = tls.hmac(P.Hmac, &finished_digest, p.server_finished_key);824 const expected_server_verify_data = tls.hmac(P.Hmac, &finished_digest, pv.server_finished_key);
656 if (!mem.eql(u8, &expected_server_verify_data, handshake))825 if (!std.crypto.timing_safe.eql([P.Hmac.mac_length]u8, expected_server_verify_data, hsd.array(P.Hmac.mac_length).*)) return error.TlsDecryptError;
657 return error.TlsDecryptError;
658 const handshake_hash = p.transcript_hash.finalResult();826 const handshake_hash = p.transcript_hash.finalResult();
659 const verify_data = tls.hmac(P.Hmac, &handshake_hash, p.client_finished_key);827 const verify_data = tls.hmac(P.Hmac, &handshake_hash, pv.client_finished_key);
660 const out_cleartext = [_]u8{828 const out_cleartext = .{@intFromEnum(tls.HandshakeType.finished)} ++
661 @intFromEnum(tls.HandshakeType.finished),829 array(u24, u8, verify_data) ++
662 0, 0, verify_data.len, // length830 .{@intFromEnum(tls.ContentType.handshake)};
663 } ++ verify_data ++ [1]u8{@intFromEnum(tls.ContentType.handshake)};
664831
665 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;832 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;
666833
667 var finished_msg = [_]u8{834 var finished_msg = .{@intFromEnum(tls.ContentType.application_data)} ++
668 @intFromEnum(tls.ContentType.application_data),835 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
669 0x03, 0x03, // legacy protocol version836 array(u16, u8, @as([wrapped_len]u8, undefined));
670 0, wrapped_len, // byte length of encrypted record
671 } ++ @as([wrapped_len]u8, undefined);
672837
673 const ad = finished_msg[0..5];838 const ad = finished_msg[0..tls.record_header_len];
674 const ciphertext = finished_msg[5..][0..out_cleartext.len];839 const ciphertext = finished_msg[tls.record_header_len..][0..out_cleartext.len];
675 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];840 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];
676 const nonce = p.client_handshake_iv;841 const nonce = pv.client_handshake_iv;
677 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);842 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, pv.client_handshake_key);
678843
679 const both_msgs = client_change_cipher_spec_msg ++ finished_msg;844 const all_msgs = client_change_cipher_spec_msg ++ finished_msg;
680 var both_msgs_vec = [_]std.posix.iovec_const{.{845 var all_msgs_vec = [_]std.posix.iovec_const{
681 .base = &both_msgs,846 .{ .base = &all_msgs, .len = all_msgs.len },
682 .len = both_msgs.len,847 };
683 }};848 try stream.writevAll(&all_msgs_vec);
684 try stream.writevAll(&both_msgs_vec);849
685850 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
686 const client_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);851 const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);
687 const server_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);852 if (options.ssl_key_log_file) |key_log_file| logSecrets(key_log_file, .{
688 break :c @unionInit(tls.ApplicationCipher, @tagName(tag), .{853 .counter = key_seq,
854 .client_random = &client_hello_rand,
855 }, .{
856 .SERVER_TRAFFIC_SECRET = &server_secret,
857 .CLIENT_TRAFFIC_SECRET = &client_secret,
858 });
859 key_seq += 1;
860 break :app_cipher @unionInit(tls.ApplicationCipher, @tagName(tag), .{ .tls_1_3 = .{
689 .client_secret = client_secret,861 .client_secret = client_secret,
690 .server_secret = server_secret,862 .server_secret = server_secret,
691 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),863 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),
692 .server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length),864 .server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length),
693 .client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length),865 .client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length),
694 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),866 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),
695 });867 } });
696 },868 },
697 };869 .tls_1_2 => {
698 const leftover = d.rest();870 const pv = &p.version.tls_1_2;
699 var client: Client = .{871 const P = @TypeOf(p.*).A;
700 .read_seq = 0,872 try hsd.ensure(P.verify_data_length);
701 .write_seq = 0,873 if (!std.crypto.timing_safe.eql([P.verify_data_length]u8, pv.expected_server_verify_data, hsd.array(P.verify_data_length).*)) return error.TlsDecryptError;
702 .partial_cleartext_idx = 0,874 break :app_cipher @unionInit(tls.ApplicationCipher, @tagName(tag), .{ .tls_1_2 = pv.app_cipher });
703 .partial_ciphertext_idx = 0,875 },
704 .partial_ciphertext_end = @intCast(leftover.len),876 else => unreachable,
705 .received_close_notify = false,877 },
706 .application_cipher = app_cipher,878 };
707 .partially_read_buffer = undefined,879 const leftover = d.rest();
708 };880 var client: Client = .{
709 @memcpy(client.partially_read_buffer[0..leftover.len], leftover);881 .tls_version = tls_version,
710 return client;882 .read_seq = switch (tls_version) {
711 },883 .tls_1_3 => 0,
712 else => {884 .tls_1_2 => read_seq,
713 return error.TlsUnexpectedMessage;885 else => unreachable,
714 },886 },
715 }887 .write_seq = switch (tls_version) {
716 if (ctd.eof()) break;888 .tls_1_3 => 0,
889 .tls_1_2 => write_seq,
890 else => unreachable,
891 },
892 .partial_cleartext_idx = 0,
893 .partial_ciphertext_idx = 0,
894 .partial_ciphertext_end = @intCast(leftover.len),
895 .received_close_notify = false,
896 .allow_truncation_attacks = false,
897 .application_cipher = app_cipher,
898 .partially_read_buffer = undefined,
899 .ssl_key_log = if (options.ssl_key_log_file) |key_log_file| .{
900 .client_key_seq = key_seq,
901 .server_key_seq = key_seq,
902 .client_random = client_hello_rand,
903 .file = key_log_file,
904 } else null,
905 };
906 @memcpy(client.partially_read_buffer[0..leftover.len], leftover);
907 return client;
908 },
909 else => return error.TlsUnexpectedMessage,
717 }910 }
911 if (ctd.eof()) break;
912 cleartext_fragment_start = ctd.idx;
718 },913 },
719 else => {914 else => return error.TlsUnexpectedMessage,
720 return error.TlsUnexpectedMessage;
721 },
722 }915 }
916 cleartext_fragment_start = 0;
917 cleartext_fragment_end = 0;
723 }918 }
724}919}
725920
726/// Sends TLS-encrypted data to `stream`, which must conform to `StreamInterface`.921/// Sends TLS-encrypted data to `stream`, which must conform to `StreamInterface`.
727/// Returns the number of plaintext bytes sent, which may be fewer than `bytes.len`.922/// Returns the number of cleartext bytes sent, which may be fewer than `bytes.len`.
728pub fn write(c: *Client, stream: anytype, bytes: []const u8) !usize {923pub fn write(c: *Client, stream: anytype, bytes: []const u8) !usize {
729 return writeEnd(c, stream, bytes, false);924 return writeEnd(c, stream, bytes, false);
730}925}
...@@ -749,7 +944,7 @@ pub fn writeAllEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !v...@@ -749,7 +944,7 @@ pub fn writeAllEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !v
749}944}
750945
751/// Sends TLS-encrypted data to `stream`, which must conform to `StreamInterface`.946/// Sends TLS-encrypted data to `stream`, which must conform to `StreamInterface`.
752/// Returns the number of plaintext bytes sent, which may be fewer than `bytes.len`.947/// Returns the number of cleartext bytes sent, which may be fewer than `bytes.len`.
753/// If `end` is true, then this function additionally sends a `close_notify` alert,948/// If `end` is true, then this function additionally sends a `close_notify` alert,
754/// which is necessary for the server to distinguish between a properly finished949/// which is necessary for the server to distinguish between a properly finished
755/// TLS session, or a truncation attack.950/// TLS session, or a truncation attack.
...@@ -813,62 +1008,126 @@ fn prepareCiphertextRecord(...@@ -813,62 +1008,126 @@ fn prepareCiphertextRecord(
813 var iovec_end: usize = 0;1008 var iovec_end: usize = 0;
814 var bytes_i: usize = 0;1009 var bytes_i: usize = 0;
815 switch (c.application_cipher) {1010 switch (c.application_cipher) {
816 inline else => |*p| {1011 inline else => |*p| switch (c.tls_version) {
817 const P = @TypeOf(p.*);1012 .tls_1_3 => {
818 const overhead_len = tls.record_header_len + P.AEAD.tag_length + 1;1013 const pv = &p.tls_1_3;
819 const close_notify_alert_reserved = tls.close_notify_alert.len + overhead_len;1014 const P = @TypeOf(p.*);
820 while (true) {1015 const overhead_len = tls.record_header_len + P.AEAD.tag_length + 1;
821 const encrypted_content_len: u16 = @intCast(@min(1016 const close_notify_alert_reserved = tls.close_notify_alert.len + overhead_len;
822 @min(bytes.len - bytes_i, tls.max_ciphertext_inner_record_len),1017 while (true) {
823 ciphertext_buf.len -|1018 const encrypted_content_len: u16 = @min(
824 (close_notify_alert_reserved + overhead_len + ciphertext_end),1019 bytes.len - bytes_i,
825 ));1020 tls.max_ciphertext_inner_record_len,
826 if (encrypted_content_len == 0) return .{1021 ciphertext_buf.len -|
827 .iovec_end = iovec_end,1022 (close_notify_alert_reserved + overhead_len + ciphertext_end),
828 .ciphertext_end = ciphertext_end,1023 );
829 .overhead_len = overhead_len,1024 if (encrypted_content_len == 0) return .{
830 };1025 .iovec_end = iovec_end,
8311026 .ciphertext_end = ciphertext_end,
832 @memcpy(cleartext_buf[0..encrypted_content_len], bytes[bytes_i..][0..encrypted_content_len]);1027 .overhead_len = overhead_len,
833 cleartext_buf[encrypted_content_len] = @intFromEnum(inner_content_type);1028 };
834 bytes_i += encrypted_content_len;1029
835 const ciphertext_len = encrypted_content_len + 1;1030 @memcpy(cleartext_buf[0..encrypted_content_len], bytes[bytes_i..][0..encrypted_content_len]);
836 const cleartext = cleartext_buf[0..ciphertext_len];1031 cleartext_buf[encrypted_content_len] = @intFromEnum(inner_content_type);
8371032 bytes_i += encrypted_content_len;
838 const record_start = ciphertext_end;1033 const ciphertext_len = encrypted_content_len + 1;
839 const ad = ciphertext_buf[ciphertext_end..][0..5];1034 const cleartext = cleartext_buf[0..ciphertext_len];
840 ad.* =1035
841 [_]u8{@intFromEnum(tls.ContentType.application_data)} ++1036 const record_start = ciphertext_end;
842 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++1037 const ad = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];
843 int2(ciphertext_len + P.AEAD.tag_length);1038 ad.* = .{@intFromEnum(tls.ContentType.application_data)} ++
844 ciphertext_end += ad.len;1039 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
845 const ciphertext = ciphertext_buf[ciphertext_end..][0..ciphertext_len];1040 int(u16, ciphertext_len + P.AEAD.tag_length);
846 ciphertext_end += ciphertext_len;1041 ciphertext_end += ad.len;
847 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.AEAD.tag_length];1042 const ciphertext = ciphertext_buf[ciphertext_end..][0..ciphertext_len];
848 ciphertext_end += auth_tag.len;1043 ciphertext_end += ciphertext_len;
849 const nonce = if (builtin.zig_backend == .stage2_x86_64 and1044 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.AEAD.tag_length];
850 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)1045 ciphertext_end += auth_tag.len;
851 nonce: {1046 const nonce = if (builtin.zig_backend == .stage2_x86_64 and
852 var nonce = p.client_iv;1047 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
853 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);1048 nonce: {
854 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.write_seq, .big);1049 var nonce = pv.client_iv;
855 break :nonce nonce;1050 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
856 } else nonce: {1051 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.write_seq, .big);
857 const V = @Vector(P.AEAD.nonce_length, u8);1052 break :nonce nonce;
858 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);1053 } else nonce: {
859 const operand: V = pad ++ @as([8]u8, @bitCast(big(c.write_seq)));1054 const V = @Vector(P.AEAD.nonce_length, u8);
860 break :nonce @as(V, p.client_iv) ^ operand;1055 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
861 };1056 const operand: V = pad ++ std.mem.toBytes(big(c.write_seq));
862 c.write_seq += 1; // TODO send key_update on overflow1057 break :nonce @as(V, pv.client_iv) ^ operand;
863 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, p.client_key);1058 };
8641059 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_key);
865 const record = ciphertext_buf[record_start..ciphertext_end];1060 c.write_seq += 1; // TODO send key_update on overflow
866 iovecs[iovec_end] = .{1061
867 .base = record.ptr,1062 const record = ciphertext_buf[record_start..ciphertext_end];
868 .len = record.len,1063 iovecs[iovec_end] = .{
869 };1064 .base = record.ptr,
870 iovec_end += 1;1065 .len = record.len,
871 }1066 };
1067 iovec_end += 1;
1068 }
1069 },
1070 .tls_1_2 => {
1071 const pv = &p.tls_1_2;
1072 const P = @TypeOf(p.*);
1073 const overhead_len = tls.record_header_len + P.record_iv_length + P.mac_length;
1074 const close_notify_alert_reserved = tls.close_notify_alert.len + overhead_len;
1075 while (true) {
1076 const message_len: u16 = @min(
1077 bytes.len - bytes_i,
1078 tls.max_ciphertext_inner_record_len,
1079 ciphertext_buf.len -|
1080 (close_notify_alert_reserved + overhead_len + ciphertext_end),
1081 );
1082 if (message_len == 0) return .{
1083 .iovec_end = iovec_end,
1084 .ciphertext_end = ciphertext_end,
1085 .overhead_len = overhead_len,
1086 };
1087
1088 @memcpy(cleartext_buf[0..message_len], bytes[bytes_i..][0..message_len]);
1089 bytes_i += message_len;
1090 const cleartext = cleartext_buf[0..message_len];
1091
1092 const record_start = ciphertext_end;
1093 const record_header = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];
1094 ciphertext_end += tls.record_header_len;
1095 record_header.* = .{@intFromEnum(inner_content_type)} ++
1096 int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
1097 int(u16, P.record_iv_length + message_len + P.mac_length);
1098 const ad = std.mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int(u16, message_len);
1099 const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length];
1100 ciphertext_end += P.record_iv_length;
1101 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
1102 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
1103 nonce: {
1104 var nonce = pv.client_write_IV ++ pv.client_salt;
1105 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
1106 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.write_seq, .big);
1107 break :nonce nonce;
1108 } else nonce: {
1109 const V = @Vector(P.AEAD.nonce_length, u8);
1110 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1111 const operand: V = pad ++ @as([8]u8, @bitCast(big(c.write_seq)));
1112 break :nonce @as(V, pv.client_write_IV ++ pv.client_salt) ^ operand;
1113 };
1114 record_iv.* = nonce[P.fixed_iv_length..].*;
1115 const ciphertext = ciphertext_buf[ciphertext_end..][0..message_len];
1116 ciphertext_end += message_len;
1117 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.mac_length];
1118 ciphertext_end += P.mac_length;
1119 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_write_key);
1120 c.write_seq += 1; // TODO send key_update on overflow
1121
1122 const record = ciphertext_buf[record_start..ciphertext_end];
1123 iovecs[iovec_end] = .{
1124 .base = record.ptr,
1125 .len = record.len,
1126 };
1127 iovec_end += 1;
1128 }
1129 },
1130 else => unreachable,
872 },1131 },
873 }1132 }
874}1133}
...@@ -990,7 +1249,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove...@@ -990,7 +1249,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove
990 // beginning of the buffer will be used for such purposes.1249 // beginning of the buffer will be used for such purposes.
991 const cleartext_buf_len = free_size - ciphertext_buf_len;1250 const cleartext_buf_len = free_size - ciphertext_buf_len;
9921251
993 // Recoup `partially_read_buffer space`. This is necessary because it is assumed1252 // Recoup `partially_read_buffer` space. This is necessary because it is assumed
994 // below that `frag0` is big enough to hold at least one record.1253 // below that `frag0` is big enough to hold at least one record.
995 limitedOverlapCopy(c.partially_read_buffer[0..c.partial_ciphertext_end], c.partial_ciphertext_idx);1254 limitedOverlapCopy(c.partially_read_buffer[0..c.partial_ciphertext_end], c.partial_ciphertext_idx);
996 c.partial_ciphertext_end -= c.partial_ciphertext_idx;1255 c.partial_ciphertext_end -= c.partial_ciphertext_idx;
...@@ -1105,164 +1364,211 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove...@@ -1105,164 +1364,211 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove
1105 in = 0;1364 in = 0;
1106 continue;1365 continue;
1107 }1366 }
1108 switch (ct) {1367 const cleartext, const inner_ct: tls.ContentType = cleartext: switch (c.application_cipher) {
1368 inline else => |*p| switch (c.tls_version) {
1369 .tls_1_3 => {
1370 const pv = &p.tls_1_3;
1371 const P = @TypeOf(p.*);
1372 const ad = frag[in - tls.record_header_len ..][0..tls.record_header_len];
1373 const ciphertext_len = record_len - P.AEAD.tag_length;
1374 const ciphertext = frag[in..][0..ciphertext_len];
1375 in += ciphertext_len;
1376 const auth_tag = frag[in..][0..P.AEAD.tag_length].*;
1377 const nonce = if (builtin.zig_backend == .stage2_x86_64 and
1378 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
1379 nonce: {
1380 var nonce = pv.server_iv;
1381 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
1382 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.read_seq, .big);
1383 break :nonce nonce;
1384 } else nonce: {
1385 const V = @Vector(P.AEAD.nonce_length, u8);
1386 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1387 const operand: V = pad ++ std.mem.toBytes(big(c.read_seq));
1388 break :nonce @as(V, pv.server_iv) ^ operand;
1389 };
1390 const out_buf = vp.peek();
1391 const cleartext_buf = if (ciphertext.len <= out_buf.len)
1392 out_buf
1393 else
1394 &cleartext_stack_buffer;
1395 const cleartext = cleartext_buf[0..ciphertext.len];
1396 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch
1397 return error.TlsBadRecordMac;
1398 const msg = mem.trimRight(u8, cleartext, "\x00");
1399 break :cleartext .{ msg[0 .. msg.len - 1], @enumFromInt(msg[msg.len - 1]) };
1400 },
1401 .tls_1_2 => {
1402 const pv = &p.tls_1_2;
1403 const P = @TypeOf(p.*);
1404 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
1405 const ad = std.mem.toBytes(big(c.read_seq)) ++
1406 frag[in - tls.record_header_len ..][0 .. 1 + 2] ++
1407 std.mem.toBytes(big(message_len));
1408 const record_iv = frag[in..][0..P.record_iv_length].*;
1409 in += P.record_iv_length;
1410 const masked_read_seq = c.read_seq &
1411 comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length);
1412 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
1413 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
1414 nonce: {
1415 var nonce = pv.server_write_IV ++ record_iv;
1416 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
1417 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ masked_read_seq, .big);
1418 break :nonce nonce;
1419 } else nonce: {
1420 const V = @Vector(P.AEAD.nonce_length, u8);
1421 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1422 const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq)));
1423 break :nonce @as(V, pv.server_write_IV ++ record_iv) ^ operand;
1424 };
1425 const ciphertext = frag[in..][0..message_len];
1426 in += message_len;
1427 const auth_tag = frag[in..][0..P.mac_length].*;
1428 in += P.mac_length;
1429 const out_buf = vp.peek();
1430 const cleartext_buf = if (message_len <= out_buf.len)
1431 out_buf
1432 else
1433 &cleartext_stack_buffer;
1434 const cleartext = cleartext_buf[0..ciphertext.len];
1435 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch
1436 return error.TlsBadRecordMac;
1437 break :cleartext .{ cleartext, ct };
1438 },
1439 else => unreachable,
1440 },
1441 };
1442 c.read_seq = try std.math.add(u64, c.read_seq, 1);
1443 switch (inner_ct) {
1109 .alert => {1444 .alert => {
1110 if (in + 2 > frag.len) return error.TlsDecodeError;1445 if (cleartext.len != 2) return error.TlsDecodeError;
1111 const level: tls.AlertLevel = @enumFromInt(frag[in]);1446 const level: tls.AlertLevel = @enumFromInt(cleartext[0]);
1112 const desc: tls.AlertDescription = @enumFromInt(frag[in + 1]);1447 const desc: tls.AlertDescription = @enumFromInt(cleartext[1]);
1448 if (desc == .close_notify) {
1449 c.received_close_notify = true;
1450 c.partial_ciphertext_end = c.partial_ciphertext_idx;
1451 return vp.total;
1452 }
1113 _ = level;1453 _ = level;
11141454
1115 try desc.toError();1455 try desc.toError();
1116 // TODO: handle server-side closures1456 // TODO: handle server-side closures
1117 return error.TlsUnexpectedMessage;1457 return error.TlsUnexpectedMessage;
1118 },1458 },
1119 .application_data => {1459 .handshake => {
1120 const cleartext = switch (c.application_cipher) {1460 var ct_i: usize = 0;
1121 inline else => |*p| c: {1461 while (true) {
1122 const P = @TypeOf(p.*);1462 const handshake_type: tls.HandshakeType = @enumFromInt(cleartext[ct_i]);
1123 const ad = frag[in - 5 ..][0..5];1463 ct_i += 1;
1124 const ciphertext_len = record_len - P.AEAD.tag_length;1464 const handshake_len = mem.readInt(u24, cleartext[ct_i..][0..3], .big);
1125 const ciphertext = frag[in..][0..ciphertext_len];1465 ct_i += 3;
1126 in += ciphertext_len;1466 const next_handshake_i = ct_i + handshake_len;
1127 const auth_tag = frag[in..][0..P.AEAD.tag_length].*;1467 if (next_handshake_i > cleartext.len)
1128 const nonce = if (builtin.zig_backend == .stage2_x86_64 and1468 return error.TlsBadLength;
1129 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)1469 const handshake = cleartext[ct_i..next_handshake_i];
1130 nonce: {1470 switch (handshake_type) {
1131 var nonce = p.server_iv;1471 .new_session_ticket => {
1132 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);1472 // This client implementation ignores new session tickets.
1133 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.read_seq, .big);1473 },
1134 break :nonce nonce;1474 .key_update => {
1135 } else nonce: {1475 switch (c.application_cipher) {
1136 const V = @Vector(P.AEAD.nonce_length, u8);1476 inline else => |*p| {
1137 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);1477 const pv = &p.tls_1_3;
1138 const operand: V = pad ++ @as([8]u8, @bitCast(big(c.read_seq)));1478 const P = @TypeOf(p.*);
1139 break :nonce @as(V, p.server_iv) ^ operand;1479 const server_secret = hkdfExpandLabel(P.Hkdf, pv.server_secret, "traffic upd", "", P.Hash.digest_length);
1140 };1480 if (c.ssl_key_log) |*key_log| logSecrets(key_log.file, .{
1141 const out_buf = vp.peek();1481 .counter = key_log.serverCounter(),
1142 const cleartext_buf = if (ciphertext.len <= out_buf.len)1482 .client_random = &key_log.client_random,
1143 out_buf1483 }, .{
1144 else1484 .SERVER_TRAFFIC_SECRET = &server_secret,
1145 &cleartext_stack_buffer;1485 });
1146 const cleartext = cleartext_buf[0..ciphertext.len];1486 pv.server_secret = server_secret;
1147 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_key) catch1487 pv.server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
1148 return error.TlsBadRecordMac;1488 pv.server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
1149 break :c mem.trimRight(u8, cleartext, "\x00");
1150 },
1151 };
1152
1153 c.read_seq = try std.math.add(u64, c.read_seq, 1);
1154
1155 const inner_ct: tls.ContentType = @enumFromInt(cleartext[cleartext.len - 1]);
1156 switch (inner_ct) {
1157 .alert => {
1158 const level: tls.AlertLevel = @enumFromInt(cleartext[0]);
1159 const desc: tls.AlertDescription = @enumFromInt(cleartext[1]);
1160 if (desc == .close_notify) {
1161 c.received_close_notify = true;
1162 c.partial_ciphertext_end = c.partial_ciphertext_idx;
1163 return vp.total;
1164 }
1165 _ = level;
1166
1167 try desc.toError();
1168 // TODO: handle server-side closures
1169 return error.TlsUnexpectedMessage;
1170 },
1171 .handshake => {
1172 var ct_i: usize = 0;
1173 while (true) {
1174 const handshake_type: tls.HandshakeType = @enumFromInt(cleartext[ct_i]);
1175 ct_i += 1;
1176 const handshake_len = mem.readInt(u24, cleartext[ct_i..][0..3], .big);
1177 ct_i += 3;
1178 const next_handshake_i = ct_i + handshake_len;
1179 if (next_handshake_i > cleartext.len - 1)
1180 return error.TlsBadLength;
1181 const handshake = cleartext[ct_i..next_handshake_i];
1182 switch (handshake_type) {
1183 .new_session_ticket => {
1184 // This client implementation ignores new session tickets.
1185 },1489 },
1186 .key_update => {1490 }
1491 c.read_seq = 0;
1492
1493 switch (@as(tls.KeyUpdateRequest, @enumFromInt(handshake[0]))) {
1494 .update_requested => {
1187 switch (c.application_cipher) {1495 switch (c.application_cipher) {
1188 inline else => |*p| {1496 inline else => |*p| {
1497 const pv = &p.tls_1_3;
1189 const P = @TypeOf(p.*);1498 const P = @TypeOf(p.*);
1190 const server_secret = hkdfExpandLabel(P.Hkdf, p.server_secret, "traffic upd", "", P.Hash.digest_length);1499 const client_secret = hkdfExpandLabel(P.Hkdf, pv.client_secret, "traffic upd", "", P.Hash.digest_length);
1191 p.server_secret = server_secret;1500 if (c.ssl_key_log) |*key_log| logSecrets(key_log.file, .{
1192 p.server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);1501 .counter = key_log.clientCounter(),
1193 p.server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);1502 .client_random = &key_log.client_random,
1503 }, .{
1504 .CLIENT_TRAFFIC_SECRET = &client_secret,
1505 });
1506 pv.client_secret = client_secret;
1507 pv.client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
1508 pv.client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
1194 },1509 },
1195 }1510 }
1196 c.read_seq = 0;1511 c.write_seq = 0;
1197
1198 switch (@as(tls.KeyUpdateRequest, @enumFromInt(handshake[0]))) {
1199 .update_requested => {
1200 switch (c.application_cipher) {
1201 inline else => |*p| {
1202 const P = @TypeOf(p.*);
1203 const client_secret = hkdfExpandLabel(P.Hkdf, p.client_secret, "traffic upd", "", P.Hash.digest_length);
1204 p.client_secret = client_secret;
1205 p.client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
1206 p.client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
1207 },
1208 }
1209 c.write_seq = 0;
1210 },
1211 .update_not_requested => {},
1212 _ => return error.TlsIllegalParameter,
1213 }
1214 },
1215 else => {
1216 return error.TlsUnexpectedMessage;
1217 },1512 },
1513 .update_not_requested => {},
1514 _ => return error.TlsIllegalParameter,
1218 }1515 }
1219 ct_i = next_handshake_i;1516 },
1220 if (ct_i >= cleartext.len - 1) break;1517 else => {
1221 }1518 return error.TlsUnexpectedMessage;
1222 },1519 },
1223 .application_data => {1520 }
1224 // Determine whether the output buffer or a stack1521 ct_i = next_handshake_i;
1225 // buffer was used for storing the cleartext.1522 if (ct_i >= cleartext.len) break;
1226 if (cleartext.ptr == &cleartext_stack_buffer) {
1227 // Stack buffer was used, so we must copy to the output buffer.
1228 const msg = cleartext[0 .. cleartext.len - 1];
1229 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {
1230 // We have already run out of room in iovecs. Continue
1231 // appending to `partially_read_buffer`.
1232 @memcpy(
1233 c.partially_read_buffer[c.partial_ciphertext_idx..][0..msg.len],
1234 msg,
1235 );
1236 c.partial_ciphertext_idx = @intCast(c.partial_ciphertext_idx + msg.len);
1237 } else {
1238 const amt = vp.put(msg);
1239 if (amt < msg.len) {
1240 const rest = msg[amt..];
1241 c.partial_cleartext_idx = 0;
1242 c.partial_ciphertext_idx = @intCast(rest.len);
1243 @memcpy(c.partially_read_buffer[0..rest.len], rest);
1244 }
1245 }
1246 } else {
1247 // Output buffer was used directly which means no
1248 // memory copying needs to occur, and we can move
1249 // on to the next ciphertext record.
1250 vp.next(cleartext.len - 1);
1251 }
1252 },
1253 else => {
1254 return error.TlsUnexpectedMessage;
1255 },
1256 }1523 }
1257 },1524 },
1258 else => {1525 .application_data => {
1259 return error.TlsUnexpectedMessage;1526 // Determine whether the output buffer or a stack
1527 // buffer was used for storing the cleartext.
1528 if (cleartext.ptr == &cleartext_stack_buffer) {
1529 // Stack buffer was used, so we must copy to the output buffer.
1530 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {
1531 // We have already run out of room in iovecs. Continue
1532 // appending to `partially_read_buffer`.
1533 @memcpy(
1534 c.partially_read_buffer[c.partial_ciphertext_idx..][0..cleartext.len],
1535 cleartext,
1536 );
1537 c.partial_ciphertext_idx = @intCast(c.partial_ciphertext_idx + cleartext.len);
1538 } else {
1539 const amt = vp.put(cleartext);
1540 if (amt < cleartext.len) {
1541 const rest = cleartext[amt..];
1542 c.partial_cleartext_idx = 0;
1543 c.partial_ciphertext_idx = @intCast(rest.len);
1544 @memcpy(c.partially_read_buffer[0..rest.len], rest);
1545 }
1546 }
1547 } else {
1548 // Output buffer was used directly which means no
1549 // memory copying needs to occur, and we can move
1550 // on to the next ciphertext record.
1551 vp.next(cleartext.len);
1552 }
1260 },1553 },
1554 else => return error.TlsUnexpectedMessage,
1261 }1555 }
1262 in = end;1556 in = end;
1263 }1557 }
1264}1558}
12651559
1560fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void {
1561 const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false;
1562 defer if (locked) key_log_file.unlock();
1563 key_log_file.seekFromEnd(0) catch {};
1564 inline for (@typeInfo(@TypeOf(secrets)).@"struct".fields) |field| key_log_file.writer().print("{s}" ++
1565 (if (@hasField(@TypeOf(context), "counter")) "_{d}" else "") ++ " {} {}\n", .{field.name} ++
1566 (if (@hasField(@TypeOf(context), "counter")) .{context.counter} else .{}) ++ .{
1567 std.fmt.fmtSliceHexLower(context.client_random),
1568 std.fmt.fmtSliceHexLower(@field(secrets, field.name)),
1569 }) catch {};
1570}
1571
1266fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize {1572fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize {
1267 const saved_buf = frag[in..];1573 const saved_buf = frag[in..];
1268 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {1574 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {
...@@ -1326,6 +1632,86 @@ inline fn big(x: anytype) @TypeOf(x) {...@@ -1326,6 +1632,86 @@ inline fn big(x: anytype) @TypeOf(x) {
1326 };1632 };
1327}1633}
13281634
1635const KeyShare = struct {
1636 ml_kem768_kp: crypto.kem.ml_kem.MLKem768.KeyPair,
1637 secp256r1_kp: crypto.sign.ecdsa.EcdsaP256Sha256.KeyPair,
1638 secp384r1_kp: crypto.sign.ecdsa.EcdsaP384Sha384.KeyPair,
1639 x25519_kp: crypto.dh.X25519.KeyPair,
1640 sk_buf: [sk_max_len]u8,
1641 sk_len: std.math.IntFittingRange(0, sk_max_len),
1642
1643 const sk_max_len = @max(
1644 crypto.dh.X25519.shared_length + crypto.kem.ml_kem.MLKem768.shared_length,
1645 crypto.ecc.P256.scalar.encoded_length,
1646 crypto.ecc.P384.scalar.encoded_length,
1647 crypto.dh.X25519.shared_length,
1648 );
1649
1650 fn init(seed: [112]u8) error{IdentityElement}!KeyShare {
1651 return .{
1652 .ml_kem768_kp = try .create(null),
1653 .secp256r1_kp = try .create(seed[0..32].*),
1654 .secp384r1_kp = try .create(seed[32..80].*),
1655 .x25519_kp = try .create(seed[80..112].*),
1656 .sk_buf = undefined,
1657 .sk_len = 0,
1658 };
1659 }
1660
1661 fn exchange(
1662 ks: *KeyShare,
1663 named_group: tls.NamedGroup,
1664 server_pub_key: []const u8,
1665 ) error{ TlsIllegalParameter, TlsDecryptFailure }!void {
1666 switch (named_group) {
1667 .x25519_ml_kem768 => {
1668 const hksl = crypto.kem.ml_kem.MLKem768.ciphertext_length;
1669 const xksl = hksl + crypto.dh.X25519.public_length;
1670 if (server_pub_key.len != xksl) return error.TlsIllegalParameter;
1671
1672 const hsk = ks.ml_kem768_kp.secret_key.decaps(server_pub_key[0..hksl]) catch
1673 return error.TlsDecryptFailure;
1674 const xsk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[hksl..xksl].*) catch
1675 return error.TlsDecryptFailure;
1676 @memcpy(ks.sk_buf[0..hsk.len], &hsk);
1677 @memcpy(ks.sk_buf[hsk.len..][0..xsk.len], &xsk);
1678 ks.sk_len = hsk.len + xsk.len;
1679 },
1680 .secp256r1 => {
1681 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;
1682 const pk = PublicKey.fromSec1(server_pub_key) catch return error.TlsDecryptFailure;
1683 const mul = pk.p.mulPublic(ks.secp256r1_kp.secret_key.bytes, .big) catch
1684 return error.TlsDecryptFailure;
1685 const sk = mul.affineCoordinates().x.toBytes(.big);
1686 @memcpy(ks.sk_buf[0..sk.len], &sk);
1687 ks.sk_len = sk.len;
1688 },
1689 .secp384r1 => {
1690 const PublicKey = crypto.sign.ecdsa.EcdsaP384Sha384.PublicKey;
1691 const pk = PublicKey.fromSec1(server_pub_key) catch return error.TlsDecryptFailure;
1692 const mul = pk.p.mulPublic(ks.secp384r1_kp.secret_key.bytes, .big) catch
1693 return error.TlsDecryptFailure;
1694 const sk = mul.affineCoordinates().x.toBytes(.big);
1695 @memcpy(ks.sk_buf[0..sk.len], &sk);
1696 ks.sk_len = sk.len;
1697 },
1698 .x25519 => {
1699 const ksl = crypto.dh.X25519.public_length;
1700 if (server_pub_key.len != ksl) return error.TlsIllegalParameter;
1701 const sk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[0..ksl].*) catch
1702 return error.TlsDecryptFailure;
1703 @memcpy(ks.sk_buf[0..sk.len], &sk);
1704 ks.sk_len = sk.len;
1705 },
1706 else => return error.TlsIllegalParameter,
1707 }
1708 }
1709
1710 fn getSharedSecret(ks: *const KeyShare) ?[]const u8 {
1711 return if (ks.sk_len > 0) ks.sk_buf[0..ks.sk_len] else null;
1712 }
1713};
1714
1329fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {1715fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {
1330 return switch (scheme) {1716 return switch (scheme) {
1331 .ecdsa_secp256r1_sha256 => crypto.sign.ecdsa.EcdsaP256Sha256,1717 .ecdsa_secp256r1_sha256 => crypto.sign.ecdsa.EcdsaP256Sha256,
...@@ -1334,11 +1720,20 @@ fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {...@@ -1334,11 +1720,20 @@ fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {
1334 };1720 };
1335}1721}
13361722
1337fn SchemeHash(comptime scheme: tls.SignatureScheme) type {1723fn SchemeRsa(comptime scheme: tls.SignatureScheme) type {
1338 return switch (scheme) {1724 return switch (scheme) {
1339 .rsa_pss_rsae_sha256 => crypto.hash.sha2.Sha256,1725 .rsa_pkcs1_sha256,
1340 .rsa_pss_rsae_sha384 => crypto.hash.sha2.Sha384,1726 .rsa_pkcs1_sha384,
1341 .rsa_pss_rsae_sha512 => crypto.hash.sha2.Sha512,1727 .rsa_pkcs1_sha512,
1728 .rsa_pkcs1_sha1,
1729 => Certificate.rsa.PKCS1v1_5Signature,
1730 .rsa_pss_rsae_sha256,
1731 .rsa_pss_rsae_sha384,
1732 .rsa_pss_rsae_sha512,
1733 .rsa_pss_pss_sha256,
1734 .rsa_pss_pss_sha384,
1735 .rsa_pss_pss_sha512,
1736 => Certificate.rsa.PSSSignature,
1342 else => @compileError("bad scheme"),1737 else => @compileError("bad scheme"),
1343 };1738 };
1344}1739}
...@@ -1350,6 +1745,146 @@ fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {...@@ -1350,6 +1745,146 @@ fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {
1350 };1745 };
1351}1746}
13521747
1748fn SchemeHash(comptime scheme: tls.SignatureScheme) type {
1749 return switch (scheme) {
1750 .rsa_pkcs1_sha256,
1751 .ecdsa_secp256r1_sha256,
1752 .rsa_pss_rsae_sha256,
1753 .rsa_pss_pss_sha256,
1754 => crypto.hash.sha2.Sha256,
1755 .rsa_pkcs1_sha384,
1756 .ecdsa_secp384r1_sha384,
1757 .rsa_pss_rsae_sha384,
1758 .rsa_pss_pss_sha384,
1759 => crypto.hash.sha2.Sha384,
1760 .rsa_pkcs1_sha512,
1761 .ecdsa_secp521r1_sha512,
1762 .rsa_pss_rsae_sha512,
1763 .rsa_pss_pss_sha512,
1764 => crypto.hash.sha2.Sha512,
1765 .rsa_pkcs1_sha1,
1766 .ecdsa_sha1,
1767 => crypto.hash.Sha1,
1768 else => @compileError("bad scheme"),
1769 };
1770}
1771
1772const CertificatePublicKey = struct {
1773 algo: Certificate.AlgorithmCategory,
1774 buf: [600]u8,
1775 len: u16,
1776
1777 fn init(
1778 cert_pub_key: *CertificatePublicKey,
1779 algo: Certificate.AlgorithmCategory,
1780 pub_key: []const u8,
1781 ) error{CertificatePublicKeyInvalid}!void {
1782 if (pub_key.len > cert_pub_key.buf.len) return error.CertificatePublicKeyInvalid;
1783 cert_pub_key.algo = algo;
1784 @memcpy(cert_pub_key.buf[0..pub_key.len], pub_key);
1785 cert_pub_key.len = @intCast(pub_key.len);
1786 }
1787
1788 const VerifyError = error{ TlsDecodeError, TlsBadSignatureScheme, InvalidEncoding } ||
1789 // ecdsa
1790 crypto.errors.EncodingError ||
1791 crypto.errors.NotSquareError ||
1792 crypto.errors.NonCanonicalError ||
1793 SchemeEcdsa(.ecdsa_secp256r1_sha256).Signature.VerifyError ||
1794 SchemeEcdsa(.ecdsa_secp384r1_sha384).Signature.VerifyError ||
1795 // rsa
1796 error{TlsBadRsaSignatureBitCount} ||
1797 Certificate.rsa.PublicKey.ParseDerError ||
1798 Certificate.rsa.PublicKey.FromBytesError ||
1799 Certificate.rsa.PSSSignature.VerifyError ||
1800 Certificate.rsa.PKCS1v1_5Signature.VerifyError ||
1801 // eddsa
1802 SchemeEddsa(.ed25519).Signature.VerifyError;
1803
1804 fn verifySignature(
1805 cert_pub_key: *const CertificatePublicKey,
1806 sigd: *tls.Decoder,
1807 msg: []const []const u8,
1808 ) VerifyError!void {
1809 const pub_key = cert_pub_key.buf[0..cert_pub_key.len];
1810
1811 try sigd.ensure(2 + 2);
1812 const scheme = sigd.decode(tls.SignatureScheme);
1813 const sig_len = sigd.decode(u16);
1814 try sigd.ensure(sig_len);
1815 const encoded_sig = sigd.slice(sig_len);
1816
1817 if (cert_pub_key.algo != @as(Certificate.AlgorithmCategory, switch (scheme) {
1818 .ecdsa_secp256r1_sha256,
1819 .ecdsa_secp384r1_sha384,
1820 => .X9_62_id_ecPublicKey,
1821 .rsa_pkcs1_sha256,
1822 .rsa_pkcs1_sha384,
1823 .rsa_pkcs1_sha512,
1824 .rsa_pss_rsae_sha256,
1825 .rsa_pss_rsae_sha384,
1826 .rsa_pss_rsae_sha512,
1827 .rsa_pkcs1_sha1,
1828 => .rsaEncryption,
1829 .rsa_pss_pss_sha256,
1830 .rsa_pss_pss_sha384,
1831 .rsa_pss_pss_sha512,
1832 => .rsassa_pss,
1833 else => return error.TlsBadSignatureScheme,
1834 })) return error.TlsBadSignatureScheme;
1835
1836 switch (scheme) {
1837 inline .ecdsa_secp256r1_sha256,
1838 .ecdsa_secp384r1_sha384,
1839 => |comptime_scheme| {
1840 const Ecdsa = SchemeEcdsa(comptime_scheme);
1841 const sig = try Ecdsa.Signature.fromDer(encoded_sig);
1842 const key = try Ecdsa.PublicKey.fromSec1(pub_key);
1843 var ver = try sig.verifier(key);
1844 for (msg) |part| ver.update(part);
1845 try ver.verify();
1846 },
1847 inline .rsa_pkcs1_sha256,
1848 .rsa_pkcs1_sha384,
1849 .rsa_pkcs1_sha512,
1850 .rsa_pss_rsae_sha256,
1851 .rsa_pss_rsae_sha384,
1852 .rsa_pss_rsae_sha512,
1853 .rsa_pss_pss_sha256,
1854 .rsa_pss_pss_sha384,
1855 .rsa_pss_pss_sha512,
1856 .rsa_pkcs1_sha1,
1857 => |comptime_scheme| {
1858 const RsaSignature = SchemeRsa(comptime_scheme);
1859 const Hash = SchemeHash(comptime_scheme);
1860 const PublicKey = Certificate.rsa.PublicKey;
1861 const components = try PublicKey.parseDer(pub_key);
1862 const exponent = components.exponent;
1863 const modulus = components.modulus;
1864 switch (modulus.len) {
1865 inline 128, 256, 384, 512 => |modulus_len| {
1866 const key: PublicKey = try .fromBytes(exponent, modulus);
1867 const sig = RsaSignature.fromBytes(modulus_len, encoded_sig);
1868 try RsaSignature.concatVerify(modulus_len, sig, msg, key, Hash);
1869 },
1870 else => return error.TlsBadRsaSignatureBitCount,
1871 }
1872 },
1873 inline .ed25519 => |comptime_scheme| {
1874 const Eddsa = SchemeEddsa(comptime_scheme);
1875 if (encoded_sig.len != Eddsa.Signature.encoded_length) return error.InvalidEncoding;
1876 const sig = Eddsa.Signature.fromBytes(encoded_sig[0..Eddsa.Signature.encoded_length].*);
1877 if (pub_key.len != Eddsa.PublicKey.encoded_length) return error.InvalidEncoding;
1878 const key = try Eddsa.PublicKey.fromBytes(pub_key[0..Eddsa.PublicKey.encoded_length].*);
1879 var ver = try sig.verifier(key);
1880 for (msg) |part| ver.update(part);
1881 try ver.verify();
1882 },
1883 else => unreachable,
1884 }
1885 }
1886};
1887
1353/// Abstraction for sending multiple byte buffers to a slice of iovecs.1888/// Abstraction for sending multiple byte buffers to a slice of iovecs.
1354const VecPut = struct {1889const VecPut = struct {
1355 iovecs: []const std.posix.iovec,1890 iovecs: []const std.posix.iovec,
...@@ -1447,20 +1982,26 @@ fn limitVecs(iovecs: []std.posix.iovec, len: usize) []std.posix.iovec {...@@ -1447,20 +1982,26 @@ fn limitVecs(iovecs: []std.posix.iovec, len: usize) []std.posix.iovec {
1447/// aes128-gcm: 138 MiB/s1982/// aes128-gcm: 138 MiB/s
1448/// aes256-gcm: 120 MiB/s1983/// aes256-gcm: 120 MiB/s
1449const cipher_suites = if (crypto.core.aes.has_hardware_support)1984const cipher_suites = if (crypto.core.aes.has_hardware_support)
1450 enum_array(tls.CipherSuite, &.{1985 array(u16, tls.CipherSuite, .{
1451 .AEGIS_128L_SHA256,1986 .AEGIS_128L_SHA256,
1452 .AEGIS_256_SHA512,1987 .AEGIS_256_SHA512,
1453 .AES_128_GCM_SHA256,1988 .AES_128_GCM_SHA256,
1989 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
1454 .AES_256_GCM_SHA384,1990 .AES_256_GCM_SHA384,
1991 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
1455 .CHACHA20_POLY1305_SHA256,1992 .CHACHA20_POLY1305_SHA256,
1993 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
1456 })1994 })
1457else1995else
1458 enum_array(tls.CipherSuite, &.{1996 array(u16, tls.CipherSuite, .{
1459 .CHACHA20_POLY1305_SHA256,1997 .CHACHA20_POLY1305_SHA256,
1998 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
1460 .AEGIS_128L_SHA256,1999 .AEGIS_128L_SHA256,
1461 .AEGIS_256_SHA512,2000 .AEGIS_256_SHA512,
1462 .AES_128_GCM_SHA256,2001 .AES_128_GCM_SHA256,
2002 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
1463 .AES_256_GCM_SHA384,2003 .AES_256_GCM_SHA384,
2004 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
1464 });2005 });
14652006
1466test {2007test {
lib/std/http/Client.zig+28-7
...@@ -388,6 +388,7 @@ pub const Connection = struct {...@@ -388,6 +388,7 @@ pub const Connection = struct {
388388
389 // try to cleanly close the TLS connection, for any server that cares.389 // try to cleanly close the TLS connection, for any server that cares.
390 _ = conn.tls_client.writeEnd(conn.stream, "", true) catch {};390 _ = conn.tls_client.writeEnd(conn.stream, "", true) catch {};
391 if (conn.tls_client.ssl_key_log) |key_log| key_log.file.close();
391 allocator.destroy(conn.tls_client);392 allocator.destroy(conn.tls_client);
392 }393 }
393394
...@@ -566,7 +567,7 @@ pub const Response = struct {...@@ -566,7 +567,7 @@ pub const Response = struct {
566 .reason = undefined,567 .reason = undefined,
567 .version = undefined,568 .version = undefined,
568 .keep_alive = false,569 .keep_alive = false,
569 .parser = proto.HeadersParser.init(&header_buffer),570 .parser = .init(&header_buffer),
570 };571 };
571572
572 @memcpy(header_buffer[0..response_bytes.len], response_bytes);573 @memcpy(header_buffer[0..response_bytes.len], response_bytes);
...@@ -610,7 +611,7 @@ pub const Response = struct {...@@ -610,7 +611,7 @@ pub const Response = struct {
610 }611 }
611612
612 pub fn iterateHeaders(r: Response) http.HeaderIterator {613 pub fn iterateHeaders(r: Response) http.HeaderIterator {
613 return http.HeaderIterator.init(r.parser.get());614 return .init(r.parser.get());
614 }615 }
615616
616 test iterateHeaders {617 test iterateHeaders {
...@@ -628,7 +629,7 @@ pub const Response = struct {...@@ -628,7 +629,7 @@ pub const Response = struct {
628 .reason = undefined,629 .reason = undefined,
629 .version = undefined,630 .version = undefined,
630 .keep_alive = false,631 .keep_alive = false,
631 .parser = proto.HeadersParser.init(&header_buffer),632 .parser = .init(&header_buffer),
632 };633 };
633634
634 @memcpy(header_buffer[0..response_bytes.len], response_bytes);635 @memcpy(header_buffer[0..response_bytes.len], response_bytes);
...@@ -771,7 +772,7 @@ pub const Request = struct {...@@ -771,7 +772,7 @@ pub const Request = struct {
771 req.client.connection_pool.release(req.client.allocator, req.connection.?);772 req.client.connection_pool.release(req.client.allocator, req.connection.?);
772 req.connection = null;773 req.connection = null;
773774
774 var server_header = std.heap.FixedBufferAllocator.init(req.response.parser.header_bytes_buffer);775 var server_header: std.heap.FixedBufferAllocator = .init(req.response.parser.header_bytes_buffer);
775 defer req.response.parser.header_bytes_buffer = server_header.buffer[server_header.end_index..];776 defer req.response.parser.header_bytes_buffer = server_header.buffer[server_header.end_index..];
776 const protocol, const valid_uri = try validateUri(uri, server_header.allocator());777 const protocol, const valid_uri = try validateUri(uri, server_header.allocator());
777778
...@@ -1354,7 +1355,27 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec...@@ -1354,7 +1355,27 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec
1354 conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client);1355 conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client);
1355 errdefer client.allocator.destroy(conn.data.tls_client);1356 errdefer client.allocator.destroy(conn.data.tls_client);
13561357
1357 conn.data.tls_client.* = std.crypto.tls.Client.init(stream, client.ca_bundle, host) catch return error.TlsInitializationFailed;1358 const ssl_key_log_file: ?std.fs.File = if (std.options.http_enable_ssl_key_log_file) ssl_key_log_file: {
1359 const ssl_key_log_path = std.process.getEnvVarOwned(client.allocator, "SSLKEYLOGFILE") catch |err| switch (err) {
1360 error.EnvironmentVariableNotFound, error.InvalidWtf8 => break :ssl_key_log_file null,
1361 error.OutOfMemory => return error.OutOfMemory,
1362 };
1363 defer client.allocator.free(ssl_key_log_path);
1364 break :ssl_key_log_file std.fs.cwd().createFile(ssl_key_log_path, .{
1365 .truncate = false,
1366 .mode = switch (builtin.os.tag) {
1367 .windows, .wasi => 0,
1368 else => 0o600,
1369 },
1370 }) catch null;
1371 } else null;
1372 errdefer if (ssl_key_log_file) |key_log_file| key_log_file.close();
1373
1374 conn.data.tls_client.* = std.crypto.tls.Client.init(stream, .{
1375 .host = .{ .explicit = host },
1376 .ca = .{ .bundle = client.ca_bundle },
1377 .ssl_key_log_file = ssl_key_log_file,
1378 }) catch return error.TlsInitializationFailed;
1358 // This is appropriate for HTTPS because the HTTP headers contain1379 // This is appropriate for HTTPS because the HTTP headers contain
1359 // the content length which is used to detect truncation attacks.1380 // the content length which is used to detect truncation attacks.
1360 conn.data.tls_client.allow_truncation_attacks = true;1381 conn.data.tls_client.allow_truncation_attacks = true;
...@@ -1620,7 +1641,7 @@ pub fn open(...@@ -1620,7 +1641,7 @@ pub fn open(
1620 }1641 }
1621 }1642 }
16221643
1623 var server_header = std.heap.FixedBufferAllocator.init(options.server_header_buffer);1644 var server_header: std.heap.FixedBufferAllocator = .init(options.server_header_buffer);
1624 const protocol, const valid_uri = try validateUri(uri, server_header.allocator());1645 const protocol, const valid_uri = try validateUri(uri, server_header.allocator());
16251646
1626 if (protocol == .tls and @atomicLoad(bool, &client.next_https_rescan_certs, .acquire)) {1647 if (protocol == .tls and @atomicLoad(bool, &client.next_https_rescan_certs, .acquire)) {
...@@ -1654,7 +1675,7 @@ pub fn open(...@@ -1654,7 +1675,7 @@ pub fn open(
1654 .status = undefined,1675 .status = undefined,
1655 .reason = undefined,1676 .reason = undefined,
1656 .keep_alive = undefined,1677 .keep_alive = undefined,
1657 .parser = proto.HeadersParser.init(server_header.buffer[server_header.end_index..]),1678 .parser = .init(server_header.buffer[server_header.end_index..]),
1658 },1679 },
1659 .headers = options.headers,1680 .headers = options.headers,
1660 .extra_headers = options.extra_headers,1681 .extra_headers = options.extra_headers,
lib/std/http/protocol.zig+21-3
...@@ -172,7 +172,13 @@ pub const HeadersParser = struct {...@@ -172,7 +172,13 @@ pub const HeadersParser = struct {
172 const data_avail = r.next_chunk_length;172 const data_avail = r.next_chunk_length;
173173
174 if (skip) {174 if (skip) {
175 try conn.fill();175 conn.fill() catch |err| switch (err) {
176 error.EndOfStream => {
177 r.done = true;
178 return 0;
179 },
180 else => |e| return e,
181 };
176182
177 const nread = @min(conn.peek().len, data_avail);183 const nread = @min(conn.peek().len, data_avail);
178 conn.drop(@intCast(nread));184 conn.drop(@intCast(nread));
...@@ -196,7 +202,13 @@ pub const HeadersParser = struct {...@@ -196,7 +202,13 @@ pub const HeadersParser = struct {
196 }202 }
197 },203 },
198 .chunk_data_suffix, .chunk_data_suffix_r, .chunk_head_size, .chunk_head_ext, .chunk_head_r => {204 .chunk_data_suffix, .chunk_data_suffix_r, .chunk_head_size, .chunk_head_ext, .chunk_head_r => {
199 try conn.fill();205 conn.fill() catch |err| switch (err) {
206 error.EndOfStream => {
207 r.done = true;
208 return 0;
209 },
210 else => |e| return e,
211 };
200212
201 const i = r.findChunkedLen(conn.peek());213 const i = r.findChunkedLen(conn.peek());
202 conn.drop(@intCast(i));214 conn.drop(@intCast(i));
...@@ -226,7 +238,13 @@ pub const HeadersParser = struct {...@@ -226,7 +238,13 @@ pub const HeadersParser = struct {
226 const out_avail = buffer.len - out_index;238 const out_avail = buffer.len - out_index;
227239
228 if (skip) {240 if (skip) {
229 try conn.fill();241 conn.fill() catch |err| switch (err) {
242 error.EndOfStream => {
243 r.done = true;
244 return 0;
245 },
246 else => |e| return e,
247 };
230248
231 const nread = @min(conn.peek().len, data_avail);249 const nread = @min(conn.peek().len, data_avail);
232 conn.drop(@intCast(nread));250 conn.drop(@intCast(nread));
lib/std/std.zig+5
...@@ -146,6 +146,11 @@ pub const Options = struct {...@@ -146,6 +146,11 @@ pub const Options = struct {
146 /// make a HTTPS connection.146 /// make a HTTPS connection.
147 http_disable_tls: bool = false,147 http_disable_tls: bool = false,
148148
149 /// This enables `std.http.Client` to log ssl secrets to the file specified by the SSLKEYLOGFILE
150 /// env var. Creating such a log file allows other programs with access to that file to decrypt
151 /// all `std.http.Client` traffic made by this program.
152 http_enable_ssl_key_log_file: bool = @import("builtin").mode == .Debug,
153
149 side_channels_mitigations: crypto.SideChannelsMitigations = crypto.default_side_channels_mitigations,154 side_channels_mitigations: crypto.SideChannelsMitigations = crypto.default_side_channels_mitigations,
150};155};
151156