quic 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/LICENSE-DEPENDENCIES.txt +101 -0
- data/README.md +69 -6
- data/examples/doq_demo.rb +122 -0
- data/examples/echo_demo.rb +23 -12
- data/examples/handshake_demo.rb +18 -20
- data/examples/io_loop_demo.rb +144 -0
- data/examples/verify_demo.rb +105 -0
- data/ext/quic/connection_client.c +403 -68
- data/ext/quic/extconf.rb +138 -28
- data/ext/quic/patches/picotls/0001-openssl-use-raw-public-key-api-for-X25519.patch +74 -0
- data/ext/quic/quic.c +63 -51
- data/ext/quic/quic.h +35 -25
- data/ext/quic/stream.c +59 -23
- data/ext/quic/stream.h +7 -3
- data/lib/quic/connection.rb +69 -9
- data/lib/quic/settings.rb +23 -3
- data/lib/quic/stream.rb +17 -14
- data/lib/quic/transport_params.rb +1 -1
- data/lib/quic/version.rb +2 -2
- data/lib/quic.rb +1 -1
- data/sig/quic.rbs +42 -5
- metadata +31 -9
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Server certificate verification demo: handshake with a public QUIC server
|
|
4
|
+
# under a few QUIC::Settings verification configurations and report how each
|
|
5
|
+
# one ends.
|
|
6
|
+
#
|
|
7
|
+
# 1. The defaults: verify_mode :peer against the system CA store.
|
|
8
|
+
# 2. ca_file holding only a throwaway CA generated on the spot. The server's
|
|
9
|
+
# chain does not lead to it, so the handshake fails with
|
|
10
|
+
# QUIC::Error::CertificateVerifyFailed.
|
|
11
|
+
# 3. verify_mode :none, which skips verification entirely.
|
|
12
|
+
# 4. ca_file from VERIFY_CA_FILE, when that variable is set.
|
|
13
|
+
#
|
|
14
|
+
# Override the target via env vars:
|
|
15
|
+
# VERIFY_HOST (default cloudflare-quic.com)
|
|
16
|
+
# VERIFY_PORT (default 443)
|
|
17
|
+
# VERIFY_ALPN (default h3)
|
|
18
|
+
# VERIFY_CA_FILE (default unset: step 4 is skipped)
|
|
19
|
+
#
|
|
20
|
+
# Each attempt connects its socket to the address its client resolved, so
|
|
21
|
+
# both talk to the same peer (see examples/handshake_demo.rb).
|
|
22
|
+
#
|
|
23
|
+
# Run with: bundle exec ruby examples/verify_demo.rb
|
|
24
|
+
|
|
25
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
26
|
+
require "quic"
|
|
27
|
+
require "socket"
|
|
28
|
+
require "openssl"
|
|
29
|
+
require "tmpdir"
|
|
30
|
+
|
|
31
|
+
TARGET_HOST = ENV.fetch("VERIFY_HOST", "cloudflare-quic.com")
|
|
32
|
+
TARGET_PORT = Integer(ENV.fetch("VERIFY_PORT", "443"))
|
|
33
|
+
TARGET_ALPN = ENV.fetch("VERIFY_ALPN", "h3")
|
|
34
|
+
TARGET_CA_FILE = ENV["VERIFY_CA_FILE"]
|
|
35
|
+
|
|
36
|
+
# A self-signed CA that no real server chains to, written as PEM into dir.
|
|
37
|
+
def write_untrusted_ca(dir)
|
|
38
|
+
key = OpenSSL::PKey::EC.generate("prime256v1")
|
|
39
|
+
name = OpenSSL::X509::Name.parse("/CN=quic-ruby verify_demo CA")
|
|
40
|
+
cert = OpenSSL::X509::Certificate.new
|
|
41
|
+
cert.version = 2
|
|
42
|
+
cert.serial = 1
|
|
43
|
+
cert.subject = name
|
|
44
|
+
cert.issuer = name
|
|
45
|
+
cert.public_key = key
|
|
46
|
+
cert.not_before = Time.now - 60
|
|
47
|
+
cert.not_after = Time.now + 3600
|
|
48
|
+
factory = OpenSSL::X509::ExtensionFactory.new(cert, cert)
|
|
49
|
+
cert.add_extension(factory.create_extension("basicConstraints", "CA:TRUE", true))
|
|
50
|
+
cert.add_extension(factory.create_extension("keyUsage", "keyCertSign,cRLSign", true))
|
|
51
|
+
cert.sign(key, OpenSSL::Digest.new("SHA256"))
|
|
52
|
+
File.join(dir, "untrusted-ca.pem").tap { |path| File.write(path, cert.to_pem) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# verify_result is an X509_V_ERR_* value; show the matching Ruby constant.
|
|
56
|
+
def verify_result_name(value)
|
|
57
|
+
name = OpenSSL::X509.constants.grep(/\AV_ERR_/).find { |c| OpenSSL::X509.const_get(c) == value }
|
|
58
|
+
name ? "OpenSSL::X509::#{name}" : "unknown"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def handshake(label, settings)
|
|
62
|
+
client = QUIC::Connection::Client.new(
|
|
63
|
+
host: TARGET_HOST, port: TARGET_PORT, address_family: :inet, settings: settings
|
|
64
|
+
)
|
|
65
|
+
addr = client.remote_address
|
|
66
|
+
sock = UDPSocket.new(Socket::AF_INET)
|
|
67
|
+
sock.connect(addr.ip_address, addr.ip_port)
|
|
68
|
+
|
|
69
|
+
puts "== #{label} (#{addr.ip_address}:#{addr.ip_port})"
|
|
70
|
+
puts " verify_mode=#{settings.verify_mode.inspect} ca_file=#{settings.ca_file.inspect} ca_path=#{settings.ca_path.inspect}"
|
|
71
|
+
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
72
|
+
client.bind(sock).run
|
|
73
|
+
elapsed_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000).round(1)
|
|
74
|
+
puts " handshake completed (#{elapsed_ms}ms)"
|
|
75
|
+
client.close
|
|
76
|
+
rescue QUIC::Error::CertificateVerifyFailed => e
|
|
77
|
+
puts " #{e.class}: #{e.message}"
|
|
78
|
+
puts " verify_result=#{e.verify_result.inspect} (#{verify_result_name(e.verify_result)})"
|
|
79
|
+
puts " tls_alert=#{e.tls_alert.inspect}"
|
|
80
|
+
rescue QUIC::Error::CryptoError => e
|
|
81
|
+
puts " #{e.class}: #{e.message} (tls_alert=#{e.tls_alert.inspect})"
|
|
82
|
+
rescue QUIC::Error => e
|
|
83
|
+
puts " #{e.class}: #{e.message}"
|
|
84
|
+
ensure
|
|
85
|
+
sock&.close
|
|
86
|
+
puts
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
puts "libcrypto: #{QUIC.library_versions[:openssl]}"
|
|
90
|
+
puts "Target: #{TARGET_HOST}:#{TARGET_PORT}, alpn #{TARGET_ALPN}"
|
|
91
|
+
puts
|
|
92
|
+
|
|
93
|
+
base = QUIC::Settings.default.with(alpn: [TARGET_ALPN])
|
|
94
|
+
|
|
95
|
+
handshake("system CA store (default)", base)
|
|
96
|
+
|
|
97
|
+
Dir.mktmpdir do |dir|
|
|
98
|
+
handshake("ca_file with an unrelated CA", base.with(ca_file: write_untrusted_ca(dir)))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
handshake("verify_mode :none (not verified)", base.with(verify_mode: :none))
|
|
102
|
+
|
|
103
|
+
if TARGET_CA_FILE && !TARGET_CA_FILE.empty?
|
|
104
|
+
handshake("ca_file from VERIFY_CA_FILE", base.with(ca_file: TARGET_CA_FILE))
|
|
105
|
+
end
|