authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-29 17:56:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-02 16:57:16-07:00
log22e2aaa283646858502ac1075c9657383366005d
tree0b2205d9e61b5732a32a2d5d315e282233517d7b
parente4a9b19a1490d5c41a4d8c10f47ba5639de48404

crypto.tls: support rsa_pss_rsae_sha256 and fixes

* fix eof logic * fix read logic * fix VecPut logic * add some debug prints to remove later

2 files changed, 239 insertions(+), 37 deletions(-)

lib/std/crypto/Certificate.zig+182-16
...@@ -474,19 +474,9 @@ fn verifyRsa(...@@ -474,19 +474,9 @@ fn verifyRsa(
474 pub_key: []const u8,474 pub_key: []const u8,
475) !void {475) !void {
476 if (pub_key_algo != .rsaEncryption) return error.CertificateSignatureAlgorithmMismatch;476 if (pub_key_algo != .rsaEncryption) return error.CertificateSignatureAlgorithmMismatch;
477 const pub_key_seq = try der.Element.parse(pub_key, 0);477 const pk_components = try rsa.PublicKey.parseDer(pub_key);
478 if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;478 const exponent = pk_components.exponent;
479 const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);479 const modulus = pk_components.modulus;
480 if (modulus_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType;
481 const exponent_elem = try der.Element.parse(pub_key, modulus_elem.slice.end);
482 if (exponent_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType;
483 // Skip over meaningless zeroes in the modulus.
484 const modulus_raw = pub_key[modulus_elem.slice.start..modulus_elem.slice.end];
485 const modulus_offset = for (modulus_raw) |byte, i| {
486 if (byte != 0) break i;
487 } else modulus_raw.len;
488 const modulus = modulus_raw[modulus_offset..];
489 const exponent = pub_key[exponent_elem.slice.start..exponent_elem.slice.end];
490 if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;480 if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;
491 if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;481 if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;
492482
...@@ -688,10 +678,154 @@ test {...@@ -688,10 +678,154 @@ test {
688/// which is licensed under the Apache License Version 2.0, January 2004678/// which is licensed under the Apache License Version 2.0, January 2004
689/// http://www.apache.org/licenses/679/// http://www.apache.org/licenses/
690/// The code has been modified.680/// The code has been modified.
691const rsa = struct {681pub const rsa = struct {
692 const BigInt = std.math.big.int.Managed;682 const BigInt = std.math.big.int.Managed;
693683
694 const PublicKey = struct {684 pub const PSSSignature = struct {
685 pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
686 var result = [1]u8{0} ** modulus_len;
687 std.mem.copy(u8, &result, msg);
688 return result;
689 }
690
691 pub fn verify(comptime modulus_len: usize, sig: [modulus_len]u8, msg: []const u8, public_key: PublicKey, comptime Hash: type, allocator: std.mem.Allocator) !void {
692 const mod_bits = try countBits(public_key.n.toConst(), allocator);
693 const em_dec = try encrypt(modulus_len, sig, public_key, allocator);
694
695 try EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash, allocator);
696 }
697
698 fn EMSA_PSS_VERIFY(msg: []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type, allocator: std.mem.Allocator) !void {
699 // TODO
700 // 1. If the length of M is greater than the input limitation for
701 // the hash function (2^61 - 1 octets for SHA-1), output
702 // "inconsistent" and stop.
703
704 // emLen = \ceil(emBits/8)
705 const emLen = ((emBit - 1) / 8) + 1;
706 std.debug.assert(emLen == em.len);
707
708 // 2. Let mHash = Hash(M), an octet string of length hLen.
709 var mHash: [Hash.digest_length]u8 = undefined;
710 Hash.hash(msg, &mHash, .{});
711
712 // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
713 if (emLen < Hash.digest_length + sLen + 2) {
714 return error.InvalidSignature;
715 }
716
717 // 4. If the rightmost octet of EM does not have hexadecimal value
718 // 0xbc, output "inconsistent" and stop.
719 if (em[em.len - 1] != 0xbc) {
720 return error.InvalidSignature;
721 }
722
723 // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM,
724 // and let H be the next hLen octets.
725 const maskedDB = em[0..(emLen - Hash.digest_length - 1)];
726 const h = em[(emLen - Hash.digest_length - 1)..(emLen - 1)];
727
728 // 6. If the leftmost 8emLen - emBits bits of the leftmost octet in
729 // maskedDB are not all equal to zero, output "inconsistent" and
730 // stop.
731 const zero_bits = emLen * 8 - emBit;
732 var mask: u8 = maskedDB[0];
733 var i: usize = 0;
734 while (i < 8 - zero_bits) : (i += 1) {
735 mask = mask >> 1;
736 }
737 if (mask != 0) {
738 return error.InvalidSignature;
739 }
740
741 // 7. Let dbMask = MGF(H, emLen - hLen - 1).
742 const mgf_len = emLen - Hash.digest_length - 1;
743 var mgf_out = try allocator.alloc(u8, ((mgf_len - 1) / Hash.digest_length + 1) * Hash.digest_length);
744 defer allocator.free(mgf_out);
745 var dbMask = try MGF1(mgf_out, h, mgf_len, Hash, allocator);
746
747 // 8. Let DB = maskedDB \xor dbMask.
748 i = 0;
749 while (i < dbMask.len) : (i += 1) {
750 dbMask[i] = maskedDB[i] ^ dbMask[i];
751 }
752
753 // 9. Set the leftmost 8emLen - emBits bits of the leftmost octet
754 // in DB to zero.
755 i = 0;
756 mask = 0;
757 while (i < 8 - zero_bits) : (i += 1) {
758 mask = mask << 1;
759 mask += 1;
760 }
761 dbMask[0] = dbMask[0] & mask;
762
763 // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not
764 // zero or if the octet at position emLen - hLen - sLen - 1 (the
765 // leftmost position is "position 1") does not have hexadecimal
766 // value 0x01, output "inconsistent" and stop.
767 if (dbMask[mgf_len - sLen - 2] != 0x00) {
768 return error.InvalidSignature;
769 }
770
771 if (dbMask[mgf_len - sLen - 1] != 0x01) {
772 return error.InvalidSignature;
773 }
774
775 // 11. Let salt be the last sLen octets of DB.
776 const salt = dbMask[(mgf_len - sLen)..];
777
778 // 12. Let
779 // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
780 // M' is an octet string of length 8 + hLen + sLen with eight
781 // initial zero octets.
782 var m_p = try allocator.alloc(u8, 8 + Hash.digest_length + sLen);
783 defer allocator.free(m_p);
784 std.mem.copy(u8, m_p, &([_]u8{0} ** 8));
785 std.mem.copy(u8, m_p[8..], &mHash);
786 std.mem.copy(u8, m_p[(8 + Hash.digest_length)..], salt);
787
788 // 13. Let H' = Hash(M'), an octet string of length hLen.
789 var h_p: [Hash.digest_length]u8 = undefined;
790 Hash.hash(m_p, &h_p, .{});
791
792 // 14. If H = H', output "consistent". Otherwise, output
793 // "inconsistent".
794 if (!std.mem.eql(u8, h, &h_p)) {
795 return error.InvalidSignature;
796 }
797 }
798
799 fn MGF1(out: []u8, seed: []const u8, len: usize, comptime Hash: type, allocator: std.mem.Allocator) ![]u8 {
800 var counter: usize = 0;
801 var idx: usize = 0;
802 var c: [4]u8 = undefined;
803
804 var hash = try allocator.alloc(u8, seed.len + c.len);
805 defer allocator.free(hash);
806 std.mem.copy(u8, hash, seed);
807 var hashed: [Hash.digest_length]u8 = undefined;
808
809 while (idx < len) {
810 c[0] = @intCast(u8, (counter >> 24) & 0xFF);
811 c[1] = @intCast(u8, (counter >> 16) & 0xFF);
812 c[2] = @intCast(u8, (counter >> 8) & 0xFF);
813 c[3] = @intCast(u8, counter & 0xFF);
814
815 std.mem.copy(u8, hash[seed.len..], &c);
816 Hash.hash(hash, &hashed, .{});
817
818 std.mem.copy(u8, out[idx..], &hashed);
819 idx += hashed.len;
820
821 counter += 1;
822 }
823
824 return out[0..len];
825 }
826 };
827
828 pub const PublicKey = struct {
695 n: BigInt,829 n: BigInt,
696 e: BigInt,830 e: BigInt,
697831
...@@ -714,6 +848,24 @@ const rsa = struct {...@@ -714,6 +848,24 @@ const rsa = struct {
714 .e = _e,848 .e = _e,
715 };849 };
716 }850 }
851
852 pub fn parseDer(pub_key: []const u8) !struct { modulus: []const u8, exponent: []const u8 } {
853 const pub_key_seq = try der.Element.parse(pub_key, 0);
854 if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;
855 const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);
856 if (modulus_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType;
857 const exponent_elem = try der.Element.parse(pub_key, modulus_elem.slice.end);
858 if (exponent_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType;
859 // Skip over meaningless zeroes in the modulus.
860 const modulus_raw = pub_key[modulus_elem.slice.start..modulus_elem.slice.end];
861 const modulus_offset = for (modulus_raw) |byte, i| {
862 if (byte != 0) break i;
863 } else modulus_raw.len;
864 return .{
865 .modulus = modulus_raw[modulus_offset..],
866 .exponent = pub_key[exponent_elem.slice.start..exponent_elem.slice.end],
867 };
868 }
717 };869 };
718870
719 fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey, allocator: std.mem.Allocator) ![modulus_len]u8 {871 fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey, allocator: std.mem.Allocator) ![modulus_len]u8 {
...@@ -812,6 +964,20 @@ const rsa = struct {...@@ -812,6 +964,20 @@ const rsa = struct {
812 try BigInt.divFloor(&q, rem, a, n);964 try BigInt.divFloor(&q, rem, a, n);
813 }965 }
814966
967 fn countBits(a: std.math.big.int.Const, allocator: std.mem.Allocator) !usize {
968 var i: usize = 0;
969 var a_copy = try BigInt.init(allocator);
970 defer a_copy.deinit();
971 try a_copy.copy(a);
972
973 while (!a_copy.eqZero()) {
974 try a_copy.shiftRight(&a_copy, 1);
975 i += 1;
976 }
977
978 return i;
979 }
980
815 // TODO: flush the toilet981 // TODO: flush the toilet
816 const poop = std.heap.page_allocator;982 pub const poop = std.heap.page_allocator;
817};983};
lib/std/crypto/tls/Client.zig+57-21
...@@ -536,7 +536,24 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)...@@ -536,7 +536,24 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
536 try sig.verify(verify_bytes, key);536 try sig.verify(verify_bytes, key);
537 },537 },
538 .rsa_pss_rsae_sha256 => {538 .rsa_pss_rsae_sha256 => {
539 @panic("TODO signature scheme: rsa_pss_rsae_sha256");539 if (main_cert_pub_key_algo != .rsaEncryption)
540 return error.TlsBadSignatureScheme;
541
542 const Hash = crypto.hash.sha2.Sha256;
543 const rsa = Certificate.rsa;
544 const components = try rsa.PublicKey.parseDer(main_cert_pub_key);
545 const exponent = components.exponent;
546 const modulus = components.modulus;
547 switch (modulus.len) {
548 inline 128, 256, 512 => |modulus_len| {
549 const key = try rsa.PublicKey.fromBytes(exponent, modulus, rsa.poop);
550 const sig = rsa.PSSSignature.fromBytes(modulus_len, encoded_sig);
551 try rsa.PSSSignature.verify(modulus_len, sig, verify_bytes, key, Hash, rsa.poop);
552 },
553 else => {
554 return error.TlsBadRsaSignatureBitCount;
555 },
556 }
540 },557 },
541 else => {558 else => {
542 //std.debug.print("signature scheme: {any}\n", .{559 //std.debug.print("signature scheme: {any}\n", .{
...@@ -737,7 +754,7 @@ pub fn writeAll(c: *Client, stream: net.Stream, bytes: []const u8) !void {...@@ -737,7 +754,7 @@ pub fn writeAll(c: *Client, stream: net.Stream, bytes: []const u8) !void {
737}754}
738755
739pub fn eof(c: Client) bool {756pub fn eof(c: Client) bool {
740 return c.received_close_notify and c.partial_ciphertext_end == 0;757 return c.received_close_notify and c.partial_ciphertext_idx >= c.partial_ciphertext_end;
741}758}
742759
743/// Returns the number of bytes read, calling the underlying read function the760/// Returns the number of bytes read, calling the underlying read function the
...@@ -822,6 +839,10 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -822,6 +839,10 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
822 c.partial_cleartext_idx = 0;839 c.partial_cleartext_idx = 0;
823 c.partial_ciphertext_idx = 0;840 c.partial_ciphertext_idx = 0;
824 c.partial_ciphertext_end = 0;841 c.partial_ciphertext_end = 0;
842 } else {
843 std.debug.print("finished giving partial cleartext. {d} bytes ciphertext remain\n", .{
844 c.partial_ciphertext_end - c.partial_ciphertext_idx,
845 });
825 }846 }
826 }847 }
827848
...@@ -866,8 +887,9 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -866,8 +887,9 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
866887
867 // There might be more bytes inside `in_stack_buffer` that need to be processed,888 // There might be more bytes inside `in_stack_buffer` that need to be processed,
868 // but at least frag0 will have one complete ciphertext record.889 // but at least frag0 will have one complete ciphertext record.
869 const frag0 = c.partially_read_buffer[0..@min(c.partially_read_buffer.len, actual_read_len)];890 const frag0_end = @min(c.partially_read_buffer.len, c.partial_ciphertext_end + actual_read_len);
870 var frag1 = in_stack_buffer[0 .. actual_read_len - frag0.len];891 const frag0 = c.partially_read_buffer[c.partial_ciphertext_idx..frag0_end];
892 var frag1 = in_stack_buffer[0..actual_read_len -| first_iov.len];
871 // We need to decipher frag0 and frag1 but there may be a ciphertext record893 // We need to decipher frag0 and frag1 but there may be a ciphertext record
872 // straddling the boundary. We can handle this with two memcpy() calls to894 // straddling the boundary. We can handle this with two memcpy() calls to
873 // assemble the straddling record in between handling the two sides.895 // assemble the straddling record in between handling the two sides.
...@@ -900,12 +922,14 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -900,12 +922,14 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
900 const record_len = (record_len_byte_0 << 8) | record_len_byte_1;922 const record_len = (record_len_byte_0 << 8) | record_len_byte_1;
901 if (record_len > max_ciphertext_len) return error.TlsRecordOverflow;923 if (record_len > max_ciphertext_len) return error.TlsRecordOverflow;
902924
903 const second_len = record_len + tls.ciphertext_record_header_len - first.len;925 const full_record_len = record_len + tls.ciphertext_record_header_len;
926 const second_len = full_record_len - first.len;
904 if (frag1.len < second_len)927 if (frag1.len < second_len)
905 return finishRead2(c, first, frag1, vp.total);928 return finishRead2(c, first, frag1, vp.total);
906929
907 mem.copy(u8, frag[0..in], first);930 mem.copy(u8, frag[0..in], first);
908 mem.copy(u8, frag[first.len..], frag1[0..second_len]);931 mem.copy(u8, frag[first.len..], frag1[0..second_len]);
932 frag = frag[0..full_record_len];
909 frag1 = frag1[second_len..];933 frag1 = frag1[second_len..];
910 in = 0;934 in = 0;
911 continue;935 continue;
...@@ -914,23 +938,35 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -914,23 +938,35 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
914 in += 1;938 in += 1;
915 const legacy_version = mem.readIntBig(u16, frag[in..][0..2]);939 const legacy_version = mem.readIntBig(u16, frag[in..][0..2]);
916 in += 2;940 in += 2;
917 _ = legacy_version;941 //_ = legacy_version;
918 const record_len = mem.readIntBig(u16, frag[in..][0..2]);942 const record_len = mem.readIntBig(u16, frag[in..][0..2]);
943 std.debug.print("ct={any} legacy_version={x} record_len={d}\n", .{
944 ct, legacy_version, record_len,
945 });
919 if (record_len > max_ciphertext_len) return error.TlsRecordOverflow;946 if (record_len > max_ciphertext_len) return error.TlsRecordOverflow;
920 in += 2;947 in += 2;
921 const end = in + record_len;948 const end = in + record_len;
922 if (end > frag.len) {949 if (end > frag.len) {
950 // We need the record header on the next iteration of the loop.
951 in -= tls.ciphertext_record_header_len;
952
923 if (frag.ptr == frag1.ptr)953 if (frag.ptr == frag1.ptr)
924 return finishRead(c, frag, in, vp.total);954 return finishRead(c, frag, in, vp.total);
925955
926 // A record straddles the two fragments. Copy into the now-empty first fragment.956 // A record straddles the two fragments. Copy into the now-empty first fragment.
927 const first = frag[in..];957 const first = frag[in..];
928 const second_len = record_len + tls.ciphertext_record_header_len - first.len;958 const full_record_len = record_len + tls.ciphertext_record_header_len;
929 if (frag1.len < second_len)959 const second_len = full_record_len - first.len;
960 if (frag1.len < second_len) {
961 std.debug.print("end > frag.len finishRead2 end={d} frag.len={d}\n", .{
962 end, frag.len,
963 });
930 return finishRead2(c, first, frag1, vp.total);964 return finishRead2(c, first, frag1, vp.total);
965 }
931966
932 mem.copy(u8, frag[0..in], first);967 mem.copy(u8, frag[0..in], first);
933 mem.copy(u8, frag[first.len..], frag1[0..second_len]);968 mem.copy(u8, frag[first.len..], frag1[0..second_len]);
969 frag = frag[0..full_record_len];
934 frag1 = frag1[second_len..];970 frag1 = frag1[second_len..];
935 in = 0;971 in = 0;
936 continue;972 continue;
...@@ -991,9 +1027,11 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -991,9 +1027,11 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
991 const handshake = cleartext[ct_i..next_handshake_i];1027 const handshake = cleartext[ct_i..next_handshake_i];
992 switch (handshake_type) {1028 switch (handshake_type) {
993 .new_session_ticket => {1029 .new_session_ticket => {
1030 std.debug.print("new_session_ticket\n", .{});
994 // This client implementation ignores new session tickets.1031 // This client implementation ignores new session tickets.
995 },1032 },
996 .key_update => {1033 .key_update => {
1034 std.debug.print("key_update\n", .{});
997 switch (c.application_cipher) {1035 switch (c.application_cipher) {
998 inline else => |*p| {1036 inline else => |*p| {
999 const P = @TypeOf(p.*);1037 const P = @TypeOf(p.*);
...@@ -1042,10 +1080,13 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -1042,10 +1080,13 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
1042 const dest = c.partially_read_buffer[c.partial_ciphertext_idx..];1080 const dest = c.partially_read_buffer[c.partial_ciphertext_idx..];
1043 mem.copy(u8, dest, msg);1081 mem.copy(u8, dest, msg);
1044 c.partial_ciphertext_idx = @intCast(@TypeOf(c.partial_ciphertext_idx), c.partial_ciphertext_idx + msg.len);1082 c.partial_ciphertext_idx = @intCast(@TypeOf(c.partial_ciphertext_idx), c.partial_ciphertext_idx + msg.len);
1083 std.debug.print("application_data {d} bytes to partial buffer\n", .{msg.len});
1045 } else {1084 } else {
1046 const amt = vp.put(msg);1085 const amt = vp.put(msg);
1086 std.debug.print("application_data {d} bytes to read buffer\n", .{msg.len});
1047 if (amt < msg.len) {1087 if (amt < msg.len) {
1048 const rest = msg[amt..];1088 const rest = msg[amt..];
1089 std.debug.print(" {d} bytes to partial buffer\n", .{rest.len});
1049 c.partial_cleartext_idx = 0;1090 c.partial_cleartext_idx = 0;
1050 c.partial_ciphertext_idx = @intCast(@TypeOf(c.partial_ciphertext_idx), rest.len);1091 c.partial_ciphertext_idx = @intCast(@TypeOf(c.partial_ciphertext_idx), rest.len);
1051 mem.copy(u8, &c.partially_read_buffer, rest);1092 mem.copy(u8, &c.partially_read_buffer, rest);
...@@ -1055,6 +1096,7 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove...@@ -1055,6 +1096,7 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
1055 // Output buffer was used directly which means no1096 // Output buffer was used directly which means no
1056 // memory copying needs to occur, and we can move1097 // memory copying needs to occur, and we can move
1057 // on to the next ciphertext record.1098 // on to the next ciphertext record.
1099 std.debug.print("application_data {d} bytes directly to read buffer\n", .{cleartext.len - 1});
1058 vp.next(cleartext.len - 1);1100 vp.next(cleartext.len - 1);
1059 }1101 }
1060 },1102 },
...@@ -1166,10 +1208,6 @@ const VecPut = struct {...@@ -1166,10 +1208,6 @@ const VecPut = struct {
1166 const src = bytes[bytes_i..][0..@min(dest.len, bytes.len - bytes_i)];1208 const src = bytes[bytes_i..][0..@min(dest.len, bytes.len - bytes_i)];
1167 mem.copy(u8, dest, src);1209 mem.copy(u8, dest, src);
1168 bytes_i += src.len;1210 bytes_i += src.len;
1169 if (bytes_i >= bytes.len) {
1170 vp.total += bytes_i;
1171 return bytes_i;
1172 }
1173 vp.off += src.len;1211 vp.off += src.len;
1174 if (vp.off >= v.iov_len) {1212 if (vp.off >= v.iov_len) {
1175 vp.off = 0;1213 vp.off = 0;
...@@ -1179,6 +1217,10 @@ const VecPut = struct {...@@ -1179,6 +1217,10 @@ const VecPut = struct {
1179 return bytes_i;1217 return bytes_i;
1180 }1218 }
1181 }1219 }
1220 if (bytes_i >= bytes.len) {
1221 vp.total += bytes_i;
1222 return bytes_i;
1223 }
1182 }1224 }
1183 }1225 }
11841226
...@@ -1201,17 +1243,11 @@ const VecPut = struct {...@@ -1201,17 +1243,11 @@ const VecPut = struct {
1201 }1243 }
12021244
1203 fn freeSize(vp: VecPut) usize {1245 fn freeSize(vp: VecPut) usize {
1246 if (vp.idx >= vp.iovecs.len) return 0;
1204 var total: usize = 0;1247 var total: usize = 0;
1205
1206 total += vp.iovecs[vp.idx].iov_len - vp.off;1248 total += vp.iovecs[vp.idx].iov_len - vp.off;
12071249 if (vp.idx + 1 >= vp.iovecs.len) return total;
1208 if (vp.idx + 1 >= vp.iovecs.len)1250 for (vp.iovecs[vp.idx + 1 ..]) |v| total += v.iov_len;
1209 return total;
1210
1211 for (vp.iovecs[vp.idx + 1 ..]) |v| {
1212 total += v.iov_len;
1213 }
1214
1215 return total;1251 return total;
1216 }1252 }
1217};1253};