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(
474474 pub_key: []const u8,
475475) !void {
476476 if (pub_key_algo != .rsaEncryption) return error.CertificateSignatureAlgorithmMismatch;
477 const pub_key_seq = try der.Element.parse(pub_key, 0);
478 if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;
479 const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);
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];
477 const pk_components = try rsa.PublicKey.parseDer(pub_key);
478 const exponent = pk_components.exponent;
479 const modulus = pk_components.modulus;
490480 if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;
491481 if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;
492482
......@@ -688,10 +678,154 @@ test {
688678/// which is licensed under the Apache License Version 2.0, January 2004
689679/// http://www.apache.org/licenses/
690680/// The code has been modified.
691const rsa = struct {
681pub const rsa = struct {
692682 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 {
695829 n: BigInt,
696830 e: BigInt,
697831
......@@ -714,6 +848,24 @@ const rsa = struct {
714848 .e = _e,
715849 };
716850 }
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 }
717869 };
718870
719871 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 {
812964 try BigInt.divFloor(&q, rem, a, n);
813965 }
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
815981 // TODO: flush the toilet
816 const poop = std.heap.page_allocator;
982 pub const poop = std.heap.page_allocator;
817983};
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)
536536 try sig.verify(verify_bytes, key);
537537 },
538538 .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 }
540557 },
541558 else => {
542559 //std.debug.print("signature scheme: {any}\n", .{
......@@ -737,7 +754,7 @@ pub fn writeAll(c: *Client, stream: net.Stream, bytes: []const u8) !void {
737754}
738755
739756pub 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;
741758}
742759
743760/// 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
822839 c.partial_cleartext_idx = 0;
823840 c.partial_ciphertext_idx = 0;
824841 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 });
825846 }
826847 }
827848
......@@ -866,8 +887,9 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
866887
867888 // There might be more bytes inside `in_stack_buffer` that need to be processed,
868889 // 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)];
870 var frag1 = in_stack_buffer[0 .. actual_read_len - frag0.len];
890 const frag0_end = @min(c.partially_read_buffer.len, c.partial_ciphertext_end + actual_read_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];
871893 // We need to decipher frag0 and frag1 but there may be a ciphertext record
872894 // straddling the boundary. We can handle this with two memcpy() calls to
873895 // 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
900922 const record_len = (record_len_byte_0 << 8) | record_len_byte_1;
901923 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;
904927 if (frag1.len < second_len)
905928 return finishRead2(c, first, frag1, vp.total);
906929
907930 mem.copy(u8, frag[0..in], first);
908931 mem.copy(u8, frag[first.len..], frag1[0..second_len]);
932 frag = frag[0..full_record_len];
909933 frag1 = frag1[second_len..];
910934 in = 0;
911935 continue;
......@@ -914,23 +938,35 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
914938 in += 1;
915939 const legacy_version = mem.readIntBig(u16, frag[in..][0..2]);
916940 in += 2;
917 _ = legacy_version;
941 //_ = legacy_version;
918942 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 });
919946 if (record_len > max_ciphertext_len) return error.TlsRecordOverflow;
920947 in += 2;
921948 const end = in + record_len;
922949 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
923953 if (frag.ptr == frag1.ptr)
924954 return finishRead(c, frag, in, vp.total);
925955
926956 // A record straddles the two fragments. Copy into the now-empty first fragment.
927957 const first = frag[in..];
928 const second_len = record_len + tls.ciphertext_record_header_len - first.len;
929 if (frag1.len < second_len)
958 const full_record_len = record_len + tls.ciphertext_record_header_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 });
930964 return finishRead2(c, first, frag1, vp.total);
965 }
931966
932967 mem.copy(u8, frag[0..in], first);
933968 mem.copy(u8, frag[first.len..], frag1[0..second_len]);
969 frag = frag[0..full_record_len];
934970 frag1 = frag1[second_len..];
935971 in = 0;
936972 continue;
......@@ -991,9 +1027,11 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
9911027 const handshake = cleartext[ct_i..next_handshake_i];
9921028 switch (handshake_type) {
9931029 .new_session_ticket => {
1030 std.debug.print("new_session_ticket\n", .{});
9941031 // This client implementation ignores new session tickets.
9951032 },
9961033 .key_update => {
1034 std.debug.print("key_update\n", .{});
9971035 switch (c.application_cipher) {
9981036 inline else => |*p| {
9991037 const P = @TypeOf(p.*);
......@@ -1042,10 +1080,13 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove
10421080 const dest = c.partially_read_buffer[c.partial_ciphertext_idx..];
10431081 mem.copy(u8, dest, msg);
10441082 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});
10451084 } else {
10461085 const amt = vp.put(msg);
1086 std.debug.print("application_data {d} bytes to read buffer\n", .{msg.len});
10471087 if (amt < msg.len) {
10481088 const rest = msg[amt..];
1089 std.debug.print(" {d} bytes to partial buffer\n", .{rest.len});
10491090 c.partial_cleartext_idx = 0;
10501091 c.partial_ciphertext_idx = @intCast(@TypeOf(c.partial_ciphertext_idx), rest.len);
10511092 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
10551096 // Output buffer was used directly which means no
10561097 // memory copying needs to occur, and we can move
10571098 // on to the next ciphertext record.
1099 std.debug.print("application_data {d} bytes directly to read buffer\n", .{cleartext.len - 1});
10581100 vp.next(cleartext.len - 1);
10591101 }
10601102 },
......@@ -1166,10 +1208,6 @@ const VecPut = struct {
11661208 const src = bytes[bytes_i..][0..@min(dest.len, bytes.len - bytes_i)];
11671209 mem.copy(u8, dest, src);
11681210 bytes_i += src.len;
1169 if (bytes_i >= bytes.len) {
1170 vp.total += bytes_i;
1171 return bytes_i;
1172 }
11731211 vp.off += src.len;
11741212 if (vp.off >= v.iov_len) {
11751213 vp.off = 0;
......@@ -1179,6 +1217,10 @@ const VecPut = struct {
11791217 return bytes_i;
11801218 }
11811219 }
1220 if (bytes_i >= bytes.len) {
1221 vp.total += bytes_i;
1222 return bytes_i;
1223 }
11821224 }
11831225 }
11841226
......@@ -1201,17 +1243,11 @@ const VecPut = struct {
12011243 }
12021244
12031245 fn freeSize(vp: VecPut) usize {
1246 if (vp.idx >= vp.iovecs.len) return 0;
12041247 var total: usize = 0;
1205
12061248 total += vp.iovecs[vp.idx].iov_len - vp.off;
1207
1208 if (vp.idx + 1 >= vp.iovecs.len)
1209 return total;
1210
1211 for (vp.iovecs[vp.idx + 1 ..]) |v| {
1212 total += v.iov_len;
1213 }
1214
1249 if (vp.idx + 1 >= vp.iovecs.len) return total;
1250 for (vp.iovecs[vp.idx + 1 ..]) |v| total += v.iov_len;
12151251 return total;
12161252 }
12171253};