authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-10-31 20:55:34-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-11-07 20:25:26-05:00
logc2a779ae79facc0c6a102825723ae707fdbf8c19
tree22043923bf37b6475617ab570d7366beda478a91
parentee9f00d673f2bccddc2751c328758a2820d2bb70

std.crypto.tls: implement TLSv1.2


6 files changed, 1538 insertions(+), 816 deletions(-)

lib/std/crypto/25519/ed25519.zig+25-11
...@@ -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,26 @@ pub const Ed25519 = struct {...@@ -214,17 +219,26 @@ 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 try sig.concatVerify(&.{msg}, public_key);
226 st.update(msg);233 }
227 return st.verify();234
235 /// Verify the signature against a concatenated message and public key.
236 /// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
237 /// or SignatureVerificationError if the signature is invalid for the given message and key.
238 pub fn concatVerify(sig: Signature, msg: []const []const u8, public_key: PublicKey) VerifyError!void {
239 var st = try Verifier.init(sig, public_key);
240 for (msg) |part| st.update(part);
241 try st.verify();
228 }242 }
229 };243 };
230244
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+32-18
...@@ -91,24 +91,33 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -91,24 +91,33 @@ 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 try sig.concatVerify(&.{msg}, public_key);
103 st.update(msg);105 }
104 return st.verify();106
107 /// Verify the signature against a concatenated message and public key.
108 /// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
109 /// or SignatureVerificationError if the signature is invalid for the given message and key.
110 pub fn concatVerify(sig: Signature, msg: []const []const u8, public_key: PublicKey) VerifyError!void {
111 var st = try Verifier.init(sig, public_key);
112 for (msg) |part| st.update(part);
113 try st.verify();
105 }114 }
106115
107 /// Return the raw signature (r, s) in big-endian format.116 /// Return the raw signature (r, s) in big-endian format.
108 pub fn toBytes(self: Signature) [encoded_length]u8 {117 pub fn toBytes(sig: Signature) [encoded_length]u8 {
109 var bytes: [encoded_length]u8 = undefined;118 var bytes: [encoded_length]u8 = undefined;
110 @memcpy(bytes[0 .. encoded_length / 2], &self.r);119 @memcpy(bytes[0 .. encoded_length / 2], &sig.r);
111 @memcpy(bytes[encoded_length / 2 ..], &self.s);120 @memcpy(bytes[encoded_length / 2 ..], &sig.s);
112 return bytes;121 return bytes;
113 }122 }
114123
...@@ -124,23 +133,23 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -124,23 +133,23 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
124 /// Encode the signature using the DER format.133 /// Encode the signature using the DER format.
125 /// The maximum length of the DER encoding is der_encoded_length_max.134 /// 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.135 /// 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 {136 pub fn toDer(sig: Signature, buf: *[der_encoded_length_max]u8) []u8 {
128 var fb = io.fixedBufferStream(buf);137 var fb = io.fixedBufferStream(buf);
129 const w = fb.writer();138 const w = fb.writer();
130 const r_len = @as(u8, @intCast(self.r.len + (self.r[0] >> 7)));139 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)));140 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));141 const seq_len = @as(u8, @intCast(2 + r_len + 2 + s_len));
133 w.writeAll(&[_]u8{ 0x30, seq_len }) catch unreachable;142 w.writeAll(&[_]u8{ 0x30, seq_len }) catch unreachable;
134 w.writeAll(&[_]u8{ 0x02, r_len }) catch unreachable;143 w.writeAll(&[_]u8{ 0x02, r_len }) catch unreachable;
135 if (self.r[0] >> 7 != 0) {144 if (sig.r[0] >> 7 != 0) {
136 w.writeByte(0x00) catch unreachable;145 w.writeByte(0x00) catch unreachable;
137 }146 }
138 w.writeAll(&self.r) catch unreachable;147 w.writeAll(&sig.r) catch unreachable;
139 w.writeAll(&[_]u8{ 0x02, s_len }) catch unreachable;148 w.writeAll(&[_]u8{ 0x02, s_len }) catch unreachable;
140 if (self.s[0] >> 7 != 0) {149 if (sig.s[0] >> 7 != 0) {
141 w.writeByte(0x00) catch unreachable;150 w.writeByte(0x00) catch unreachable;
142 }151 }
143 w.writeAll(&self.s) catch unreachable;152 w.writeAll(&sig.s) catch unreachable;
144 return fb.getWritten();153 return fb.getWritten();
145 }154 }
146155
...@@ -236,7 +245,9 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -236,7 +245,9 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
236 s: Curve.scalar.Scalar,245 s: Curve.scalar.Scalar,
237 public_key: PublicKey,246 public_key: PublicKey,
238247
239 fn init(sig: Signature, public_key: PublicKey) (IdentityElementError || NonCanonicalError)!Verifier {248 pub const InitError = IdentityElementError || NonCanonicalError;
249
250 fn init(sig: Signature, public_key: PublicKey) InitError!Verifier {
240 const r = try Curve.scalar.Scalar.fromBytes(sig.r, .big);251 const r = try Curve.scalar.Scalar.fromBytes(sig.r, .big);
241 const s = try Curve.scalar.Scalar.fromBytes(sig.s, .big);252 const s = try Curve.scalar.Scalar.fromBytes(sig.s, .big);
242 if (r.isZero() or s.isZero()) return error.IdentityElement;253 if (r.isZero() or s.isZero()) return error.IdentityElement;
...@@ -254,8 +265,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -254,8 +265,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
254 self.h.update(data);265 self.h.update(data);
255 }266 }
256267
268 pub const VerifyError = IdentityElementError || NonCanonicalError ||
269 SignatureVerificationError;
270
257 /// Verify that the signature is valid for the entire message.271 /// Verify that the signature is valid for the entire message.
258 pub fn verify(self: *Verifier) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void {272 pub fn verify(self: *Verifier) VerifyError!void {
259 const ht = Curve.scalar.encoded_length;273 const ht = Curve.scalar.encoded_length;
260 const h_len = @max(Hash.digest_length, ht);274 const h_len = @max(Hash.digest_length, ht);
261 var h: [h_len]u8 = [_]u8{0} ** h_len;275 var h: [h_len]u8 = [_]u8{0} ** h_len;
lib/std/crypto/tls.zig+229-71
...@@ -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
...@@ -286,6 +292,20 @@ pub const NamedGroup = enum(u16) {...@@ -286,6 +292,20 @@ pub const NamedGroup = enum(u16) {
286};292};
287293
288pub const CipherSuite = enum(u16) {294pub const CipherSuite = enum(u16) {
295 RSA_WITH_AES_128_CBC_SHA = 0x002F,
296 DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
297 RSA_WITH_AES_256_CBC_SHA = 0x0035,
298 DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
299 RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
300 RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
301 DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
302 DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
303 RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
304 RSA_WITH_AES_256_GCM_SHA384 = 0x009D,
305 DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
306 DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F,
307 EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
308
289 AES_128_GCM_SHA256 = 0x1301,309 AES_128_GCM_SHA256 = 0x1301,
290 AES_256_GCM_SHA384 = 0x1302,310 AES_256_GCM_SHA384 = 0x1302,
291 CHACHA20_POLY1305_SHA256 = 0x1303,311 CHACHA20_POLY1305_SHA256 = 0x1303,
...@@ -293,7 +313,98 @@ pub const CipherSuite = enum(u16) {...@@ -293,7 +313,98 @@ pub const CipherSuite = enum(u16) {
293 AES_128_CCM_8_SHA256 = 0x1305,313 AES_128_CCM_8_SHA256 = 0x1305,
294 AEGIS_256_SHA512 = 0x1306,314 AEGIS_256_SHA512 = 0x1306,
295 AEGIS_128L_SHA256 = 0x1307,315 AEGIS_128L_SHA256 = 0x1307,
316
317 ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
318 ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
319 ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
320 ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
321 ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
322 ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
323 ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
324 ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
325 ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
326 ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
327 ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
328 ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
329
330 ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8,
331 ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9,
332 DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA,
333
296 _,334 _,
335
336 pub const With = enum {
337 AES_128_CBC_SHA,
338 AES_256_CBC_SHA,
339 AES_128_CBC_SHA256,
340 AES_256_CBC_SHA256,
341 AES_256_CBC_SHA384,
342
343 AES_128_GCM_SHA256,
344 AES_256_GCM_SHA384,
345
346 CHACHA20_POLY1305_SHA256,
347
348 AES_128_CCM_SHA256,
349 AES_128_CCM_8_SHA256,
350
351 AEGIS_256_SHA512,
352 AEGIS_128L_SHA256,
353 };
354
355 pub fn with(cipher_suite: CipherSuite) With {
356 return switch (cipher_suite) {
357 .RSA_WITH_AES_128_CBC_SHA,
358 .DHE_RSA_WITH_AES_128_CBC_SHA,
359 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
360 .ECDHE_RSA_WITH_AES_128_CBC_SHA,
361 => .AES_128_CBC_SHA,
362 .RSA_WITH_AES_256_CBC_SHA,
363 .DHE_RSA_WITH_AES_256_CBC_SHA,
364 .ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
365 .ECDHE_RSA_WITH_AES_256_CBC_SHA,
366 => .AES_256_CBC_SHA,
367 .RSA_WITH_AES_128_CBC_SHA256,
368 .DHE_RSA_WITH_AES_128_CBC_SHA256,
369 .ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
370 .ECDHE_RSA_WITH_AES_128_CBC_SHA256,
371 => .AES_128_CBC_SHA256,
372 .RSA_WITH_AES_256_CBC_SHA256,
373 .DHE_RSA_WITH_AES_256_CBC_SHA256,
374 => .AES_256_CBC_SHA256,
375 .ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
376 .ECDHE_RSA_WITH_AES_256_CBC_SHA384,
377 => .AES_256_CBC_SHA384,
378
379 .RSA_WITH_AES_128_GCM_SHA256,
380 .DHE_RSA_WITH_AES_128_GCM_SHA256,
381 .AES_128_GCM_SHA256,
382 .ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
383 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
384 => .AES_128_GCM_SHA256,
385 .RSA_WITH_AES_256_GCM_SHA384,
386 .DHE_RSA_WITH_AES_256_GCM_SHA384,
387 .AES_256_GCM_SHA384,
388 .ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
389 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
390 => .AES_256_GCM_SHA384,
391
392 .CHACHA20_POLY1305_SHA256,
393 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
394 .ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
395 .DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
396 => .CHACHA20_POLY1305_SHA256,
397
398 .AES_128_CCM_SHA256 => .AES_128_CCM_SHA256,
399 .AES_128_CCM_8_SHA256 => .AES_128_CCM_8_SHA256,
400
401 .AEGIS_256_SHA512 => .AEGIS_256_SHA512,
402 .AEGIS_128L_SHA256 => .AEGIS_128L_SHA256,
403
404 .EMPTY_RENEGOTIATION_INFO_SCSV => unreachable,
405 _ => unreachable,
406 };
407 }
297};408};
298409
299pub const CertificateType = enum(u8) {410pub const CertificateType = enum(u8) {
...@@ -308,58 +419,108 @@ pub const KeyUpdateRequest = enum(u8) {...@@ -308,58 +419,108 @@ pub const KeyUpdateRequest = enum(u8) {
308 _,419 _,
309};420};
310421
311pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type) type {422pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
312 return struct {423 return struct {
313 pub const AEAD = AeadType;424 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);
317425
318 handshake_secret: [Hkdf.prk_length]u8,426 transcript_hash: A.Hash,
319 master_secret: [Hkdf.prk_length]u8,427 version: union {
320 client_handshake_key: [AEAD.key_length]u8,428 tls_1_2: struct {
321 server_handshake_key: [AEAD.key_length]u8,429 server_verify_data: [12]u8,
322 client_finished_key: [Hmac.key_length]u8,430 app_cipher: A.Tls_1_2,
323 server_finished_key: [Hmac.key_length]u8,431 },
324 client_handshake_iv: [AEAD.nonce_length]u8,432 tls_1_3: struct {
325 server_handshake_iv: [AEAD.nonce_length]u8,433 handshake_secret: [A.Hkdf.prk_length]u8,
326 transcript_hash: Hash,434 master_secret: [A.Hkdf.prk_length]u8,
435 client_handshake_key: [A.AEAD.key_length]u8,
436 server_handshake_key: [A.AEAD.key_length]u8,
437 client_finished_key: [A.Hmac.key_length]u8,
438 server_finished_key: [A.Hmac.key_length]u8,
439 client_handshake_iv: [A.AEAD.nonce_length]u8,
440 server_handshake_iv: [A.AEAD.nonce_length]u8,
441 },
442 },
327 };443 };
328}444}
329445
330pub const HandshakeCipher = union(enum) {446pub const HandshakeCipher = union(enum) {
331 AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256),447 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),448 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),449 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),450 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),451 AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
336};452};
337453
338pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type) type {454pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
339 return struct {455 return union {
340 pub const AEAD = AeadType;456 pub const AEAD = AeadType;
341 pub const Hash = HashType;457 pub const Hash = HashType;
342 pub const Hmac = crypto.auth.hmac.Hmac(Hash);458 pub const Hmac = crypto.auth.hmac.Hmac(Hash);
343 pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);459 pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
344460
345 client_secret: [Hash.digest_length]u8,461 pub const enc_key_length = AEAD.key_length;
346 server_secret: [Hash.digest_length]u8,462 pub const fixed_iv_length = AEAD.nonce_length - explicit_iv_length;
347 client_key: [AEAD.key_length]u8,463 pub const record_iv_length = explicit_iv_length;
348 server_key: [AEAD.key_length]u8,464 pub const mac_length = AEAD.tag_length;
349 client_iv: [AEAD.nonce_length]u8,465 pub const mac_key_length = Hmac.key_length_min;
350 server_iv: [AEAD.nonce_length]u8,466
467 tls_1_2: Tls_1_2,
468 tls_1_3: Tls_1_3,
469
470 pub const Tls_1_2 = extern struct {
471 client_write_MAC_key: [mac_key_length]u8,
472 server_write_MAC_key: [mac_key_length]u8,
473 client_write_key: [enc_key_length]u8,
474 server_write_key: [enc_key_length]u8,
475 client_write_IV: [fixed_iv_length]u8,
476 server_write_IV: [fixed_iv_length]u8,
477 // non-standard entropy
478 client_salt: [record_iv_length]u8,
479 };
480
481 pub const Tls_1_3 = struct {
482 client_secret: [Hash.digest_length]u8,
483 server_secret: [Hash.digest_length]u8,
484 client_key: [AEAD.key_length]u8,
485 server_key: [AEAD.key_length]u8,
486 client_iv: [AEAD.nonce_length]u8,
487 server_iv: [AEAD.nonce_length]u8,
488 };
351 };489 };
352}490}
353491
354/// Encryption parameters for application traffic.492/// Encryption parameters for application traffic.
355pub const ApplicationCipher = union(enum) {493pub const ApplicationCipher = union(enum) {
356 AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256),494 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),495 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),496 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),497 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),498 AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
361};499};
362500
501pub fn hmacExpandLabel(
502 comptime Hmac: type,
503 secret: []const u8,
504 label_then_seed: []const []const u8,
505 comptime len: usize,
506) [len]u8 {
507 const initial_hmac: Hmac = .init(secret);
508 var a: [Hmac.mac_length]u8 = undefined;
509 var result: [std.mem.alignForwardAnyAlign(usize, len, Hmac.mac_length)]u8 = undefined;
510 var index: usize = 0;
511 while (index < result.len) : (index += Hmac.mac_length) {
512 var a_hmac = initial_hmac;
513 if (index > 0) a_hmac.update(&a) else for (label_then_seed) |part| a_hmac.update(part);
514 a_hmac.final(&a);
515
516 var result_hmac = initial_hmac;
517 result_hmac.update(&a);
518 for (label_then_seed) |part| result_hmac.update(part);
519 result_hmac.final(result[index..][0..Hmac.mac_length]);
520 }
521 return result[0..len].*;
522}
523
363pub fn hkdfExpandLabel(524pub fn hkdfExpandLabel(
364 comptime Hkdf: type,525 comptime Hkdf: type,
365 key: [Hkdf.prk_length]u8,526 key: [Hkdf.prk_length]u8,
...@@ -418,19 +579,16 @@ pub inline fn enum_array(comptime E: type, comptime tags: []const E) [2 + @sizeO...@@ -418,19 +579,16 @@ pub inline fn enum_array(comptime E: type, comptime tags: []const E) [2 + @sizeO
418 return array(2, result);579 return array(2, result);
419}580}
420581
421pub inline fn int2(x: u16) [2]u8 {582pub inline fn int2(int: u16) [2]u8 {
422 return .{583 var arr: [2]u8 = undefined;
423 @as(u8, @truncate(x >> 8)),584 std.mem.writeInt(u16, &arr, int, .big);
424 @as(u8, @truncate(x)),585 return arr;
425 };
426}586}
427587
428pub inline fn int3(x: u24) [3]u8 {588pub inline fn int3(int: u24) [3]u8 {
429 return .{589 var arr: [3]u8 = undefined;
430 @as(u8, @truncate(x >> 16)),590 std.mem.writeInt(u24, &arr, int, .big);
431 @as(u8, @truncate(x >> 8)),591 return arr;
432 @as(u8, @truncate(x)),
433 };
434}592}
435593
436/// An abstraction to ensure that protocol-parsing code does not perform an594/// An abstraction to ensure that protocol-parsing code does not perform an
lib/std/crypto/tls/Client.zig+1004-571
...@@ -8,12 +8,14 @@ const assert = std.debug.assert;...@@ -8,12 +8,14 @@ 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 int2 = tls.int2;
13const int3 = tls.int3;14const int3 = tls.int3;
14const array = tls.array;15const array = tls.array;
15const enum_array = tls.enum_array;16const enum_array = tls.enum_array;
1617
18tls_version: tls.ProtocolVersion,
17read_seq: u64,19read_seq: u64,
18write_seq: u64,20write_seq: u64,
19/// The starting index of cleartext bytes inside `partially_read_buffer`.21/// The starting index of cleartext bytes inside `partially_read_buffer`.
...@@ -136,7 +138,7 @@ pub fn InitError(comptime Stream: type) type {...@@ -136,7 +138,7 @@ pub fn InitError(comptime Stream: type) type {
136 };138 };
137}139}
138140
139/// Initiates a TLS handshake and establishes a TLSv1.3 session with `stream`, which141/// Initiates a TLS handshake and establishes a TLSv1.2 or TLSv1.3 session with `stream`, which
140/// must conform to `StreamInterface`.142/// must conform to `StreamInterface`.
141///143///
142/// `host` is only borrowed during this function call.144/// `host` is only borrowed during this function call.
...@@ -145,26 +147,20 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -145,26 +147,20 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
145147
146 var random_buffer: [128]u8 = undefined;148 var random_buffer: [128]u8 = undefined;
147 crypto.random.bytes(&random_buffer);149 crypto.random.bytes(&random_buffer);
148 const hello_rand = random_buffer[0..32].*;150 const client_hello_rand = random_buffer[0..32].*;
151 var server_hello_rand: [32]u8 = undefined;
149 const legacy_session_id = random_buffer[32..64].*;152 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].*;
152153
153 const x25519_kp = crypto.dh.X25519.KeyPair.create(x25519_kp_seed) catch |err| switch (err) {154 var key_share = KeyShare.init(random_buffer[64..128].*) catch |err| switch (err) {
154 // Only possible to happen if the private key is all zeroes.155 // Only possible to happen if the seed is all zeroes.
155 error.IdentityElement => return error.InsufficientEntropy,156 error.IdentityElement => return error.InsufficientEntropy,
156 };157 };
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,
160 };
161 const ml_kem768_kp = crypto.kem.ml_kem.MLKem768.KeyPair.create(null) catch {};
162158
163 const extensions_payload =159 const extensions_payload =
164 tls.extension(.supported_versions, [_]u8{160 tls.extension(.supported_versions, [_]u8{2 + 2} ++ // byte length of supported versions
165 0x02, // byte length of supported versions161 int2(@intFromEnum(tls.ProtocolVersion.tls_1_3)) ++
166 0x03, 0x04, // TLS 1.3162 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2))) ++
167 }) ++ tls.extension(.signature_algorithms, enum_array(tls.SignatureScheme, &.{163 tls.extension(.signature_algorithms, enum_array(tls.SignatureScheme, &.{
168 .ecdsa_secp256r1_sha256,164 .ecdsa_secp256r1_sha256,
169 .ecdsa_secp384r1_sha384,165 .ecdsa_secp384r1_sha384,
170 .rsa_pss_rsae_sha256,166 .rsa_pss_rsae_sha256,
...@@ -178,11 +174,11 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -178,11 +174,11 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
178 })) ++ tls.extension(174 })) ++ tls.extension(
179 .key_share,175 .key_share,
180 array(1, int2(@intFromEnum(tls.NamedGroup.x25519)) ++176 array(1, int2(@intFromEnum(tls.NamedGroup.x25519)) ++
181 array(1, x25519_kp.public_key) ++177 array(1, key_share.x25519_kp.public_key) ++
182 int2(@intFromEnum(tls.NamedGroup.secp256r1)) ++178 int2(@intFromEnum(tls.NamedGroup.secp256r1)) ++
183 array(1, secp256r1_kp.public_key.toUncompressedSec1()) ++179 array(1, key_share.secp256r1_kp.public_key.toUncompressedSec1()) ++
184 int2(@intFromEnum(tls.NamedGroup.x25519_ml_kem768)) ++180 int2(@intFromEnum(tls.NamedGroup.x25519_ml_kem768)) ++
185 array(1, x25519_kp.public_key ++ ml_kem768_kp.public_key.toBytes())),181 array(1, key_share.x25519_kp.public_key ++ key_share.ml_kem768_kp.public_key.toBytes())),
186 ) ++182 ) ++
187 int2(@intFromEnum(tls.ExtensionType.server_name)) ++183 int2(@intFromEnum(tls.ExtensionType.server_name)) ++
188 int2(host_len + 5) ++ // byte length of this extension payload184 int2(host_len + 5) ++ // byte length of this extension payload
...@@ -198,7 +194,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -198,7 +194,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
198194
199 const client_hello =195 const client_hello =
200 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++196 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
201 hello_rand ++197 client_hello_rand ++
202 [1]u8{32} ++ legacy_session_id ++198 [1]u8{32} ++ legacy_session_id ++
203 cipher_suites ++199 cipher_suites ++
204 int2(legacy_compression_methods) ++200 int2(legacy_compression_methods) ++
...@@ -209,16 +205,16 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -209,16 +205,16 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
209 int3(@intCast(client_hello.len + host_len)) ++205 int3(@intCast(client_hello.len + host_len)) ++
210 client_hello;206 client_hello;
211207
212 const plaintext_header = [_]u8{208 const cleartext_header = [_]u8{@intFromEnum(tls.ContentType.handshake)} ++
213 @intFromEnum(tls.ContentType.handshake),209 int2(@intFromEnum(tls.ProtocolVersion.tls_1_0)) ++ // legacy_record_version
214 0x03, 0x01, // legacy_record_version210 int2(@intCast(out_handshake.len + host_len)) ++
215 } ++ int2(@intCast(out_handshake.len + host_len)) ++ out_handshake;211 out_handshake;
216212
217 {213 {
218 var iovecs = [_]std.posix.iovec_const{214 var iovecs = [_]std.posix.iovec_const{
219 .{215 .{
220 .base = &plaintext_header,216 .base = &cleartext_header,
221 .len = plaintext_header.len,217 .len = cleartext_header.len,
222 },218 },
223 .{219 .{
224 .base = host.ptr,220 .base = host.ptr,
...@@ -228,8 +224,10 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -228,8 +224,10 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
228 try stream.writevAll(&iovecs);224 try stream.writevAll(&iovecs);
229 }225 }
230226
231 const client_hello_bytes1 = plaintext_header[5..];227 const client_hello_bytes1 = cleartext_header[tls.record_header_len..];
232228
229 var tls_version: tls.ProtocolVersion = undefined;
230 var cipher_suite_tag: tls.CipherSuite = undefined;
233 var handshake_cipher: tls.HandshakeCipher = undefined;231 var handshake_cipher: tls.HandshakeCipher = undefined;
234 var handshake_buffer: [8000]u8 = undefined;232 var handshake_buffer: [8000]u8 = undefined;
235 var d: tls.Decoder = .{ .buf = &handshake_buffer };233 var d: tls.Decoder = .{ .buf = &handshake_buffer };
...@@ -259,10 +257,10 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -259,10 +257,10 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
259 if (handshake_type != .server_hello) return error.TlsUnexpectedMessage;257 if (handshake_type != .server_hello) return error.TlsUnexpectedMessage;
260 const length = ptd.decode(u24);258 const length = ptd.decode(u24);
261 var hsd = try ptd.sub(length);259 var hsd = try ptd.sub(length);
262 try hsd.ensure(2 + 32 + 1 + 32 + 2 + 1 + 2);260 try hsd.ensure(2 + 32 + 1 + 32 + 2 + 1);
263 const legacy_version = hsd.decode(u16);261 const legacy_version = hsd.decode(u16);
264 const random = hsd.array(32);262 @memcpy(&server_hello_rand, hsd.array(32));
265 if (mem.eql(u8, random, &tls.hello_retry_request_sequence)) {263 if (mem.eql(u8, &server_hello_rand, &tls.hello_retry_request_sequence)) {
266 // This is a HelloRetryRequest message. This client implementation264 // This is a HelloRetryRequest message. This client implementation
267 // does not expect to get one.265 // does not expect to get one.
268 return error.TlsUnexpectedMessage;266 return error.TlsUnexpectedMessage;
...@@ -270,83 +268,44 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -270,83 +268,44 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
270 const legacy_session_id_echo_len = hsd.decode(u8);268 const legacy_session_id_echo_len = hsd.decode(u8);
271 if (legacy_session_id_echo_len != 32) return error.TlsIllegalParameter;269 if (legacy_session_id_echo_len != 32) return error.TlsIllegalParameter;
272 const legacy_session_id_echo = hsd.array(32);270 const legacy_session_id_echo = hsd.array(32);
273 if (!mem.eql(u8, legacy_session_id_echo, &legacy_session_id))271 cipher_suite_tag = hsd.decode(tls.CipherSuite);
274 return error.TlsIllegalParameter;
275 const cipher_suite_tag = hsd.decode(tls.CipherSuite);
276 hsd.skip(1); // legacy_compression_method272 hsd.skip(1); // legacy_compression_method
277 const extensions_size = hsd.decode(u16);273 var supported_version: ?u16 = null;
278 var all_extd = try hsd.sub(extensions_size);274 if (!hsd.eof()) {
279 var supported_version: u16 = 0;275 try hsd.ensure(2);
280 var shared_key: []const u8 = undefined;276 const extensions_size = hsd.decode(u16);
281 var have_shared_key = false;277 var all_extd = try hsd.sub(extensions_size);
282 while (!all_extd.eof()) {278 while (!all_extd.eof()) {
283 try all_extd.ensure(2 + 2);279 try all_extd.ensure(2 + 2);
284 const et = all_extd.decode(tls.ExtensionType);280 const et = all_extd.decode(tls.ExtensionType);
285 const ext_size = all_extd.decode(u16);281 const ext_size = all_extd.decode(u16);
286 var extd = try all_extd.sub(ext_size);282 var extd = try all_extd.sub(ext_size);
287 switch (et) {283 switch (et) {
288 .supported_versions => {284 .supported_versions => {
289 if (supported_version != 0) return error.TlsIllegalParameter;285 if (supported_version) |_| return error.TlsIllegalParameter;
290 try extd.ensure(2);286 try extd.ensure(2);
291 supported_version = extd.decode(u16);287 supported_version = extd.decode(u16);
292 },288 },
293 .key_share => {289 .key_share => {
294 if (have_shared_key) return error.TlsIllegalParameter;290 if (key_share.getSharedSecret()) |_| return error.TlsIllegalParameter;
295 have_shared_key = true;291 try extd.ensure(4);
296 try extd.ensure(4);292 const named_group = extd.decode(tls.NamedGroup);
297 const named_group = extd.decode(tls.NamedGroup);293 const key_size = extd.decode(u16);
298 const key_size = extd.decode(u16);294 try extd.ensure(key_size);
299 try extd.ensure(key_size);295 try key_share.exchange(named_group, extd.slice(key_size));
300 switch (named_group) {296 },
301 .x25519_ml_kem768 => {297 else => {},
302 const xksl = crypto.dh.X25519.public_length;298 }
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 }299 }
344 }300 }
345 if (!have_shared_key) return error.TlsIllegalParameter;
346301
347 const tls_version = if (supported_version == 0) legacy_version else supported_version;302 tls_version = @enumFromInt(supported_version orelse legacy_version);
348 if (tls_version != @intFromEnum(tls.ProtocolVersion.tls_1_3))303 switch (tls_version) {
349 return error.TlsIllegalParameter;304 .tls_1_3 => if (!mem.eql(u8, legacy_session_id_echo, &legacy_session_id)) return error.TlsIllegalParameter,
305 .tls_1_2 => if (mem.eql(u8, server_hello_rand[24..31], "DOWNGRD") and
306 server_hello_rand[31] >> 1 == 0x00) return error.TlsIllegalParameter,
307 else => return error.TlsIllegalParameter,
308 }
350309
351 switch (cipher_suite_tag) {310 switch (cipher_suite_tag) {
352 inline .AES_128_GCM_SHA256,311 inline .AES_128_GCM_SHA256,
...@@ -354,43 +313,63 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -354,43 +313,63 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
354 .CHACHA20_POLY1305_SHA256,313 .CHACHA20_POLY1305_SHA256,
355 .AEGIS_256_SHA512,314 .AEGIS_256_SHA512,
356 .AEGIS_128L_SHA256,315 .AEGIS_128L_SHA256,
316
317 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
318 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
319 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
357 => |tag| {320 => |tag| {
358 const P = std.meta.TagPayloadByName(tls.HandshakeCipher, @tagName(tag));321 handshake_cipher = @unionInit(tls.HandshakeCipher, @tagName(tag.with()), .{
359 handshake_cipher = @unionInit(tls.HandshakeCipher, @tagName(tag), .{322 .transcript_hash = .init(.{}),
360 .handshake_secret = undefined,323 .version = 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 });324 });
370 const p = &@field(handshake_cipher, @tagName(tag));325 const p = &@field(handshake_cipher, @tagName(tag.with()));
371 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1326 p.transcript_hash.update(client_hello_bytes1); // Client Hello part 1
372 p.transcript_hash.update(host); // Client Hello part 2327 p.transcript_hash.update(host); // Client Hello part 2
373 p.transcript_hash.update(server_hello_fragment);328 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 },329 },
391 else => {330
392 return error.TlsIllegalParameter;331 else => return error.TlsIllegalParameter,
332 }
333 switch (tls_version) {
334 .tls_1_3 => switch (cipher_suite_tag) {
335 inline .AES_128_GCM_SHA256,
336 .AES_256_GCM_SHA384,
337 .CHACHA20_POLY1305_SHA256,
338 .AEGIS_256_SHA512,
339 .AEGIS_128L_SHA256,
340 => |tag| {
341 const sk = key_share.getSharedSecret() orelse return error.TlsIllegalParameter;
342 const p = &@field(handshake_cipher, @tagName(tag.with()));
343 const P = @TypeOf(p.*).A;
344 const hello_hash = p.transcript_hash.peek();
345 const zeroes = [1]u8{0} ** P.Hash.digest_length;
346 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);
347 const empty_hash = tls.emptyHash(P.Hash);
348 p.version = .{ .tls_1_3 = undefined };
349 const pv = &p.version.tls_1_3;
350 const hs_derived_secret = hkdfExpandLabel(P.Hkdf, early_secret, "derived", &empty_hash, P.Hash.digest_length);
351 pv.handshake_secret = P.Hkdf.extract(&hs_derived_secret, sk);
352 const ap_derived_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "derived", &empty_hash, P.Hash.digest_length);
353 pv.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes);
354 const client_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length);
355 const server_secret = hkdfExpandLabel(P.Hkdf, pv.handshake_secret, "s hs traffic", &hello_hash, P.Hash.digest_length);
356 pv.client_finished_key = hkdfExpandLabel(P.Hkdf, client_secret, "finished", "", P.Hmac.key_length);
357 pv.server_finished_key = hkdfExpandLabel(P.Hkdf, server_secret, "finished", "", P.Hmac.key_length);
358 pv.client_handshake_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
359 pv.server_handshake_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
360 pv.client_handshake_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
361 pv.server_handshake_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
362 },
363 else => return error.TlsIllegalParameter,
364 },
365 .tls_1_2 => switch (cipher_suite_tag) {
366 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
367 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
368 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
369 => {},
370 else => return error.TlsIllegalParameter,
393 },371 },
372 else => return error.TlsIllegalParameter,
394 }373 }
395 },374 },
396 else => return error.TlsUnexpectedMessage,375 else => return error.TlsUnexpectedMessage,
...@@ -404,58 +383,74 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -404,58 +383,74 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
404 // the previous certificate in memory so that it can be verified by the383 // the previous certificate in memory so that it can be verified by the
405 // next one.384 // next one.
406 var cert_index: usize = 0;385 var cert_index: usize = 0;
386 var write_seq: u64 = 0;
407 var read_seq: u64 = 0;387 var read_seq: u64 = 0;
408 var prev_cert: Certificate.Parsed = undefined;388 var prev_cert: Certificate.Parsed = undefined;
409 // Set to true once a trust chain has been established from the first389 const CipherState = enum {
410 // certificate to a root CA.390 /// No cipher is in use
391 cleartext,
392 /// Handshake cipher is in use
393 handshake,
394 /// Application cipher is in use
395 application,
396 };
397 var pending_cipher_state: CipherState = switch (tls_version) {
398 .tls_1_3 => .handshake,
399 .tls_1_2 => .cleartext,
400 else => unreachable,
401 };
402 var cipher_state: CipherState = .cleartext;
411 const HandshakeState = enum {403 const HandshakeState = enum {
412 /// In this state we expect only an encrypted_extensions message.404 /// In this state we expect only an encrypted_extensions message.
413 encrypted_extensions,405 encrypted_extensions,
414 /// In this state we expect certificate messages.406 /// In this state we expect certificate handshake messages.
415 certificate,407 certificate,
416 /// In this state we expect certificate or certificate_verify messages.408 /// In this state we expect certificate or certificate_verify messages.
417 /// certificate messages are ignored since the trust chain is already409 /// certificate messages are ignored since the trust chain is already
418 /// established.410 /// established.
419 trust_chain_established,411 trust_chain_established,
420 /// In this state, we expect only the finished message.412 /// In this state, we expect only the server_hello_done handshake message.
413 server_hello_done,
414 /// In this state, we expect only the finished handshake message.
421 finished,415 finished,
422 };416 };
423 var handshake_state: HandshakeState = .encrypted_extensions;417 var handshake_state: HandshakeState = switch (tls_version) {
418 .tls_1_3 => .encrypted_extensions,
419 .tls_1_2 => .certificate,
420 else => unreachable,
421 };
424 var cleartext_bufs: [2][8000]u8 = undefined;422 var cleartext_bufs: [2][8000]u8 = undefined;
425 var main_cert_pub_key_algo: Certificate.AlgorithmCategory = undefined;423 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();424 const now_sec = std.time.timestamp();
429425
430 while (true) {426 while (true) {
431 try d.readAtLeastOurAmt(stream, tls.record_header_len);427 try d.readAtLeastOurAmt(stream, tls.record_header_len);
432 const record_header = d.buf[d.idx..][0..5];428 const record_header = d.buf[d.idx..][0..tls.record_header_len];
433 const ct = d.decode(tls.ContentType);429 const record_ct = d.decode(tls.ContentType);
434 d.skip(2); // legacy_version430 d.skip(2); // legacy_version
435 const record_len = d.decode(u16);431 const record_len = d.decode(u16);
436 try d.readAtLeast(stream, record_len);432 try d.readAtLeast(stream, record_len);
437 var record_decoder = try d.sub(record_len);433 var record_decoder = try d.sub(record_len);
438 switch (ct) {434 var ctd, const ct = content: switch (cipher_state) {
439 .change_cipher_spec => {435 .cleartext => .{ record_decoder, record_ct },
440 try record_decoder.ensure(1);436 .handshake => {
441 if (record_decoder.decode(u8) != 0x01) return error.TlsIllegalParameter;437 std.debug.assert(tls_version == .tls_1_3);
442 },438 if (record_ct != .application_data) return error.TlsUnexpectedMessage;
443 .application_data => {439 try record_decoder.ensure(record_len);
444 const cleartext_buf = &cleartext_bufs[cert_index % 2];440 const cleartext_buf = &cleartext_bufs[cert_index % 2];
445441 const cleartext = cleartext: switch (handshake_cipher) {
446 const cleartext = switch (handshake_cipher) {442 inline else => |*p| {
447 inline else => |*p| c: {443 const pv = &p.version.tls_1_3;
448 const P = @TypeOf(p.*);444 const P = @TypeOf(p.*).A;
449 const ciphertext_len = record_len - P.AEAD.tag_length;445 if (record_len < P.AEAD.tag_length) return error.TlsRecordOverflow;
450 try record_decoder.ensure(ciphertext_len + P.AEAD.tag_length);446 const ciphertext = record_decoder.slice(record_len - P.AEAD.tag_length);
451 const ciphertext = record_decoder.slice(ciphertext_len);
452 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;447 if (ciphertext.len > cleartext_buf.len) return error.TlsRecordOverflow;
453 const cleartext = cleartext_buf[0..ciphertext.len];448 const cleartext = cleartext_buf[0..ciphertext.len];
454 const auth_tag = record_decoder.array(P.AEAD.tag_length).*;449 const auth_tag = record_decoder.array(P.AEAD.tag_length).*;
455 const nonce = if (builtin.zig_backend == .stage2_x86_64 and450 const nonce = if (builtin.zig_backend == .stage2_x86_64 and
456 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)451 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
457 nonce: {452 nonce: {
458 var nonce = p.server_handshake_iv;453 var nonce = pv.server_handshake_iv;
459 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);454 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
460 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ read_seq, .big);455 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ read_seq, .big);
461 break :nonce nonce;456 break :nonce nonce;
...@@ -463,200 +458,320 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -463,200 +458,320 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
463 const V = @Vector(P.AEAD.nonce_length, u8);458 const V = @Vector(P.AEAD.nonce_length, u8);
464 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);459 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
465 const operand: V = pad ++ @as([8]u8, @bitCast(big(read_seq)));460 const operand: V = pad ++ @as([8]u8, @bitCast(big(read_seq)));
466 break :nonce @as(V, p.server_handshake_iv) ^ operand;461 break :nonce @as(V, pv.server_handshake_iv) ^ operand;
467 };462 };
468 read_seq += 1;463 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;464 return error.TlsBadRecordMac;
471 break :c @constCast(mem.trimRight(u8, cleartext, "\x00"));465 break :cleartext mem.trimRight(u8, cleartext, "\x00");
472 },466 },
473 };467 };
468 read_seq += 1;
469 const ct: tls.ContentType = @enumFromInt(cleartext[cleartext.len - 1]);
470 if (ct != .handshake) return error.TlsUnexpectedMessage;
471 break :content .{ tls.Decoder.fromTheirSlice(@constCast(cleartext[0 .. cleartext.len - 1])), ct };
472 },
473 .application => {
474 std.debug.assert(tls_version == .tls_1_2);
475 if (record_ct != .handshake) return error.TlsUnexpectedMessage;
476 try record_decoder.ensure(record_len);
477 const cleartext_buf = &cleartext_bufs[cert_index % 2];
478 const cleartext = cleartext: switch (handshake_cipher) {
479 inline else => |*p| {
480 const pv = &p.version.tls_1_2;
481 const P = @TypeOf(p.*).A;
482 if (record_len < P.record_iv_length + P.mac_length) return error.TlsRecordOverflow;
483 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
484 if (message_len > cleartext_buf.len) return error.TlsRecordOverflow;
485 const cleartext = cleartext_buf[0..message_len];
486 const ad = std.mem.toBytes(big(read_seq)) ++
487 record_header[0 .. 1 + 2] ++
488 std.mem.toBytes(big(message_len));
489 const record_iv = record_decoder.array(P.record_iv_length).*;
490 const masked_read_seq = read_seq &
491 comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length);
492 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
493 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
494 nonce: {
495 var nonce = pv.app_cipher.server_write_IV ++ record_iv;
496 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
497 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ masked_read_seq, .big);
498 break :nonce nonce;
499 } else nonce: {
500 const V = @Vector(P.AEAD.nonce_length, u8);
501 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
502 const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq)));
503 break :nonce @as(V, pv.app_cipher.server_write_IV ++ record_iv) ^ operand;
504 };
505 const ciphertext = record_decoder.slice(message_len);
506 const auth_tag = record_decoder.array(P.mac_length);
507 P.AEAD.decrypt(cleartext, ciphertext, auth_tag.*, ad, nonce, pv.app_cipher.server_write_key) catch return error.TlsBadRecordMac;
508 break :cleartext cleartext;
509 },
510 };
511 read_seq += 1;
512 break :content .{ tls.Decoder.fromTheirSlice(cleartext), record_ct };
513 },
514 };
515 switch (ct) {
516 .alert => {
517 try ctd.ensure(2);
518 const level = ctd.decode(tls.AlertLevel);
519 const desc = ctd.decode(tls.AlertDescription);
520 _ = level;
474521
475 const inner_ct: tls.ContentType = @enumFromInt(cleartext[cleartext.len - 1]);522 // if this isn't a error alert, then it's a closure alert, which makes no sense in a handshake
476 if (inner_ct != .handshake) return error.TlsUnexpectedMessage;523 try desc.toError();
477524 // TODO: handle server-side closures
478 var ctd = tls.Decoder.fromTheirSlice(cleartext[0 .. cleartext.len - 1]);525 return error.TlsUnexpectedMessage;
479 while (true) {526 },
480 try ctd.ensure(4);527 .change_cipher_spec => {
481 const handshake_type = ctd.decode(tls.HandshakeType);528 try ctd.ensure(1);
482 const handshake_len = ctd.decode(u24);529 if (ctd.decode(u8) != 0x01) return error.TlsIllegalParameter;
483 var hsd = try ctd.sub(handshake_len);530 cipher_state = pending_cipher_state;
484 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];531 },
485 const handshake = ctd.buf[ctd.idx - handshake_len .. ctd.idx];532 .handshake => while (true) {
486 switch (handshake_type) {533 try ctd.ensure(4);
487 .encrypted_extensions => {534 const handshake_type = ctd.decode(tls.HandshakeType);
488 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;535 const handshake_len = ctd.decode(u24);
489 handshake_state = .certificate;536 var hsd = try ctd.sub(handshake_len);
490 switch (handshake_cipher) {537 const wrapped_handshake = ctd.buf[ctd.idx - handshake_len - 4 .. ctd.idx];
491 inline else => |*p| p.transcript_hash.update(wrapped_handshake),538 switch (handshake_type) {
492 }539 .encrypted_extensions => {
493 try hsd.ensure(2);540 if (tls_version != .tls_1_3) return error.TlsUnexpectedMessage;
494 const total_ext_size = hsd.decode(u16);541 if (cipher_state != .handshake) return error.TlsUnexpectedMessage;
495 var all_extd = try hsd.sub(total_ext_size);542 if (handshake_state != .encrypted_extensions) return error.TlsUnexpectedMessage;
496 while (!all_extd.eof()) {543 handshake_state = .certificate;
497 try all_extd.ensure(4);544 switch (handshake_cipher) {
498 const et = all_extd.decode(tls.ExtensionType);545 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
499 const ext_size = all_extd.decode(u16);546 }
500 const extd = try all_extd.sub(ext_size);547 try hsd.ensure(2);
501 _ = extd;548 const total_ext_size = hsd.decode(u16);
502 switch (et) {549 var all_extd = try hsd.sub(total_ext_size);
503 .server_name => {},550 while (!all_extd.eof()) {
504 else => {},551 try all_extd.ensure(4);
505 }552 const et = all_extd.decode(tls.ExtensionType);
553 const ext_size = all_extd.decode(u16);
554 const extd = try all_extd.sub(ext_size);
555 _ = extd;
556 switch (et) {
557 .server_name => {},
558 else => {},
506 }559 }
507 },560 }
508 .certificate => cert: {561 },
509 switch (handshake_cipher) {562 .certificate => cert: {
510 inline else => |*p| p.transcript_hash.update(wrapped_handshake),563 switch (handshake_cipher) {
564 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
565 }
566 switch (handshake_state) {
567 .certificate => {},
568 .trust_chain_established => break :cert,
569 else => return error.TlsUnexpectedMessage,
570 }
571
572 switch (tls_version) {
573 .tls_1_3 => {
574 try hsd.ensure(1 + 3);
575 const cert_req_ctx_len = hsd.decode(u8);
576 if (cert_req_ctx_len != 0) return error.TlsIllegalParameter;
577 },
578 .tls_1_2 => try hsd.ensure(3),
579 else => unreachable,
580 }
581 const certs_size = hsd.decode(u24);
582 var certs_decoder = try hsd.sub(certs_size);
583 while (!certs_decoder.eof()) {
584 try certs_decoder.ensure(3);
585 const cert_size = certs_decoder.decode(u24);
586 const certd = try certs_decoder.sub(cert_size);
587
588 const subject_cert: Certificate = .{
589 .buffer = certd.buf,
590 .index = @intCast(certd.idx),
591 };
592 const subject = try subject_cert.parse();
593 if (cert_index == 0) {
594 // Verify the host on the first certificate.
595 try subject.verifyHostName(host);
596
597 // Keep track of the public key for the
598 // certificate_verify message later.
599 try main_cert_pub_key.init(subject.pub_key_algo, subject.pubKey());
600 } else {
601 try prev_cert.verify(subject, now_sec);
511 }602 }
512 switch (handshake_state) {603
513 .certificate => {},604 if (ca_bundle.verify(subject, now_sec)) |_| {
514 .trust_chain_established => break :cert,605 handshake_state = .trust_chain_established;
515 else => return error.TlsUnexpectedMessage,606 break :cert;
607 } else |err| switch (err) {
608 error.CertificateIssuerNotFound => {},
609 else => |e| return e,
516 }610 }
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 }
547
548 if (ca_bundle.verify(subject, now_sec)) |_| {
549 handshake_state = .trust_chain_established;
550 break :cert;
551 } else |err| switch (err) {
552 error.CertificateIssuerNotFound => {},
553 else => |e| return e,
554 }
555
556 prev_cert = subject;
557 cert_index += 1;
558611
612 prev_cert = subject;
613 cert_index += 1;
614
615 if (tls_version == .tls_1_3) {
559 try certs_decoder.ensure(2);616 try certs_decoder.ensure(2);
560 const total_ext_size = certs_decoder.decode(u16);617 const total_ext_size = certs_decoder.decode(u16);
561 const all_extd = try certs_decoder.sub(total_ext_size);618 const all_extd = try certs_decoder.sub(total_ext_size);
562 _ = all_extd;619 _ = all_extd;
563 }620 }
564 },621 }
565 .certificate_verify => {622 },
566 switch (handshake_state) {623 .server_key_exchange => {
567 .trust_chain_established => handshake_state = .finished,624 if (tls_version != .tls_1_2) return error.TlsUnexpectedMessage;
568 .certificate => return error.TlsCertificateNotVerified,625 if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
569 else => return error.TlsUnexpectedMessage,626 switch (handshake_state) {
570 }627 .trust_chain_established => handshake_state = .server_hello_done,
628 .certificate => return error.TlsCertificateNotVerified,
629 else => return error.TlsUnexpectedMessage,
630 }
571631
572 try hsd.ensure(4);632 switch (handshake_cipher) {
573 const scheme = hsd.decode(tls.SignatureScheme);633 inline else => |*p| p.transcript_hash.update(wrapped_handshake),
574 const sig_len = hsd.decode(u16);634 }
575 try hsd.ensure(sig_len);635 try hsd.ensure(1 + 2 + 1);
576 const encoded_sig = hsd.slice(sig_len);636 const curve_type = hsd.decode(u8);
577 const max_digest_len = 64;637 if (curve_type != 0x03) return error.TlsIllegalParameter; // named_curve
578 var verify_buffer: [64 + 34 + max_digest_len]u8 =638 const named_group = hsd.decode(tls.NamedGroup);
579 ([1]u8{0x20} ** 64) ++639 if (named_group != .secp256r1) return error.TlsIllegalParameter;
580 "TLS 1.3, server CertificateVerify\x00".* ++640 const key_size = hsd.decode(u8);
581 @as([max_digest_len]u8, undefined);641 try hsd.ensure(key_size);
582642 const server_pub_key = hsd.slice(key_size);
583 const verify_bytes = switch (handshake_cipher) {643 try main_cert_pub_key.verifySignature(&hsd, &.{ &client_hello_rand, &server_hello_rand, hsd.buf[0..hsd.idx] });
584 inline else => |*p| v: {644 try key_share.exchange(named_group, server_pub_key);
585 const transcript_digest = p.transcript_hash.peek();645 },
586 verify_buffer[verify_buffer.len - max_digest_len ..][0..transcript_digest.len].* = transcript_digest;646 .server_hello_done => {
587 p.transcript_hash.update(wrapped_handshake);647 if (tls_version != .tls_1_2) return error.TlsUnexpectedMessage;
588 break :v verify_buffer[0 .. verify_buffer.len - max_digest_len + transcript_digest.len];648 if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
589 },649 if (handshake_state != .server_hello_done) return error.TlsUnexpectedMessage;
590 };650 handshake_state = .finished;
591 const main_cert_pub_key = main_cert_pub_key_buf[0..main_cert_pub_key_len];651
592652 const client_key_exchange_msg =
593 switch (scheme) {653 [_]u8{@intFromEnum(tls.ContentType.handshake)} ++ // record content type
594 inline .ecdsa_secp256r1_sha256,654 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version
595 .ecdsa_secp384r1_sha384,655 int2(0x46) ++ // record length
596 => |comptime_scheme| {656 .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++ // handshake type
597 if (main_cert_pub_key_algo != .X9_62_id_ecPublicKey)657 int3(0x42) ++ // params length
598 return error.TlsBadSignatureScheme;658 .{0x41} ++ // pubkey length
599 const Ecdsa = SchemeEcdsa(comptime_scheme);659 key_share.secp256r1_kp.public_key.toUncompressedSec1();
600 const sig = try Ecdsa.Signature.fromDer(encoded_sig);660 // This message is to trick buggy proxies into behaving correctly.
601 const key = try Ecdsa.PublicKey.fromSec1(main_cert_pub_key);661 const client_change_cipher_spec_msg =
602 try sig.verify(verify_bytes, key);662 [_]u8{@intFromEnum(tls.ContentType.change_cipher_spec)} ++ // record content type
603 },663 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version
604 inline .rsa_pss_rsae_sha256,664 int2(1) ++ // record length
605 .rsa_pss_rsae_sha384,665 .{0x01};
606 .rsa_pss_rsae_sha512,666 const pre_master_secret = key_share.getSharedSecret().?;
607 => |comptime_scheme| {667 switch (handshake_cipher) {
608 if (main_cert_pub_key_algo != .rsaEncryption)668 inline else => |*p| {
609 return error.TlsBadSignatureScheme;669 const P = @TypeOf(p.*).A;
610670 p.transcript_hash.update(wrapped_handshake);
611 const Hash = SchemeHash(comptime_scheme);671 p.transcript_hash.update(client_key_exchange_msg[tls.record_header_len..]);
612 const rsa = Certificate.rsa;672 const master_secret = hmacExpandLabel(P.Hmac, pre_master_secret, &.{
613 const components = try rsa.PublicKey.parseDer(main_cert_pub_key);673 "master secret",
614 const exponent = components.exponent;674 &client_hello_rand,
615 const modulus = components.modulus;675 &server_hello_rand,
616 switch (modulus.len) {676 }, 48);
617 inline 128, 256, 512 => |modulus_len| {677 const key_block = hmacExpandLabel(
618 const key = try rsa.PublicKey.fromBytes(exponent, modulus);678 P.Hmac,
619 const sig = rsa.PSSSignature.fromBytes(modulus_len, encoded_sig);679 &master_secret,
620 try rsa.PSSSignature.verify(modulus_len, sig, verify_bytes, key, Hash);680 &.{ "key expansion", &server_hello_rand, &client_hello_rand },
621 },681 @sizeOf(P.Tls_1_2),
622 else => {682 );
623 return error.TlsBadRsaSignatureBitCount;683 const verify_data_len = 12;
624 },684 const client_verify_cleartext =
625 }685 [_]u8{@intFromEnum(tls.HandshakeType.finished)} ++ // handshake type
626 },686 int3(verify_data_len) ++ // verify data length
627 inline .ed25519 => |comptime_scheme| {687 hmacExpandLabel(P.Hmac, &master_secret, &.{ "client finished", &p.transcript_hash.peek() }, verify_data_len);
628 if (main_cert_pub_key_algo != .curveEd25519) return error.TlsBadSignatureScheme;688 p.transcript_hash.update(&client_verify_cleartext);
629 const Eddsa = SchemeEddsa(comptime_scheme);689 p.version = .{ .tls_1_2 = .{
630 if (encoded_sig.len != Eddsa.Signature.encoded_length) return error.InvalidEncoding;690 .server_verify_data = hmacExpandLabel(
631 const sig = Eddsa.Signature.fromBytes(encoded_sig[0..Eddsa.Signature.encoded_length].*);691 P.Hmac,
632 if (main_cert_pub_key.len != Eddsa.PublicKey.encoded_length) return error.InvalidEncoding;692 &master_secret,
633 const key = try Eddsa.PublicKey.fromBytes(main_cert_pub_key[0..Eddsa.PublicKey.encoded_length].*);693 &.{ "server finished", &p.transcript_hash.finalResult() },
634 try sig.verify(verify_bytes, key);694 verify_data_len,
635 },695 ),
636 else => {696 .app_cipher = std.mem.bytesToValue(P.Tls_1_2, &key_block),
637 return error.TlsBadSignatureScheme;697 } };
638 },698 const pv = &p.version.tls_1_2;
639 }699 pending_cipher_state = .application;
640 },700 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
641 .finished => {701 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
642 if (handshake_state != .finished) return error.TlsUnexpectedMessage;702 nonce: {
643 // This message is to trick buggy proxies into behaving correctly.703 var nonce = pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt;
644 const client_change_cipher_spec_msg = [_]u8{704 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
645 @intFromEnum(tls.ContentType.change_cipher_spec),705 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ write_seq, .big);
646 0x03, 0x03, // legacy protocol version706 break :nonce nonce;
647 0x00, 0x01, // length707 } else nonce: {
648 0x01,708 const V = @Vector(P.AEAD.nonce_length, u8);
649 };709 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
650 const app_cipher = switch (handshake_cipher) {710 const operand: V = pad ++ @as([8]u8, @bitCast(big(write_seq)));
651 inline else => |*p, tag| c: {711 break :nonce @as(V, pv.app_cipher.client_write_IV ++ pv.app_cipher.client_salt) ^ operand;
652 const P = @TypeOf(p.*);712 };
713 var client_verify_msg = [_]u8{@intFromEnum(tls.ContentType.handshake)} ++ // record content type
714 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version
715 int2(P.record_iv_length + client_verify_cleartext.len + P.mac_length) ++ // record length
716 nonce[P.fixed_iv_length..].* ++
717 @as([client_verify_cleartext.len + P.mac_length]u8, undefined);
718 P.AEAD.encrypt(
719 client_verify_msg[client_verify_msg.len - P.mac_length -
720 client_verify_cleartext.len ..][0..client_verify_cleartext.len],
721 client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length],
722 &client_verify_cleartext,
723 std.mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int2(client_verify_cleartext.len),
724 nonce,
725 pv.app_cipher.client_write_key,
726 );
727 const all_msgs = client_key_exchange_msg ++ client_change_cipher_spec_msg ++ client_verify_msg;
728 var all_msgs_vec = [_]std.posix.iovec_const{.{
729 .base = &all_msgs,
730 .len = all_msgs.len,
731 }};
732 try stream.writevAll(&all_msgs_vec);
733 },
734 }
735 write_seq += 1;
736 },
737 .certificate_verify => {
738 if (tls_version != .tls_1_3) return error.TlsUnexpectedMessage;
739 if (cipher_state != .handshake) return error.TlsUnexpectedMessage;
740 switch (handshake_state) {
741 .trust_chain_established => handshake_state = .finished,
742 .certificate => return error.TlsCertificateNotVerified,
743 else => return error.TlsUnexpectedMessage,
744 }
745 switch (handshake_cipher) {
746 inline else => |*p| {
747 try main_cert_pub_key.verifySignature(&hsd, &.{
748 " " ** 64 ++ "TLS 1.3, server CertificateVerify\x00",
749 &p.transcript_hash.peek(),
750 });
751 p.transcript_hash.update(wrapped_handshake);
752 },
753 }
754 },
755 .finished => {
756 if (cipher_state == .cleartext) return error.TlsUnexpectedMessage;
757 if (handshake_state != .finished) return error.TlsUnexpectedMessage;
758 // This message is to trick buggy proxies into behaving correctly.
759 const client_change_cipher_spec_msg =
760 [_]u8{@intFromEnum(tls.ContentType.change_cipher_spec)} ++
761 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version
762 int2(1) ++ // length
763 .{0x01};
764 const app_cipher = app_cipher: switch (handshake_cipher) {
765 inline else => |*p, tag| switch (tls_version) {
766 .tls_1_3 => {
767 const pv = &p.version.tls_1_3;
768 const P = @TypeOf(p.*).A;
653 const finished_digest = p.transcript_hash.peek();769 const finished_digest = p.transcript_hash.peek();
654 p.transcript_hash.update(wrapped_handshake);770 p.transcript_hash.update(wrapped_handshake);
655 const expected_server_verify_data = tls.hmac(P.Hmac, &finished_digest, p.server_finished_key);771 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))772 if (!mem.eql(u8, &expected_server_verify_data, hsd.buf)) return error.TlsDecryptError;
657 return error.TlsDecryptError;
658 const handshake_hash = p.transcript_hash.finalResult();773 const handshake_hash = p.transcript_hash.finalResult();
659 const verify_data = tls.hmac(P.Hmac, &handshake_hash, p.client_finished_key);774 const verify_data = tls.hmac(P.Hmac, &handshake_hash, pv.client_finished_key);
660 const out_cleartext = [_]u8{775 const out_cleartext = [_]u8{
661 @intFromEnum(tls.HandshakeType.finished),776 @intFromEnum(tls.HandshakeType.finished),
662 0, 0, verify_data.len, // length777 0, 0, verify_data.len, // length
...@@ -664,67 +779,78 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -664,67 +779,78 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
664779
665 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;780 const wrapped_len = out_cleartext.len + P.AEAD.tag_length;
666781
667 var finished_msg = [_]u8{782 var finished_msg = [_]u8{@intFromEnum(tls.ContentType.application_data)} ++
668 @intFromEnum(tls.ContentType.application_data),783 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ // legacy protocol version
669 0x03, 0x03, // legacy protocol version784 int2(wrapped_len) ++ // byte length of encrypted record
670 0, wrapped_len, // byte length of encrypted record785 @as([wrapped_len]u8, undefined);
671 } ++ @as([wrapped_len]u8, undefined);
672786
673 const ad = finished_msg[0..5];787 const ad = finished_msg[0..tls.record_header_len];
674 const ciphertext = finished_msg[5..][0..out_cleartext.len];788 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 ..];789 const auth_tag = finished_msg[finished_msg.len - P.AEAD.tag_length ..];
676 const nonce = p.client_handshake_iv;790 const nonce = pv.client_handshake_iv;
677 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);791 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, pv.client_handshake_key);
678792
679 const both_msgs = client_change_cipher_spec_msg ++ finished_msg;793 const all_msgs = client_change_cipher_spec_msg ++ finished_msg;
680 var both_msgs_vec = [_]std.posix.iovec_const{.{794 var all_msgs_vec = [_]std.posix.iovec_const{.{
681 .base = &both_msgs,795 .base = &all_msgs,
682 .len = both_msgs.len,796 .len = all_msgs.len,
683 }};797 }};
684 try stream.writevAll(&both_msgs_vec);798 try stream.writevAll(&all_msgs_vec);
685799
686 const client_secret = hkdfExpandLabel(P.Hkdf, p.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);800 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c 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);801 const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);
688 break :c @unionInit(tls.ApplicationCipher, @tagName(tag), .{802 break :app_cipher @unionInit(tls.ApplicationCipher, @tagName(tag), .{ .tls_1_3 = .{
689 .client_secret = client_secret,803 .client_secret = client_secret,
690 .server_secret = server_secret,804 .server_secret = server_secret,
691 .client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length),805 .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),806 .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),807 .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),808 .server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length),
695 });809 } });
696 },810 },
697 };811 .tls_1_2 => {
698 const leftover = d.rest();812 const pv = &p.version.tls_1_2;
699 var client: Client = .{813 try hsd.ensure(12);
700 .read_seq = 0,814 if (!std.mem.eql(u8, hsd.array(12), &pv.server_verify_data)) return error.TlsDecryptError;
701 .write_seq = 0,815 break :app_cipher @unionInit(tls.ApplicationCipher, @tagName(tag), .{ .tls_1_2 = pv.app_cipher });
702 .partial_cleartext_idx = 0,816 },
703 .partial_ciphertext_idx = 0,817 else => unreachable,
704 .partial_ciphertext_end = @intCast(leftover.len),818 },
705 .received_close_notify = false,819 };
706 .application_cipher = app_cipher,820 const leftover = d.rest();
707 .partially_read_buffer = undefined,821 var client: Client = .{
708 };822 .tls_version = tls_version,
709 @memcpy(client.partially_read_buffer[0..leftover.len], leftover);823 .read_seq = switch (tls_version) {
710 return client;824 .tls_1_3 => 0,
711 },825 .tls_1_2 => read_seq,
712 else => {826 else => unreachable,
713 return error.TlsUnexpectedMessage;827 },
714 },828 .write_seq = switch (tls_version) {
715 }829 .tls_1_3 => 0,
716 if (ctd.eof()) break;830 .tls_1_2 => write_seq,
831 else => unreachable,
832 },
833 .partial_cleartext_idx = 0,
834 .partial_ciphertext_idx = 0,
835 .partial_ciphertext_end = @intCast(leftover.len),
836 .received_close_notify = false,
837 .application_cipher = app_cipher,
838 .partially_read_buffer = undefined,
839 };
840 @memcpy(client.partially_read_buffer[0..leftover.len], leftover);
841 return client;
842 },
843 else => return error.TlsUnexpectedMessage,
717 }844 }
845 if (ctd.eof()) break;
718 },846 },
719 else => {847 else => return error.TlsUnexpectedMessage,
720 return error.TlsUnexpectedMessage;
721 },
722 }848 }
723 }849 }
724}850}
725851
726/// Sends TLS-encrypted data to `stream`, which must conform to `StreamInterface`.852/// 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`.853/// 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 {854pub fn write(c: *Client, stream: anytype, bytes: []const u8) !usize {
729 return writeEnd(c, stream, bytes, false);855 return writeEnd(c, stream, bytes, false);
730}856}
...@@ -749,7 +875,7 @@ pub fn writeAllEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !v...@@ -749,7 +875,7 @@ pub fn writeAllEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !v
749}875}
750876
751/// Sends TLS-encrypted data to `stream`, which must conform to `StreamInterface`.877/// 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`.878/// 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,879/// 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 finished880/// which is necessary for the server to distinguish between a properly finished
755/// TLS session, or a truncation attack.881/// TLS session, or a truncation attack.
...@@ -813,62 +939,127 @@ fn prepareCiphertextRecord(...@@ -813,62 +939,127 @@ fn prepareCiphertextRecord(
813 var iovec_end: usize = 0;939 var iovec_end: usize = 0;
814 var bytes_i: usize = 0;940 var bytes_i: usize = 0;
815 switch (c.application_cipher) {941 switch (c.application_cipher) {
816 inline else => |*p| {942 inline else => |*p| switch (c.tls_version) {
817 const P = @TypeOf(p.*);943 .tls_1_3 => {
818 const overhead_len = tls.record_header_len + P.AEAD.tag_length + 1;944 const pv = &p.tls_1_3;
819 const close_notify_alert_reserved = tls.close_notify_alert.len + overhead_len;945 const P = @TypeOf(p.*);
820 while (true) {946 const overhead_len = tls.record_header_len + P.AEAD.tag_length + 1;
821 const encrypted_content_len: u16 = @intCast(@min(947 const close_notify_alert_reserved = tls.close_notify_alert.len + overhead_len;
822 @min(bytes.len - bytes_i, tls.max_ciphertext_inner_record_len),948 while (true) {
823 ciphertext_buf.len -|949 const encrypted_content_len: u16 = @min(
824 (close_notify_alert_reserved + overhead_len + ciphertext_end),950 bytes.len - bytes_i,
825 ));951 tls.max_ciphertext_inner_record_len,
826 if (encrypted_content_len == 0) return .{952 ciphertext_buf.len -|
827 .iovec_end = iovec_end,953 (close_notify_alert_reserved + overhead_len + ciphertext_end),
828 .ciphertext_end = ciphertext_end,954 );
829 .overhead_len = overhead_len,955 if (encrypted_content_len == 0) return .{
830 };956 .iovec_end = iovec_end,
831957 .ciphertext_end = ciphertext_end,
832 @memcpy(cleartext_buf[0..encrypted_content_len], bytes[bytes_i..][0..encrypted_content_len]);958 .overhead_len = overhead_len,
833 cleartext_buf[encrypted_content_len] = @intFromEnum(inner_content_type);959 };
834 bytes_i += encrypted_content_len;960
835 const ciphertext_len = encrypted_content_len + 1;961 @memcpy(cleartext_buf[0..encrypted_content_len], bytes[bytes_i..][0..encrypted_content_len]);
836 const cleartext = cleartext_buf[0..ciphertext_len];962 cleartext_buf[encrypted_content_len] = @intFromEnum(inner_content_type);
837963 bytes_i += encrypted_content_len;
838 const record_start = ciphertext_end;964 const ciphertext_len = encrypted_content_len + 1;
839 const ad = ciphertext_buf[ciphertext_end..][0..5];965 const cleartext = cleartext_buf[0..ciphertext_len];
840 ad.* =966
841 [_]u8{@intFromEnum(tls.ContentType.application_data)} ++967 const record_start = ciphertext_end;
842 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++968 const ad = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];
843 int2(ciphertext_len + P.AEAD.tag_length);969 ad.* =
844 ciphertext_end += ad.len;970 [_]u8{@intFromEnum(tls.ContentType.application_data)} ++
845 const ciphertext = ciphertext_buf[ciphertext_end..][0..ciphertext_len];971 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
846 ciphertext_end += ciphertext_len;972 int2(ciphertext_len + P.AEAD.tag_length);
847 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.AEAD.tag_length];973 ciphertext_end += ad.len;
848 ciphertext_end += auth_tag.len;974 const ciphertext = ciphertext_buf[ciphertext_end..][0..ciphertext_len];
849 const nonce = if (builtin.zig_backend == .stage2_x86_64 and975 ciphertext_end += ciphertext_len;
850 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)976 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.AEAD.tag_length];
851 nonce: {977 ciphertext_end += auth_tag.len;
852 var nonce = p.client_iv;978 const nonce = if (builtin.zig_backend == .stage2_x86_64 and
853 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);979 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
854 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.write_seq, .big);980 nonce: {
855 break :nonce nonce;981 var nonce = pv.client_iv;
856 } else nonce: {982 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
857 const V = @Vector(P.AEAD.nonce_length, u8);983 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.write_seq, .big);
858 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);984 break :nonce nonce;
859 const operand: V = pad ++ @as([8]u8, @bitCast(big(c.write_seq)));985 } else nonce: {
860 break :nonce @as(V, p.client_iv) ^ operand;986 const V = @Vector(P.AEAD.nonce_length, u8);
861 };987 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
862 c.write_seq += 1; // TODO send key_update on overflow988 const operand: V = pad ++ std.mem.toBytes(big(c.write_seq));
863 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, p.client_key);989 break :nonce @as(V, pv.client_iv) ^ operand;
864990 };
865 const record = ciphertext_buf[record_start..ciphertext_end];991 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_key);
866 iovecs[iovec_end] = .{992 c.write_seq += 1; // TODO send key_update on overflow
867 .base = record.ptr,993
868 .len = record.len,994 const record = ciphertext_buf[record_start..ciphertext_end];
869 };995 iovecs[iovec_end] = .{
870 iovec_end += 1;996 .base = record.ptr,
871 }997 .len = record.len,
998 };
999 iovec_end += 1;
1000 }
1001 },
1002 .tls_1_2 => {
1003 const pv = &p.tls_1_2;
1004 const P = @TypeOf(p.*);
1005 const overhead_len = tls.record_header_len + P.record_iv_length + P.mac_length;
1006 const close_notify_alert_reserved = tls.close_notify_alert.len + overhead_len;
1007 while (true) {
1008 const message_len: u16 = @min(
1009 bytes.len - bytes_i,
1010 tls.max_ciphertext_inner_record_len,
1011 ciphertext_buf.len -|
1012 (close_notify_alert_reserved + overhead_len + ciphertext_end),
1013 );
1014 if (message_len == 0) return .{
1015 .iovec_end = iovec_end,
1016 .ciphertext_end = ciphertext_end,
1017 .overhead_len = overhead_len,
1018 };
1019
1020 @memcpy(cleartext_buf[0..message_len], bytes[bytes_i..][0..message_len]);
1021 bytes_i += message_len;
1022 const cleartext = cleartext_buf[0..message_len];
1023
1024 const record_start = ciphertext_end;
1025 const record_header = ciphertext_buf[ciphertext_end..][0..tls.record_header_len];
1026 ciphertext_end += tls.record_header_len;
1027 record_header.* = [_]u8{@intFromEnum(inner_content_type)} ++
1028 int2(@intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
1029 int2(P.record_iv_length + message_len + P.mac_length);
1030 const ad = std.mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int2(message_len);
1031 const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length];
1032 ciphertext_end += P.record_iv_length;
1033 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
1034 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
1035 nonce: {
1036 var nonce = pv.client_write_IV ++ pv.client_salt;
1037 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
1038 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.write_seq, .big);
1039 break :nonce nonce;
1040 } else nonce: {
1041 const V = @Vector(P.AEAD.nonce_length, u8);
1042 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1043 const operand: V = pad ++ @as([8]u8, @bitCast(big(c.write_seq)));
1044 break :nonce @as(V, pv.client_write_IV ++ pv.client_salt) ^ operand;
1045 };
1046 record_iv.* = nonce[P.fixed_iv_length..].*;
1047 const ciphertext = ciphertext_buf[ciphertext_end..][0..message_len];
1048 ciphertext_end += message_len;
1049 const auth_tag = ciphertext_buf[ciphertext_end..][0..P.mac_length];
1050 ciphertext_end += P.mac_length;
1051 P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_write_key);
1052 c.write_seq += 1; // TODO send key_update on overflow
1053
1054 const record = ciphertext_buf[record_start..ciphertext_end];
1055 iovecs[iovec_end] = .{
1056 .base = record.ptr,
1057 .len = record.len,
1058 };
1059 iovec_end += 1;
1060 }
1061 },
1062 else => unreachable,
872 },1063 },
873 }1064 }
874}1065}
...@@ -990,7 +1181,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove...@@ -990,7 +1181,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove
990 // beginning of the buffer will be used for such purposes.1181 // beginning of the buffer will be used for such purposes.
991 const cleartext_buf_len = free_size - ciphertext_buf_len;1182 const cleartext_buf_len = free_size - ciphertext_buf_len;
9921183
993 // Recoup `partially_read_buffer space`. This is necessary because it is assumed1184 // 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.1185 // 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);1186 limitedOverlapCopy(c.partially_read_buffer[0..c.partial_ciphertext_end], c.partial_ciphertext_idx);
996 c.partial_ciphertext_end -= c.partial_ciphertext_idx;1187 c.partial_ciphertext_end -= c.partial_ciphertext_idx;
...@@ -1105,159 +1296,182 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove...@@ -1105,159 +1296,182 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iove
1105 in = 0;1296 in = 0;
1106 continue;1297 continue;
1107 }1298 }
1108 switch (ct) {1299 const cleartext, const inner_ct: tls.ContentType = cleartext: switch (c.application_cipher) {
1300 inline else => |*p| switch (c.tls_version) {
1301 .tls_1_3 => {
1302 const pv = &p.tls_1_3;
1303 const P = @TypeOf(p.*);
1304 const ad = frag[in - tls.record_header_len ..][0..tls.record_header_len];
1305 const ciphertext_len = record_len - P.AEAD.tag_length;
1306 const ciphertext = frag[in..][0..ciphertext_len];
1307 in += ciphertext_len;
1308 const auth_tag = frag[in..][0..P.AEAD.tag_length].*;
1309 const nonce = if (builtin.zig_backend == .stage2_x86_64 and
1310 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
1311 nonce: {
1312 var nonce = pv.server_iv;
1313 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
1314 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.read_seq, .big);
1315 break :nonce nonce;
1316 } else nonce: {
1317 const V = @Vector(P.AEAD.nonce_length, u8);
1318 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1319 const operand: V = pad ++ std.mem.toBytes(big(c.read_seq));
1320 break :nonce @as(V, pv.server_iv) ^ operand;
1321 };
1322 const out_buf = vp.peek();
1323 const cleartext_buf = if (ciphertext.len <= out_buf.len)
1324 out_buf
1325 else
1326 &cleartext_stack_buffer;
1327 const cleartext = cleartext_buf[0..ciphertext.len];
1328 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch
1329 return error.TlsBadRecordMac;
1330 const msg = mem.trimRight(u8, cleartext, "\x00");
1331 break :cleartext .{ msg[0 .. msg.len - 1], @enumFromInt(msg[msg.len - 1]) };
1332 },
1333 .tls_1_2 => {
1334 const pv = &p.tls_1_2;
1335 const P = @TypeOf(p.*);
1336 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
1337 const ad = std.mem.toBytes(big(c.read_seq)) ++
1338 frag[in - tls.record_header_len ..][0 .. 1 + 2] ++
1339 std.mem.toBytes(big(message_len));
1340 const record_iv = frag[in..][0..P.record_iv_length].*;
1341 in += P.record_iv_length;
1342 const masked_read_seq = c.read_seq &
1343 comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length);
1344 const nonce: [P.AEAD.nonce_length]u8 = if (builtin.zig_backend == .stage2_x86_64 and
1345 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)
1346 nonce: {
1347 var nonce = pv.server_write_IV ++ record_iv;
1348 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);
1349 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ masked_read_seq, .big);
1350 break :nonce nonce;
1351 } else nonce: {
1352 const V = @Vector(P.AEAD.nonce_length, u8);
1353 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);
1354 const operand: V = pad ++ @as([8]u8, @bitCast(big(masked_read_seq)));
1355 break :nonce @as(V, pv.server_write_IV ++ record_iv) ^ operand;
1356 };
1357 const ciphertext = frag[in..][0..message_len];
1358 in += message_len;
1359 const auth_tag = frag[in..][0..P.mac_length].*;
1360 in += P.mac_length;
1361 const out_buf = vp.peek();
1362 const cleartext_buf = if (message_len <= out_buf.len)
1363 out_buf
1364 else
1365 &cleartext_stack_buffer;
1366 const cleartext = cleartext_buf[0..ciphertext.len];
1367 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch
1368 return error.TlsBadRecordMac;
1369 break :cleartext .{ cleartext, ct };
1370 },
1371 else => unreachable,
1372 },
1373 };
1374 c.read_seq = try std.math.add(u64, c.read_seq, 1);
1375 switch (inner_ct) {
1109 .alert => {1376 .alert => {
1110 if (in + 2 > frag.len) return error.TlsDecodeError;1377 if (cleartext.len != 2) return error.TlsDecodeError;
1111 const level: tls.AlertLevel = @enumFromInt(frag[in]);1378 const level: tls.AlertLevel = @enumFromInt(cleartext[0]);
1112 const desc: tls.AlertDescription = @enumFromInt(frag[in + 1]);1379 const desc: tls.AlertDescription = @enumFromInt(cleartext[1]);
1380 if (desc == .close_notify) {
1381 c.received_close_notify = true;
1382 c.partial_ciphertext_end = c.partial_ciphertext_idx;
1383 return vp.total;
1384 }
1113 _ = level;1385 _ = level;
11141386
1115 try desc.toError();1387 try desc.toError();
1116 // TODO: handle server-side closures1388 // TODO: handle server-side closures
1117 return error.TlsUnexpectedMessage;1389 return error.TlsUnexpectedMessage;
1118 },1390 },
1119 .application_data => {1391 .handshake => {
1120 const cleartext = switch (c.application_cipher) {1392 var ct_i: usize = 0;
1121 inline else => |*p| c: {1393 while (true) {
1122 const P = @TypeOf(p.*);1394 const handshake_type: tls.HandshakeType = @enumFromInt(cleartext[ct_i]);
1123 const ad = frag[in - 5 ..][0..5];1395 ct_i += 1;
1124 const ciphertext_len = record_len - P.AEAD.tag_length;1396 const handshake_len = mem.readInt(u24, cleartext[ct_i..][0..3], .big);
1125 const ciphertext = frag[in..][0..ciphertext_len];1397 ct_i += 3;
1126 in += ciphertext_len;1398 const next_handshake_i = ct_i + handshake_len;
1127 const auth_tag = frag[in..][0..P.AEAD.tag_length].*;1399 if (next_handshake_i > cleartext.len)
1128 const nonce = if (builtin.zig_backend == .stage2_x86_64 and1400 return error.TlsBadLength;
1129 P.AEAD.nonce_length > comptime std.simd.suggestVectorLength(u8) orelse 1)1401 const handshake = cleartext[ct_i..next_handshake_i];
1130 nonce: {1402 switch (handshake_type) {
1131 var nonce = p.server_iv;1403 .new_session_ticket => {
1132 const operand = std.mem.readInt(u64, nonce[nonce.len - 8 ..], .big);1404 // This client implementation ignores new session tickets.
1133 std.mem.writeInt(u64, nonce[nonce.len - 8 ..], operand ^ c.read_seq, .big);1405 },
1134 break :nonce nonce;1406 .key_update => {
1135 } else nonce: {1407 switch (c.application_cipher) {
1136 const V = @Vector(P.AEAD.nonce_length, u8);1408 inline else => |*p| {
1137 const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8);1409 const pv = &p.tls_1_3;
1138 const operand: V = pad ++ @as([8]u8, @bitCast(big(c.read_seq)));1410 const P = @TypeOf(p.*);
1139 break :nonce @as(V, p.server_iv) ^ operand;1411 const server_secret = hkdfExpandLabel(P.Hkdf, pv.server_secret, "traffic upd", "", P.Hash.digest_length);
1140 };1412 pv.server_secret = server_secret;
1141 const out_buf = vp.peek();1413 pv.server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);
1142 const cleartext_buf = if (ciphertext.len <= out_buf.len)1414 pv.server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);
1143 out_buf
1144 else
1145 &cleartext_stack_buffer;
1146 const cleartext = cleartext_buf[0..ciphertext.len];
1147 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, p.server_key) catch
1148 return error.TlsBadRecordMac;
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 },1415 },
1186 .key_update => {1416 }
1417 c.read_seq = 0;
1418
1419 switch (@as(tls.KeyUpdateRequest, @enumFromInt(handshake[0]))) {
1420 .update_requested => {
1187 switch (c.application_cipher) {1421 switch (c.application_cipher) {
1188 inline else => |*p| {1422 inline else => |*p| {
1423 const pv = &p.tls_1_3;
1189 const P = @TypeOf(p.*);1424 const P = @TypeOf(p.*);
1190 const server_secret = hkdfExpandLabel(P.Hkdf, p.server_secret, "traffic upd", "", P.Hash.digest_length);1425 const client_secret = hkdfExpandLabel(P.Hkdf, pv.client_secret, "traffic upd", "", P.Hash.digest_length);
1191 p.server_secret = server_secret;1426 pv.client_secret = client_secret;
1192 p.server_key = hkdfExpandLabel(P.Hkdf, server_secret, "key", "", P.AEAD.key_length);1427 pv.client_key = hkdfExpandLabel(P.Hkdf, client_secret, "key", "", P.AEAD.key_length);
1193 p.server_iv = hkdfExpandLabel(P.Hkdf, server_secret, "iv", "", P.AEAD.nonce_length);1428 pv.client_iv = hkdfExpandLabel(P.Hkdf, client_secret, "iv", "", P.AEAD.nonce_length);
1194 },
1195 }
1196 c.read_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 },1429 },
1211 .update_not_requested => {},
1212 _ => return error.TlsIllegalParameter,
1213 }1430 }
1431 c.write_seq = 0;
1214 },1432 },
1215 else => {1433 .update_not_requested => {},
1216 return error.TlsUnexpectedMessage;1434 _ => return error.TlsIllegalParameter,
1217 },
1218 }
1219 ct_i = next_handshake_i;
1220 if (ct_i >= cleartext.len - 1) break;
1221 }
1222 },
1223 .application_data => {
1224 // Determine whether the output buffer or a stack
1225 // buffer was used for storing the cleartext.
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 }1435 }
1246 } else {1436 },
1247 // Output buffer was used directly which means no1437 else => {
1248 // memory copying needs to occur, and we can move1438 return error.TlsUnexpectedMessage;
1249 // on to the next ciphertext record.1439 },
1250 vp.next(cleartext.len - 1);1440 }
1251 }1441 ct_i = next_handshake_i;
1252 },1442 if (ct_i >= cleartext.len) break;
1253 else => {
1254 return error.TlsUnexpectedMessage;
1255 },
1256 }1443 }
1257 },1444 },
1258 else => {1445 .application_data => {
1259 return error.TlsUnexpectedMessage;1446 // Determine whether the output buffer or a stack
1447 // buffer was used for storing the cleartext.
1448 if (cleartext.ptr == &cleartext_stack_buffer) {
1449 // Stack buffer was used, so we must copy to the output buffer.
1450 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {
1451 // We have already run out of room in iovecs. Continue
1452 // appending to `partially_read_buffer`.
1453 @memcpy(
1454 c.partially_read_buffer[c.partial_ciphertext_idx..][0..cleartext.len],
1455 cleartext,
1456 );
1457 c.partial_ciphertext_idx = @intCast(c.partial_ciphertext_idx + cleartext.len);
1458 } else {
1459 const amt = vp.put(cleartext);
1460 if (amt < cleartext.len) {
1461 const rest = cleartext[amt..];
1462 c.partial_cleartext_idx = 0;
1463 c.partial_ciphertext_idx = @intCast(rest.len);
1464 @memcpy(c.partially_read_buffer[0..rest.len], rest);
1465 }
1466 }
1467 } else {
1468 // Output buffer was used directly which means no
1469 // memory copying needs to occur, and we can move
1470 // on to the next ciphertext record.
1471 vp.next(cleartext.len);
1472 }
1260 },1473 },
1474 else => return error.TlsUnexpectedMessage,
1261 }1475 }
1262 in = end;1476 in = end;
1263 }1477 }
...@@ -1326,6 +1540,74 @@ inline fn big(x: anytype) @TypeOf(x) {...@@ -1326,6 +1540,74 @@ inline fn big(x: anytype) @TypeOf(x) {
1326 };1540 };
1327}1541}
13281542
1543const KeyShare = struct {
1544 x25519_kp: crypto.dh.X25519.KeyPair,
1545 secp256r1_kp: crypto.sign.ecdsa.EcdsaP256Sha256.KeyPair,
1546 ml_kem768_kp: crypto.kem.ml_kem.MLKem768.KeyPair,
1547 sk_buf: [sk_max_len]u8,
1548 sk_len: std.math.IntFittingRange(0, sk_max_len),
1549
1550 const sk_max_len = @max(
1551 crypto.dh.X25519.shared_length + crypto.kem.ml_kem.MLKem768.shared_length,
1552 crypto.dh.X25519.shared_length,
1553 crypto.ecc.P256.scalar.encoded_length,
1554 );
1555
1556 fn init(seed: [64]u8) error{IdentityElement}!KeyShare {
1557 return .{
1558 .x25519_kp = try .create(seed[0..32].*),
1559 .secp256r1_kp = try .create(seed[32..64].*),
1560 .ml_kem768_kp = try .create(null),
1561 .sk_buf = undefined,
1562 .sk_len = 0,
1563 };
1564 }
1565
1566 fn exchange(
1567 ks: *KeyShare,
1568 named_group: tls.NamedGroup,
1569 server_pub_key: []const u8,
1570 ) error{ TlsIllegalParameter, TlsDecryptFailure }!void {
1571 switch (named_group) {
1572 .x25519_ml_kem768 => {
1573 const xksl = crypto.dh.X25519.public_length;
1574 const hksl = xksl + crypto.kem.ml_kem.MLKem768.ciphertext_length;
1575 if (server_pub_key.len != hksl) return error.TlsIllegalParameter;
1576
1577 const xsk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[0..xksl].*) catch
1578 return error.TlsDecryptFailure;
1579 const hsk = ks.ml_kem768_kp.secret_key.decaps(server_pub_key[xksl..hksl]) catch
1580 return error.TlsDecryptFailure;
1581 @memcpy(ks.sk_buf[0..xsk.len], &xsk);
1582 @memcpy(ks.sk_buf[xsk.len..][0..hsk.len], &hsk);
1583 ks.sk_len = xsk.len + hsk.len;
1584 },
1585 .x25519 => {
1586 const ksl = crypto.dh.X25519.public_length;
1587 if (server_pub_key.len != ksl) return error.TlsIllegalParameter;
1588 const sk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[0..ksl].*) catch
1589 return error.TlsDecryptFailure;
1590 @memcpy(ks.sk_buf[0..sk.len], &sk);
1591 ks.sk_len = sk.len;
1592 },
1593 .secp256r1 => {
1594 const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;
1595 const pk = PublicKey.fromSec1(server_pub_key) catch return error.TlsDecryptFailure;
1596 const mul = pk.p.mulPublic(ks.secp256r1_kp.secret_key.bytes, .big) catch
1597 return error.TlsDecryptFailure;
1598 const sk = mul.affineCoordinates().x.toBytes(.big);
1599 @memcpy(ks.sk_buf[0..sk.len], &sk);
1600 ks.sk_len = sk.len;
1601 },
1602 else => return error.TlsIllegalParameter,
1603 }
1604 }
1605
1606 fn getSharedSecret(ks: *const KeyShare) ?[]const u8 {
1607 return if (ks.sk_len > 0) ks.sk_buf[0..ks.sk_len] else null;
1608 }
1609};
1610
1329fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {1611fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {
1330 return switch (scheme) {1612 return switch (scheme) {
1331 .ecdsa_secp256r1_sha256 => crypto.sign.ecdsa.EcdsaP256Sha256,1613 .ecdsa_secp256r1_sha256 => crypto.sign.ecdsa.EcdsaP256Sha256,
...@@ -1334,11 +1616,20 @@ fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {...@@ -1334,11 +1616,20 @@ fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {
1334 };1616 };
1335}1617}
13361618
1337fn SchemeHash(comptime scheme: tls.SignatureScheme) type {1619fn SchemeRsa(comptime scheme: tls.SignatureScheme) type {
1338 return switch (scheme) {1620 return switch (scheme) {
1339 .rsa_pss_rsae_sha256 => crypto.hash.sha2.Sha256,1621 .rsa_pkcs1_sha256,
1340 .rsa_pss_rsae_sha384 => crypto.hash.sha2.Sha384,1622 .rsa_pkcs1_sha384,
1341 .rsa_pss_rsae_sha512 => crypto.hash.sha2.Sha512,1623 .rsa_pkcs1_sha512,
1624 .rsa_pkcs1_sha1,
1625 => Certificate.rsa.PKCS1v1_5Signature,
1626 .rsa_pss_rsae_sha256,
1627 .rsa_pss_rsae_sha384,
1628 .rsa_pss_rsae_sha512,
1629 .rsa_pss_pss_sha256,
1630 .rsa_pss_pss_sha384,
1631 .rsa_pss_pss_sha512,
1632 => Certificate.rsa.PSSSignature,
1342 else => @compileError("bad scheme"),1633 else => @compileError("bad scheme"),
1343 };1634 };
1344}1635}
...@@ -1350,6 +1641,142 @@ fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {...@@ -1350,6 +1641,142 @@ fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {
1350 };1641 };
1351}1642}
13521643
1644fn SchemeHash(comptime scheme: tls.SignatureScheme) type {
1645 return switch (scheme) {
1646 .rsa_pkcs1_sha256,
1647 .ecdsa_secp256r1_sha256,
1648 .rsa_pss_rsae_sha256,
1649 .rsa_pss_pss_sha256,
1650 => crypto.hash.sha2.Sha256,
1651 .rsa_pkcs1_sha384,
1652 .ecdsa_secp384r1_sha384,
1653 .rsa_pss_rsae_sha384,
1654 .rsa_pss_pss_sha384,
1655 => crypto.hash.sha2.Sha384,
1656 .rsa_pkcs1_sha512,
1657 .ecdsa_secp521r1_sha512,
1658 .rsa_pss_rsae_sha512,
1659 .rsa_pss_pss_sha512,
1660 => crypto.hash.sha2.Sha512,
1661 .rsa_pkcs1_sha1,
1662 .ecdsa_sha1,
1663 => crypto.hash.Sha1,
1664 else => @compileError("bad scheme"),
1665 };
1666}
1667
1668const CertificatePublicKey = struct {
1669 algo: Certificate.AlgorithmCategory,
1670 buf: [600]u8,
1671 len: u16,
1672
1673 fn init(
1674 cert_pub_key: *CertificatePublicKey,
1675 algo: Certificate.AlgorithmCategory,
1676 pub_key: []const u8,
1677 ) error{CertificatePublicKeyInvalid}!void {
1678 if (pub_key.len > cert_pub_key.buf.len) return error.CertificatePublicKeyInvalid;
1679 cert_pub_key.algo = algo;
1680 @memcpy(cert_pub_key.buf[0..pub_key.len], pub_key);
1681 cert_pub_key.len = @intCast(pub_key.len);
1682 }
1683
1684 const VerifyError = error{ TlsDecodeError, TlsBadSignatureScheme, InvalidEncoding } ||
1685 // ecdsa
1686 crypto.errors.EncodingError ||
1687 crypto.errors.NotSquareError ||
1688 crypto.errors.NonCanonicalError ||
1689 SchemeEcdsa(.ecdsa_secp256r1_sha256).Signature.VerifyError ||
1690 SchemeEcdsa(.ecdsa_secp384r1_sha384).Signature.VerifyError ||
1691 // rsa
1692 error{TlsBadRsaSignatureBitCount} ||
1693 Certificate.rsa.PublicKey.ParseDerError ||
1694 Certificate.rsa.PublicKey.FromBytesError ||
1695 Certificate.rsa.PSSSignature.VerifyError ||
1696 Certificate.rsa.PKCS1v1_5Signature.VerifyError ||
1697 // eddsa
1698 SchemeEddsa(.ed25519).Signature.VerifyError;
1699
1700 fn verifySignature(
1701 cert_pub_key: *const CertificatePublicKey,
1702 sigd: *tls.Decoder,
1703 msg: []const []const u8,
1704 ) VerifyError!void {
1705 const pub_key = cert_pub_key.buf[0..cert_pub_key.len];
1706
1707 try sigd.ensure(2 + 2);
1708 const scheme = sigd.decode(tls.SignatureScheme);
1709 const sig_len = sigd.decode(u16);
1710 try sigd.ensure(sig_len);
1711 const encoded_sig = sigd.slice(sig_len);
1712
1713 if (cert_pub_key.algo != @as(Certificate.AlgorithmCategory, switch (scheme) {
1714 .ecdsa_secp256r1_sha256,
1715 .ecdsa_secp384r1_sha384,
1716 => .X9_62_id_ecPublicKey,
1717 .rsa_pkcs1_sha256,
1718 .rsa_pkcs1_sha384,
1719 .rsa_pkcs1_sha512,
1720 .rsa_pss_rsae_sha256,
1721 .rsa_pss_rsae_sha384,
1722 .rsa_pss_rsae_sha512,
1723 .rsa_pkcs1_sha1,
1724 => .rsaEncryption,
1725 .rsa_pss_pss_sha256,
1726 .rsa_pss_pss_sha384,
1727 .rsa_pss_pss_sha512,
1728 => .rsassa_pss,
1729 else => return error.TlsBadSignatureScheme,
1730 })) return error.TlsBadSignatureScheme;
1731
1732 switch (scheme) {
1733 inline .ecdsa_secp256r1_sha256,
1734 .ecdsa_secp384r1_sha384,
1735 => |comptime_scheme| {
1736 const Ecdsa = SchemeEcdsa(comptime_scheme);
1737 const sig = try Ecdsa.Signature.fromDer(encoded_sig);
1738 const key = try Ecdsa.PublicKey.fromSec1(pub_key);
1739 try sig.concatVerify(msg, key);
1740 },
1741 inline .rsa_pkcs1_sha256,
1742 .rsa_pkcs1_sha384,
1743 .rsa_pkcs1_sha512,
1744 .rsa_pss_rsae_sha256,
1745 .rsa_pss_rsae_sha384,
1746 .rsa_pss_rsae_sha512,
1747 .rsa_pss_pss_sha256,
1748 .rsa_pss_pss_sha384,
1749 .rsa_pss_pss_sha512,
1750 .rsa_pkcs1_sha1,
1751 => |comptime_scheme| {
1752 const RsaSignature = SchemeRsa(comptime_scheme);
1753 const Hash = SchemeHash(comptime_scheme);
1754 const PublicKey = Certificate.rsa.PublicKey;
1755 const components = try PublicKey.parseDer(pub_key);
1756 const exponent = components.exponent;
1757 const modulus = components.modulus;
1758 switch (modulus.len) {
1759 inline 128, 256, 512 => |modulus_len| {
1760 const key: PublicKey = try .fromBytes(exponent, modulus);
1761 const sig = RsaSignature.fromBytes(modulus_len, encoded_sig);
1762 try RsaSignature.concatVerify(modulus_len, sig, msg, key, Hash);
1763 },
1764 else => return error.TlsBadRsaSignatureBitCount,
1765 }
1766 },
1767 inline .ed25519 => |comptime_scheme| {
1768 const Eddsa = SchemeEddsa(comptime_scheme);
1769 if (encoded_sig.len != Eddsa.Signature.encoded_length) return error.InvalidEncoding;
1770 const sig = Eddsa.Signature.fromBytes(encoded_sig[0..Eddsa.Signature.encoded_length].*);
1771 if (pub_key.len != Eddsa.PublicKey.encoded_length) return error.InvalidEncoding;
1772 const key = try Eddsa.PublicKey.fromBytes(pub_key[0..Eddsa.PublicKey.encoded_length].*);
1773 try sig.concatVerify(msg, key);
1774 },
1775 else => unreachable,
1776 }
1777 }
1778};
1779
1353/// Abstraction for sending multiple byte buffers to a slice of iovecs.1780/// Abstraction for sending multiple byte buffers to a slice of iovecs.
1354const VecPut = struct {1781const VecPut = struct {
1355 iovecs: []const std.posix.iovec,1782 iovecs: []const std.posix.iovec,
...@@ -1451,16 +1878,22 @@ const cipher_suites = if (crypto.core.aes.has_hardware_support)...@@ -1451,16 +1878,22 @@ const cipher_suites = if (crypto.core.aes.has_hardware_support)
1451 .AEGIS_128L_SHA256,1878 .AEGIS_128L_SHA256,
1452 .AEGIS_256_SHA512,1879 .AEGIS_256_SHA512,
1453 .AES_128_GCM_SHA256,1880 .AES_128_GCM_SHA256,
1881 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
1454 .AES_256_GCM_SHA384,1882 .AES_256_GCM_SHA384,
1883 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
1455 .CHACHA20_POLY1305_SHA256,1884 .CHACHA20_POLY1305_SHA256,
1885 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
1456 })1886 })
1457else1887else
1458 enum_array(tls.CipherSuite, &.{1888 enum_array(tls.CipherSuite, &.{
1459 .CHACHA20_POLY1305_SHA256,1889 .CHACHA20_POLY1305_SHA256,
1890 .ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
1460 .AEGIS_128L_SHA256,1891 .AEGIS_128L_SHA256,
1461 .AEGIS_256_SHA512,1892 .AEGIS_256_SHA512,
1462 .AES_128_GCM_SHA256,1893 .AES_128_GCM_SHA256,
1894 .ECDHE_RSA_WITH_AES_128_GCM_SHA256,
1463 .AES_256_GCM_SHA384,1895 .AES_256_GCM_SHA384,
1896 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
1464 });1897 });
14651898
1466test {1899test {
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));