raioquic 0.1.0

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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.containerignore +4 -0
  3. data/.rubocop.yml +93 -0
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Containerfile +6 -0
  7. data/Gemfile +24 -0
  8. data/Gemfile.lock +113 -0
  9. data/LICENSE +28 -0
  10. data/README.md +48 -0
  11. data/Rakefile +16 -0
  12. data/Steepfile +8 -0
  13. data/example/curlcatcher.rb +18 -0
  14. data/example/interoperability/README.md +9 -0
  15. data/example/interoperability/aioquic/aioquic_client.py +47 -0
  16. data/example/interoperability/aioquic/aioquic_server.py +34 -0
  17. data/example/interoperability/key.pem +28 -0
  18. data/example/interoperability/localhost-unasuke-dev.crt +21 -0
  19. data/example/interoperability/quic-go/sample_server.go +61 -0
  20. data/example/interoperability/raioquic_client.rb +42 -0
  21. data/example/interoperability/raioquic_server.rb +43 -0
  22. data/example/parse_curl_example.rb +108 -0
  23. data/lib/raioquic/buffer.rb +202 -0
  24. data/lib/raioquic/core_ext.rb +54 -0
  25. data/lib/raioquic/crypto/README.md +5 -0
  26. data/lib/raioquic/crypto/aesgcm.rb +52 -0
  27. data/lib/raioquic/crypto/backend/aead.rb +52 -0
  28. data/lib/raioquic/crypto/backend.rb +12 -0
  29. data/lib/raioquic/crypto.rb +10 -0
  30. data/lib/raioquic/quic/configuration.rb +81 -0
  31. data/lib/raioquic/quic/connection.rb +2776 -0
  32. data/lib/raioquic/quic/crypto.rb +317 -0
  33. data/lib/raioquic/quic/event.rb +69 -0
  34. data/lib/raioquic/quic/logger.rb +272 -0
  35. data/lib/raioquic/quic/packet.rb +471 -0
  36. data/lib/raioquic/quic/packet_builder.rb +301 -0
  37. data/lib/raioquic/quic/rangeset.rb +113 -0
  38. data/lib/raioquic/quic/recovery.rb +528 -0
  39. data/lib/raioquic/quic/stream.rb +343 -0
  40. data/lib/raioquic/quic.rb +20 -0
  41. data/lib/raioquic/tls.rb +1659 -0
  42. data/lib/raioquic/version.rb +5 -0
  43. data/lib/raioquic.rb +12 -0
  44. data/misc/export_x25519.py +43 -0
  45. data/misc/gen_rfc8448_keypair.rb +90 -0
  46. data/raioquic.gemspec +37 -0
  47. data/sig/raioquic/buffer.rbs +37 -0
  48. data/sig/raioquic/core_ext.rbs +7 -0
  49. data/sig/raioquic/crypto/aesgcm.rbs +20 -0
  50. data/sig/raioquic/crypto/backend/aead.rbs +11 -0
  51. data/sig/raioquic/quic/configuration.rbs +34 -0
  52. data/sig/raioquic/quic/connection.rbs +277 -0
  53. data/sig/raioquic/quic/crypto.rbs +88 -0
  54. data/sig/raioquic/quic/event.rbs +51 -0
  55. data/sig/raioquic/quic/logger.rbs +57 -0
  56. data/sig/raioquic/quic/packet.rbs +157 -0
  57. data/sig/raioquic/quic/packet_builder.rbs +76 -0
  58. data/sig/raioquic/quic/rangeset.rbs +17 -0
  59. data/sig/raioquic/quic/recovery.rbs +142 -0
  60. data/sig/raioquic/quic/stream.rbs +87 -0
  61. data/sig/raioquic/tls.rbs +444 -0
  62. data/sig/raioquic.rbs +9 -0
  63. metadata +121 -0
@@ -0,0 +1,1659 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require "certifi"
5
+ require "tttls1.3"
6
+ require_relative "core_ext"
7
+
8
+ module Raioquic
9
+ # Raioquic::TLS
10
+ # Migrated from auiquic/src/aioquic/tls.py
11
+ module TLS # rubocop:disable Metrics/ModuleLength
12
+ TLS_VERSION_1_2 = 0x0303
13
+ TLS_VERSION_1_3 = 0x0304
14
+ TLS_VERSION_1_3_DRAFT_28 = 0x7f1c
15
+ TLS_VERSION_1_3_DRAFT_27 = 0x7f1b
16
+ TLS_VERSION_1_3_DRAFT_26 = 0x7f1a
17
+
18
+ class AlertDescription
19
+ CLOSE_NOTIFY = 0
20
+ UNEXPECTED_MESSAGE = 10
21
+ BAD_RECORD_MAC = 20
22
+ RECORD_OVERFLOW = 22
23
+ HANDSHAKE_FAILURE = 40
24
+ BAD_CERTIFICATE = 42
25
+ UNSUPPORTED_CERTIFICATE = 43
26
+ CERTIFICATE_REVOKED = 44
27
+ CERTIFICATE_EXPIRED = 45
28
+ CERTIFICATE_UNKNOWN = 46
29
+ ILLEGAL_PARAMETER = 47
30
+ UNKNOWN_CA = 48
31
+ ACCESS_DENIED = 49
32
+ DECODE_ERROR = 50
33
+ DECRYPT_ERROR = 51
34
+ PROTOCOL_VERSION = 70
35
+ INSUFFICIENT_SECURITY = 71
36
+ INTERNAL_ERROR = 80
37
+ INAPPROPRIATE_FALLBACK = 86
38
+ USER_CANCELED = 90
39
+ MISSING_EXTENSION = 109
40
+ UNSUPPORTED_EXTENSION = 110
41
+ UNRECOGNIZED_NAME = 112
42
+ BAD_CERTIFICATE_STATUS_RESPONSE = 113
43
+ UNKNOWN_PSK_IDENTITY = 115
44
+ CERTIFICATE_REQUIRED = 116
45
+ NO_APPLICATION_PROTOCOL = 120
46
+ end
47
+
48
+ # Abstrust class of tls alert protocol message
49
+ class Alert < StandardError
50
+ end
51
+
52
+ # Represent TLS alert bad_certificate
53
+ class AlertBadCertificate < Alert
54
+ def description
55
+ AlertDescription::BAD_CERTIFICATE
56
+ end
57
+ end
58
+
59
+ # Represent TLS alert certificate_expired
60
+ class AlertCertificateExpired < Alert
61
+ def description
62
+ AlertDescription::CERTIFICATE_EXPIRED
63
+ end
64
+ end
65
+
66
+ # Represent TLS alert decrypt_error
67
+ class AlertDecryptError < Alert
68
+ def description
69
+ AlertDescription::DECRYPT_ERROR
70
+ end
71
+ end
72
+
73
+ # Represent TLS alert handshake_failure
74
+ class AlertHandshakeFailure < Alert
75
+ def description
76
+ AlertDescription::HANDSHAKE_FAILURE
77
+ end
78
+ end
79
+
80
+ # Represent TLS alert illegal_parameter
81
+ class AlertIllegalParameter < Alert
82
+ def description
83
+ AlertDescription::ILLEGAL_PARAMETER
84
+ end
85
+ end
86
+
87
+ # Represent TLS alert internal_error
88
+ class AlertInternalError < Alert
89
+ def description
90
+ AlertDescription::INTERNAL_ERROR
91
+ end
92
+ end
93
+
94
+ # Represent TLS alert protocol_version
95
+ class AlertProtocolVersion < Alert
96
+ def description
97
+ AlertDescription::PROTOCOL_VERSION
98
+ end
99
+ end
100
+
101
+ # Represent TLS alert unexpected_message
102
+ class AlertUnexpectedMessage < Alert
103
+ def description
104
+ AlertDescription::UNEXPECTED_MESSAGE
105
+ end
106
+ end
107
+
108
+ class Direction
109
+ DECRYPT = 0
110
+ ENCRYPT = 1
111
+ end
112
+
113
+ class Epoch
114
+ INITIAL = 0
115
+ ZERO_RTT = 1
116
+ HANDSHAKE = 2
117
+ ONE_RTT = 3
118
+ end
119
+
120
+ class State
121
+ CLIENT_HANDSHAKE_START = 0
122
+ CLIENT_EXPECT_SERVER_HELLO = 1
123
+ CLIENT_EXPECT_ENCRYPTED_EXTENSIONS = 2
124
+ CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE = 3
125
+ CLIENT_EXPECT_CERTIFICATE_CERTIFICATE = 4
126
+ CLIENT_EXPECT_CERTIFICATE_VERIFY = 5
127
+ CLIENT_EXPECT_FINISHED = 6
128
+ CLIENT_POST_HANDSHAKE = 7
129
+
130
+ SERVER_EXPECT_CLIENT_HELLO = 8
131
+ SERVER_EXPECT_FINISHED = 9
132
+ SERVER_POST_HANDSHAKE = 10
133
+ end
134
+
135
+ # Load a PEM-encoded private key
136
+ def self.load_pem_private_key(data, password = nil)
137
+ OpenSSL::PKey.read(data, password)
138
+ end
139
+
140
+ # Load a chain of PEM-encoded X509 certificates
141
+ def self.load_pem_x509_certificates(data)
142
+ boundary = "-----END CERTIFICATE-----\n"
143
+ certificates = []
144
+ data.split(boundary).each do |chunk|
145
+ certificates << OpenSSL::X509::Certificate.new(chunk + boundary)
146
+ end
147
+ return certificates
148
+ end
149
+
150
+ def self.load_der_x509_certificate(certificate)
151
+ OpenSSL::X509::Certificate.new(certificate)
152
+ end
153
+
154
+ def self.verify_certificate(certificate:, chain: [], server_name: nil, cadata: nil, cafile: nil, capath: nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
155
+ now = Time.now
156
+ raise AlertCertificateExpired, "Certificate is not valid yet" if now < certificate.not_before
157
+ raise AlertCertificateExpired, "Certificate is no longer valid" if now > certificate.not_after
158
+
159
+ if server_name && !OpenSSL::SSL.verify_certificate_identity(certificate, server_name)
160
+ raise AlertBadCertificate, "hostname '#{server_name}' doesn't match '#{certificate.subject.to_a.find { |a| a[0] == "CN" }[1]}'"
161
+ end
162
+
163
+ store = OpenSSL::X509::Store.new
164
+ store.add_file(Certifi.where)
165
+
166
+ load_pem_x509_certificates(cadata).each { |cert| store.add_cert(cert) } if cadata
167
+ store.add_file(cafile) if cafile
168
+ store.add_path(capath) if capath
169
+
170
+ ctx = OpenSSL::X509::StoreContext.new(store, certificate, chain.compact)
171
+ raise AlertBadCertificate, ctx.error_string unless ctx.verify
172
+
173
+ true
174
+ end
175
+
176
+ class CipherSuite
177
+ AES_128_GCM_SHA256 = 0x1301
178
+ AES_256_GCM_SHA384 = 0x1302
179
+ CHACHA20_POLY1305_SHA256 = 0x1303
180
+ EMPTY_RENEGOTIATION_INFO_SCSV = 0x00ff
181
+ end
182
+
183
+ class CompressionMethod
184
+ NULL = 0
185
+ end
186
+
187
+ class ExtensionType
188
+ SERVER_NAME = 0
189
+ STATUS_REQUEST = 5
190
+ SUPPORTED_GROUPS = 10
191
+ SIGNATURE_ALGORITHMS = 13
192
+ ALPN = 16
193
+ COMPRESS_CERTIFICATE = 27
194
+ PRE_SHARED_KEY = 41
195
+ EARLY_DATA = 42
196
+ SUPPORTED_VERSIONS = 43
197
+ COOKIE = 44
198
+ PSK_KEY_EXCHANGE_MODES = 45
199
+ KEY_SHARE = 51
200
+ QUIC_TRANSPORT_PARAMETERS = 0x0039
201
+ QUIC_TRANSPORT_PARAMETERS_DRAFT = 0xffa5
202
+ ENCRYPTED_SERVER_NAME = 65486
203
+ end
204
+
205
+ class Group
206
+ SECP256R1 = 0x0017
207
+ SECP384R1 = 0x0018
208
+ SECP521R1 = 0x0019
209
+ X25519 = 0x001d
210
+ X448 = 0x001e
211
+ GREASE = 0xaaaa
212
+ end
213
+
214
+ class HandshakeType
215
+ CLIENT_HELLO = 1
216
+ SERVER_HELLO = 2
217
+ NEW_SESSION_TICKET = 4
218
+ END_OF_EARLY_DATA = 5
219
+ ENCRYPTED_EXTENSIONS = 8
220
+ CERTIFICATE = 11
221
+ CERTIFICATE_REQUEST = 13
222
+ CERTIFICATE_VERIFY = 15
223
+ FINISHED = 20
224
+ KEY_UPDATE = 24
225
+ COMPRESSED_CERTIFICATE = 25
226
+ MESSAGE_HASH = 254
227
+ end
228
+
229
+ class PskKeyExchangeMode
230
+ PSK_KE = 0
231
+ PSK_DHE_KE = 1
232
+ end
233
+
234
+ class SignatureAlgorithm
235
+ ECDSA_SECP256R1_SHA256 = 0x0403
236
+ ECDSA_SECP384R1_SHA384 = 0x0503
237
+ ECDSA_SECP521R1_SHA512 = 0x0603
238
+ ED25519 = 0x0807 # unsupported in current ruby?
239
+ ED448 = 0x0808 # unsupported in current ruby?
240
+ RSA_PKCS1_SHA256 = 0x0401
241
+ RSA_PKCS1_SHA384 = 0x0501
242
+ RSA_PKCS1_SHA512 = 0x0601
243
+ RSA_PSS_PSS_SHA256 = 0x0809
244
+ RSA_PSS_PSS_SHA384 = 0x080a
245
+ RSA_PSS_PSS_SHA512 = 0x080b
246
+ RSA_PSS_RSAE_SHA256 = 0x0804
247
+ RSA_PSS_RSAE_SHA384 = 0x0805
248
+ RSA_PSS_RSAE_SHA512 = 0x0806
249
+
250
+ # legacy
251
+ RSA_PKCS1_SHA1 = 0x0201 # unsupported in current ruby?
252
+ SHA1_DSA = 0x0202 # unsupported in current ruby?
253
+ ECDSA_SHA1 = 0x0203 # unsupported in current ruby?
254
+ end
255
+
256
+ def self.pull_block(buf:, capacity:)
257
+ bytes = buf.pull_bytes(capacity)
258
+ length = bytes.bytes_to_int
259
+ ends = buf.tell + length
260
+ yield length
261
+ raise RuntimeError unless buf.tell == ends
262
+ end
263
+
264
+ def self.push_block(buf:, capacity:)
265
+ start = buf.tell + capacity
266
+ buf.seek(start)
267
+ yield
268
+ ends = buf.tell
269
+ length = ends - start
270
+ buf.seek(start - capacity)
271
+ buf.push_bytes(length.to_bytes(capacity))
272
+ buf.seek(ends)
273
+ end
274
+
275
+ def self.pull_list(buf:, capacity:, func:)
276
+ items = []
277
+ pull_block(buf: buf, capacity: capacity) do |length|
278
+ ends = buf.tell + length
279
+ while buf.tell < ends # rubocop:disable Style/WhileUntilModifier
280
+ items.append(func.call)
281
+ end
282
+ end
283
+ items
284
+ end
285
+
286
+ def self.push_list(buf:, capacity:, func:, values:)
287
+ push_block(buf: buf, capacity: capacity) do
288
+ values.each { |value| func.call(value) }
289
+ end
290
+ end
291
+
292
+ # Pull an opaque value prefixed by a length
293
+ def self.pull_opaque(buf:, capacity:)
294
+ bytes = ""
295
+ pull_block(buf: buf, capacity: capacity) do |length|
296
+ bytes = buf.pull_bytes(length)
297
+ end
298
+ return bytes
299
+ end
300
+
301
+ # Push an opaque value prefix by a length.
302
+ def self.push_opaque(buf:, capacity:, value:)
303
+ push_block(buf: buf, capacity: capacity) do
304
+ buf.push_bytes(value)
305
+ end
306
+ end
307
+
308
+ def self.push_extension(buf:, extension_type:)
309
+ buf.push_uint16(extension_type)
310
+ push_block(buf: buf, capacity: 2) do
311
+ yield if block_given?
312
+ end
313
+ end
314
+
315
+ def self.pull_key_share(buf:)
316
+ group = buf.pull_uint16
317
+ data = pull_opaque(buf: buf, capacity: 2)
318
+ return [group, data]
319
+ end
320
+
321
+ def self.push_key_share(buf:, value:)
322
+ buf.push_uint16(value[0])
323
+ push_opaque(buf: buf, capacity: 2, value: value[1])
324
+ end
325
+
326
+ def self.pull_alpn_protocol(buf:)
327
+ return pull_opaque(buf: buf, capacity: 1)
328
+ end
329
+
330
+ def self.push_alpn_protocol(buf:, protocol:)
331
+ push_opaque(buf: buf, capacity: 1, value: protocol)
332
+ end
333
+
334
+ def self.pull_psk_identity(buf:)
335
+ identity = pull_opaque(buf: buf, capacity: 2)
336
+ obfuscated_ticket_age = buf.pull_uint32
337
+ return [identity, obfuscated_ticket_age]
338
+ end
339
+
340
+ def self.push_psk_identity(buf:, entry:)
341
+ push_opaque(buf: buf, capacity: 2, value: entry[0])
342
+ buf.push_uint32(entry[1])
343
+ end
344
+
345
+ def self.pull_psk_binder(buf:)
346
+ pull_opaque(buf: buf, capacity: 1)
347
+ end
348
+
349
+ def self.push_psk_binder(buf:, binder:)
350
+ push_opaque(buf: buf, capacity: 1, value: binder)
351
+ end
352
+
353
+ OfferedPsks = _ = Struct.new( # rubocop:disable Naming/ConstantName
354
+ :identities,
355
+ :binders,
356
+ )
357
+
358
+ ClientHello = _ = Struct.new( # rubocop:disable Naming/ConstantName
359
+ :random,
360
+ :legacy_session_id,
361
+ :cipher_suites,
362
+ :legacy_compression_methods,
363
+ :alpn_protocols,
364
+ :early_data,
365
+ :key_share,
366
+ :pre_shared_key,
367
+ :psk_key_exchange_modes,
368
+ :server_name,
369
+ :signature_algorithms,
370
+ :supported_groups,
371
+ :supported_versions,
372
+ :other_extensions,
373
+ )
374
+
375
+ def self.pull_client_hello(buf) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
376
+ raise RuntimeError unless buf.pull_uint8 == HandshakeType::CLIENT_HELLO
377
+
378
+ hello = ClientHello.new
379
+ pull_block(buf: buf, capacity: 3) do # rubocop:disable Metrics/BlockLength
380
+ raise RuntimeError unless buf.pull_uint16 == TLS_VERSION_1_2
381
+
382
+ hello.random = buf.pull_bytes(32)
383
+ hello.legacy_session_id = pull_opaque(buf: buf, capacity: 1)
384
+ hello.cipher_suites = pull_list(buf: buf, capacity: 2, func: buf.method(:pull_uint16))
385
+ hello.legacy_compression_methods = pull_list(buf: buf, capacity: 1, func: buf.method(:pull_uint8))
386
+ hello.early_data = false
387
+ hello.other_extensions = []
388
+
389
+ after_psk = false
390
+ pull_extension = lambda do # rubocop:disable Metrics/BlockLength
391
+ raise RuntimeError if after_psk
392
+
393
+ extension_type = buf.pull_uint16
394
+ extension_length = buf.pull_uint16
395
+ case extension_type
396
+ when ExtensionType::KEY_SHARE
397
+ hello.key_share = pull_list(buf: buf, capacity: 2, func: -> { pull_key_share(buf: buf) })
398
+ when ExtensionType::SUPPORTED_VERSIONS
399
+ hello.supported_versions = pull_list(buf: buf, capacity: 1, func: buf.method(:pull_uint16))
400
+ when ExtensionType::SIGNATURE_ALGORITHMS
401
+ hello.signature_algorithms = pull_list(buf: buf, capacity: 2, func: buf.method(:pull_uint16))
402
+ when ExtensionType::SUPPORTED_GROUPS
403
+ hello.supported_groups = pull_list(buf: buf, capacity: 2, func: buf.method(:pull_uint16))
404
+ when ExtensionType::PSK_KEY_EXCHANGE_MODES
405
+ hello.psk_key_exchange_modes = pull_list(buf: buf, capacity: 1, func: buf.method(:pull_uint8))
406
+ when ExtensionType::SERVER_NAME
407
+ pull_block(buf: buf, capacity: 2) do
408
+ raise RuntimeError unless buf.pull_uint8 == 0
409
+
410
+ hello.server_name = pull_opaque(buf: buf, capacity: 2)
411
+ end
412
+ when ExtensionType::ALPN
413
+ hello.alpn_protocols = pull_list(buf: buf, capacity: 2, func: -> { pull_alpn_protocol(buf: buf) })
414
+ when ExtensionType::EARLY_DATA
415
+ hello.early_data = true
416
+ when ExtensionType::PRE_SHARED_KEY
417
+ hello.pre_shared_key = OfferedPsks.new.tap do |op|
418
+ op.identities = pull_list(buf: buf, capacity: 2, func: -> { pull_psk_identity(buf: buf) })
419
+ op.binders = pull_list(buf: buf, capacity: 2, func: -> { pull_psk_binder(buf: buf) })
420
+ end
421
+ after_psk = true
422
+ else
423
+ hello.other_extensions << [extension_type, buf.pull_bytes(extension_length)]
424
+ end
425
+ end
426
+ pull_list(buf: buf, capacity: 2, func: pull_extension)
427
+ end
428
+
429
+ return hello
430
+ end
431
+
432
+ # rubocop:disable Metrics/BlockLength
433
+ def self.push_client_hello(buf:, hello:) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
434
+ buf.push_uint8(HandshakeType::CLIENT_HELLO)
435
+ push_block(buf: buf, capacity: 3) do
436
+ buf.push_uint16(TLS_VERSION_1_2)
437
+ buf.push_bytes(hello.random)
438
+ push_opaque(buf: buf, capacity: 1, value: hello.legacy_session_id)
439
+ push_list(buf: buf, capacity: 2, func: buf.method(:push_uint16), values: hello.cipher_suites)
440
+ push_list(buf: buf, capacity: 1, func: buf.method(:push_uint8), values: hello.legacy_compression_methods)
441
+
442
+ # extensions
443
+ push_block(buf: buf, capacity: 2) do
444
+ push_extension(buf: buf, extension_type: ExtensionType::KEY_SHARE) do
445
+ push_list(buf: buf, capacity: 2, func: ->(val) { push_key_share(buf: buf, value: val) }, values: hello.key_share)
446
+ end
447
+
448
+ push_extension(buf: buf, extension_type: ExtensionType::SUPPORTED_VERSIONS) do
449
+ push_list(buf: buf, capacity: 1, func: buf.method(:push_uint16), values: hello.supported_versions)
450
+ end
451
+
452
+ push_extension(buf: buf, extension_type: ExtensionType::SIGNATURE_ALGORITHMS) do
453
+ push_list(buf: buf, capacity: 2, func: buf.method(:push_uint16), values: hello.signature_algorithms)
454
+ end
455
+
456
+ push_extension(buf: buf, extension_type: ExtensionType::SUPPORTED_GROUPS) do
457
+ push_list(buf: buf, capacity: 2, func: buf.method(:push_uint16), values: hello.supported_groups)
458
+ end
459
+
460
+ if hello.psk_key_exchange_modes
461
+ push_extension(buf: buf, extension_type: ExtensionType::PSK_KEY_EXCHANGE_MODES) do
462
+ push_list(buf: buf, capacity: 1, func: buf.method(:push_uint8), values: hello.psk_key_exchange_modes)
463
+ end
464
+ end
465
+
466
+ if hello.server_name
467
+ push_extension(buf: buf, extension_type: ExtensionType::SERVER_NAME) do
468
+ push_block(buf: buf, capacity: 2) do
469
+ buf.push_uint8(0)
470
+ push_opaque(buf: buf, capacity: 2, value: hello.server_name)
471
+ end
472
+ end
473
+ end
474
+
475
+ if hello.alpn_protocols && hello.alpn_protocols.length > 0
476
+ push_extension(buf: buf, extension_type: ExtensionType::ALPN) do
477
+ push_list(buf: buf, capacity: 2, func: ->(proto) { push_alpn_protocol(buf: buf, protocol: proto) }, values: hello.alpn_protocols)
478
+ end
479
+ end
480
+
481
+ hello.other_extensions.each do |extension|
482
+ push_extension(buf: buf, extension_type: extension[0]) do
483
+ buf.push_bytes(extension[1])
484
+ end
485
+ end
486
+
487
+ if hello.early_data
488
+ push_extension(buf: buf, extension_type: ExtensionType::EARLY_DATA) do
489
+ # pass
490
+ end
491
+ end
492
+
493
+ if hello.pre_shared_key
494
+ push_extension(buf: buf, extension_type: ExtensionType::PRE_SHARED_KEY) do
495
+ push_list(buf: buf, capacity: 2, func: ->(val) { push_psk_identity(buf: buf, entry: val) }, values: hello.pre_shared_key.identities)
496
+ push_list(buf: buf, capacity: 2, func: ->(val) { push_psk_binder(buf: buf, binder: val) }, values: hello.pre_shared_key.binders)
497
+ end
498
+ end
499
+ end
500
+ end
501
+ end
502
+ # rubocop:enable Metrics/BlockLength
503
+
504
+ ServerHello = _ = Struct.new( # rubocop:disable Naming/ConstantName
505
+ :random,
506
+ :legacy_session_id,
507
+ :cipher_suite,
508
+ :compression_method,
509
+ :key_share,
510
+ :pre_shared_key,
511
+ :supported_version,
512
+ :other_extensions,
513
+ )
514
+
515
+ def self.pull_server_hello(buf)
516
+ raise RuntimeError unless buf.pull_uint8 == HandshakeType::SERVER_HELLO
517
+
518
+ hello = ServerHello.new
519
+ pull_block(buf: buf, capacity: 3) do
520
+ raise RuntimeError unless buf.pull_uint16 == TLS_VERSION_1_2
521
+
522
+ hello.random = buf.pull_bytes(32)
523
+ hello.legacy_session_id = pull_opaque(buf: buf, capacity: 1)
524
+ hello.cipher_suite = buf.pull_uint16
525
+ hello.compression_method = buf.pull_uint8
526
+ hello.other_extensions = []
527
+
528
+ pull_extension = lambda do
529
+ extension_type = buf.pull_uint16
530
+ extension_length = buf.pull_uint16
531
+ case extension_type
532
+ when ExtensionType::SUPPORTED_VERSIONS
533
+ hello.supported_version = buf.pull_uint16
534
+ when ExtensionType::KEY_SHARE
535
+ hello.key_share = pull_key_share(buf: buf)
536
+ when ExtensionType::PRE_SHARED_KEY
537
+ hello.pre_shared_key = buf.pull_uint16
538
+ else
539
+ hello.other_extensions << [extension_type, buf.pull_bytes(extension_length)]
540
+ end
541
+ end
542
+
543
+ pull_list(buf: buf, capacity: 2, func: pull_extension)
544
+ end
545
+
546
+ return hello
547
+ end
548
+
549
+ def self.push_server_hello(buf:, hello:) # rubocop:disable Metrics/MethodLength
550
+ hello.compression_method ||= CompressionMethod::NULL
551
+ hello.other_extensions ||= []
552
+
553
+ buf.push_uint8(HandshakeType::SERVER_HELLO)
554
+ push_block(buf: buf, capacity: 3) do # rubocop:disable Metrics/BlockLength
555
+ buf.push_uint16(TLS_VERSION_1_2)
556
+ buf.push_bytes(hello.random)
557
+
558
+ push_opaque(buf: buf, capacity: 1, value: hello.legacy_session_id)
559
+ buf.push_uint16(hello.cipher_suite)
560
+ buf.push_uint8(hello.compression_method)
561
+
562
+ # extensions
563
+ push_block(buf: buf, capacity: 2) do
564
+ if hello.supported_version
565
+ push_extension(buf: buf, extension_type: ExtensionType::SUPPORTED_VERSIONS) do
566
+ buf.push_uint16(hello.supported_version)
567
+ end
568
+ end
569
+
570
+ if hello.key_share
571
+ push_extension(buf: buf, extension_type: ExtensionType::KEY_SHARE) do
572
+ push_key_share(buf: buf, value: hello.key_share)
573
+ end
574
+ end
575
+
576
+ if hello.pre_shared_key
577
+ push_extension(buf: buf, extension_type: ExtensionType::PRE_SHARED_KEY) do
578
+ buf.push_uint16(hello.pre_shared_key)
579
+ end
580
+ end
581
+
582
+ hello.other_extensions.each do |extension|
583
+ push_extension(buf: buf, extension_type: extension[0]) do
584
+ buf.push_bytes(extension[1])
585
+ end
586
+ end
587
+ end
588
+ end
589
+ end
590
+
591
+ NewSessionTicket = _ = Struct.new( # rubocop:disable Naming/ConstantName
592
+ :ticket_lifetime,
593
+ :ticket_age_add,
594
+ :ticket_nonce,
595
+ :ticket,
596
+ :max_early_data_size,
597
+ :other_extensions,
598
+ )
599
+
600
+ def self.pull_new_session_ticket(buf)
601
+ new_session_ticket = NewSessionTicket.new
602
+ new_session_ticket.other_extensions = []
603
+
604
+ raise RuntimeError unless buf.pull_uint8 == HandshakeType::NEW_SESSION_TICKET
605
+
606
+ pull_block(buf: buf, capacity: 3) do
607
+ new_session_ticket.ticket_lifetime = buf.pull_uint32
608
+ new_session_ticket.ticket_age_add = buf.pull_uint32
609
+ new_session_ticket.ticket_nonce = pull_opaque(buf: buf, capacity: 1)
610
+ new_session_ticket.ticket = pull_opaque(buf: buf, capacity: 2)
611
+
612
+ pull_extension = lambda do
613
+ extension_type = buf.pull_uint16
614
+ extension_length = buf.pull_uint16
615
+ if extension_type == ExtensionType::EARLY_DATA
616
+ new_session_ticket.max_early_data_size = buf.pull_uint32
617
+ else
618
+ new_session_ticket.other_extensions << [extension_type, buf.pull_bytes(extension_length)]
619
+ end
620
+ end
621
+
622
+ pull_list(buf: buf, capacity: 2, func: pull_extension)
623
+ end
624
+
625
+ return new_session_ticket
626
+ end
627
+
628
+ def self.push_new_session_ticket(buf:, new_session_ticket:)
629
+ buf.push_uint8(HandshakeType::NEW_SESSION_TICKET)
630
+ push_block(buf: buf, capacity: 3) do
631
+ buf.push_uint32(new_session_ticket.ticket_lifetime)
632
+ buf.push_uint32(new_session_ticket.ticket_age_add)
633
+ push_opaque(buf: buf, capacity: 1, value: new_session_ticket.ticket_nonce)
634
+ push_opaque(buf: buf, capacity: 2, value: new_session_ticket.ticket)
635
+
636
+ push_block(buf: buf, capacity: 2) do
637
+ if new_session_ticket.max_early_data_size
638
+ push_extension(buf: buf, extension_type: ExtensionType::EARLY_DATA) do
639
+ buf.push_uint32(new_session_ticket.max_early_data_size)
640
+ end
641
+ end
642
+
643
+ new_session_ticket.other_extensions.each do |extension|
644
+ push_extension(buf: buf, extension_type: extension[0]) do
645
+ buf.push_bytes(extension[1])
646
+ end
647
+ end
648
+ end
649
+ end
650
+ end
651
+
652
+ EncryptedExtensions = _ = Struct.new( # rubocop:disable Naming/ConstantName
653
+ :alpn_protocol,
654
+ :early_data,
655
+ :other_extensions,
656
+ )
657
+
658
+ def self.pull_encrypted_extensions(buf)
659
+ extensions = EncryptedExtensions.new
660
+ extensions.other_extensions = []
661
+
662
+ raise RuntimeError unless buf.pull_uint8 == HandshakeType::ENCRYPTED_EXTENSIONS
663
+
664
+ pull_block(buf: buf, capacity: 3) do
665
+ pull_extensions = lambda do
666
+ extension_type = buf.pull_uint16
667
+ extension_length = buf.pull_uint16
668
+
669
+ case extension_type
670
+ when ExtensionType::ALPN
671
+ extensions.alpn_protocol = pull_list(buf: buf, capacity: 2, func: -> { pull_alpn_protocol(buf: buf) })[0]
672
+ when ExtensionType::EARLY_DATA
673
+ extensions.early_data = true
674
+ else
675
+ extensions.other_extensions << [extension_type, buf.pull_bytes(extension_length)]
676
+ end
677
+ end
678
+
679
+ pull_list(buf: buf, capacity: 2, func: pull_extensions)
680
+ end
681
+ return extensions
682
+ end
683
+
684
+ def self.push_encrypted_extensions(buf:, extensions:)
685
+ buf.push_uint8(HandshakeType::ENCRYPTED_EXTENSIONS)
686
+ push_block(buf: buf, capacity: 3) do
687
+ push_block(buf: buf, capacity: 2) do
688
+ if extensions.alpn_protocol
689
+ push_extension(buf: buf, extension_type: ExtensionType::ALPN) do
690
+ push_list(buf: buf, capacity: 2, func: ->(val) { push_alpn_protocol(buf: buf, protocol: val) }, values: [extensions.alpn_protocol])
691
+ end
692
+ end
693
+
694
+ if extensions.early_data
695
+ push_extension(buf: buf, extension_type: ExtensionType::EARLY_DATA) do
696
+ # pass
697
+ end
698
+ end
699
+
700
+ extensions.other_extensions.each do |extension|
701
+ push_extension(buf: buf, extension_type: extension[0]) do
702
+ buf.push_bytes(extension[1])
703
+ end
704
+ end
705
+ end
706
+ end
707
+ end
708
+
709
+ Certificate = _ = Struct.new( # rubocop:disable Naming/ConstantName
710
+ :request_context,
711
+ :certificates,
712
+ )
713
+
714
+ def self.pull_certificate(buf)
715
+ certificate = Certificate.new
716
+
717
+ raise RuntimeError unless buf.pull_uint8 == HandshakeType::CERTIFICATE
718
+
719
+ pull_block(buf: buf, capacity: 3) do
720
+ certificate.request_context = pull_opaque(buf: buf, capacity: 1)
721
+
722
+ pull_certificate_entry = lambda do
723
+ data = pull_opaque(buf: buf, capacity: 3)
724
+ extensions = pull_opaque(buf: buf, capacity: 2)
725
+ return [data, extensions]
726
+ end
727
+
728
+ certificate.certificates = pull_list(buf: buf, capacity: 3, func: pull_certificate_entry)
729
+ end
730
+
731
+ return certificate
732
+ end
733
+
734
+ def self.push_certificate(buf:, certificate:)
735
+ buf.push_uint8(HandshakeType::CERTIFICATE)
736
+ push_block(buf: buf, capacity: 3) do
737
+ push_opaque(buf: buf, capacity: 1, value: certificate.request_context)
738
+
739
+ push_certificate_entry = lambda do |entry|
740
+ push_opaque(buf: buf, capacity: 3, value: entry[0])
741
+ push_opaque(buf: buf, capacity: 2, value: entry[1])
742
+ end
743
+ push_list(buf: buf, capacity: 3, func: push_certificate_entry, values: certificate.certificates)
744
+ end
745
+ end
746
+
747
+ CertificateVerify = _ = Struct.new( # rubocop:disable Naming/ConstantName
748
+ :algorithm,
749
+ :signature,
750
+ )
751
+
752
+ def self.pull_certificate_verify(buf)
753
+ raise RuntimeError unless buf.pull_uint8 == HandshakeType::CERTIFICATE_VERIFY
754
+
755
+ certificate_verify = CertificateVerify.new
756
+ pull_block(buf: buf, capacity: 3) do
757
+ certificate_verify.algorithm = buf.pull_uint16
758
+ certificate_verify.signature = pull_opaque(buf: buf, capacity: 2)
759
+ end
760
+
761
+ return certificate_verify
762
+ end
763
+
764
+ def self.push_certificate_verify(buf:, verify:)
765
+ buf.push_uint8(HandshakeType::CERTIFICATE_VERIFY)
766
+ push_block(buf: buf, capacity: 3) do
767
+ buf.push_uint16(verify.algorithm)
768
+ push_opaque(buf: buf, capacity: 2, value: verify.signature)
769
+ end
770
+ end
771
+
772
+ Finished = _ = Struct.new( # rubocop:disable Naming/ConstantName
773
+ :verify_data,
774
+ )
775
+
776
+ def self.pull_finished(buf)
777
+ raise RuntimeError unless buf.pull_uint8 == HandshakeType::FINISHED
778
+
779
+ return Finished.new.tap do |f|
780
+ f.verify_data = pull_opaque(buf: buf, capacity: 3)
781
+ end
782
+ end
783
+
784
+ def self.push_finished(buf:, finished:)
785
+ buf.push_uint8(HandshakeType::FINISHED)
786
+ push_opaque(buf: buf, capacity: 3, value: finished.verify_data)
787
+ end
788
+
789
+ # TLS KeySchedule class
790
+ class KeySchedule
791
+ attr_reader :cipher_suite
792
+ attr_accessor :generation
793
+ attr_reader :hash
794
+
795
+ def initialize(cipher_suite)
796
+ @algorithm = TLS.cipher_suite_hash(cipher_suite)
797
+ @cipher_suite = cipher_suite
798
+ @generation = 0
799
+ @hash = @algorithm.new
800
+ @hash_empty_value = @hash.dup.digest("")
801
+ @secret = "\x00" * @hash.digest_length
802
+ end
803
+
804
+ def certificate_verify_data(context_string)
805
+ ("\x20" * 64) + context_string + "\x00" + @hash.dup.digest # rubocop:disable Style/StringConcatenation
806
+ end
807
+
808
+ def finished_verify_data(secret)
809
+ hmac_key = TTTLS13::KeySchedule.hkdf_expand_label(secret, "finished", "", @hash.digest_length, @hash.name)
810
+ OpenSSL::HMAC.digest(@hash.name, hmac_key, @hash.dup.digest)
811
+ end
812
+
813
+ def derive_secret(label)
814
+ TTTLS13::KeySchedule.hkdf_expand_label(@secret, label, @hash.dup.digest, @hash.digest_length, @hash.name)
815
+ end
816
+
817
+ def extract(key_material = nil)
818
+ key_material ||= "\x00" * @hash.digest_length
819
+
820
+ if @generation > 0 # rubocop:disable Style/IfUnlessModifier
821
+ @secret = TTTLS13::KeySchedule.hkdf_expand_label(@secret, "derived", @hash_empty_value, @hash.digest_length, @hash.name)
822
+ end
823
+ @generation += 1
824
+ @secret = OpenSSL::HMAC.digest(@hash.name, @secret, key_material)
825
+ end
826
+
827
+ def update_hash(data)
828
+ @hash.update(data)
829
+ end
830
+ end
831
+
832
+ # KeyScheduleProxy binds several KeySchedule object that has different digest length
833
+ class KeyScheduleProxy
834
+ def initialize(cipher_suites)
835
+ @schedules = cipher_suites.each_with_object({}) do |cipher_suite, hash|
836
+ hash[cipher_suite] = KeySchedule.new(cipher_suite)
837
+ hash
838
+ end
839
+ end
840
+
841
+ def extract(key_material = nil)
842
+ @schedules.each_value { |schedule| schedule.extract(key_material) }
843
+ end
844
+
845
+ def select(cipher_suite)
846
+ @schedules[cipher_suite]
847
+ end
848
+
849
+ def update_hash(data)
850
+ @schedules.each_value { |schedule| schedule.update_hash(data) }
851
+ end
852
+ end
853
+
854
+ CIPHER_SUITES = {
855
+ CipherSuite::AES_128_GCM_SHA256 => OpenSSL::Digest::SHA256,
856
+ CipherSuite::AES_256_GCM_SHA384 => OpenSSL::Digest::SHA384,
857
+ CipherSuite::CHACHA20_POLY1305_SHA256 => OpenSSL::Digest::SHA256,
858
+ }.freeze
859
+
860
+ SIGNATURE_ALGORITHMS = {
861
+ SignatureAlgorithm::ECDSA_SECP256R1_SHA256 => [nil, OpenSSL::Digest::SHA256],
862
+ SignatureAlgorithm::ECDSA_SECP384R1_SHA384 => [nil, OpenSSL::Digest::SHA384],
863
+ SignatureAlgorithm::ECDSA_SECP521R1_SHA512 => [nil, OpenSSL::Digest::SHA512],
864
+ # SignatureAlgorithm::RSA_PKCS1_SHA1 => [nil, OpenSSL::Digest::SHA1], # TODO: unsupported?
865
+ SignatureAlgorithm::RSA_PKCS1_SHA256 => [:pss, OpenSSL::Digest::SHA256],
866
+ SignatureAlgorithm::RSA_PKCS1_SHA384 => [:pss, OpenSSL::Digest::SHA384],
867
+ SignatureAlgorithm::RSA_PKCS1_SHA512 => [:pss, OpenSSL::Digest::SHA512],
868
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA256 => [:pss, OpenSSL::Digest::SHA256],
869
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA384 => [:pss, OpenSSL::Digest::SHA384],
870
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA512 => [:pss, OpenSSL::Digest::SHA512],
871
+ }.freeze
872
+
873
+ GROUP_TO_CURVE = {
874
+ Group::SECP256R1 => "prime256v1",
875
+ Group::SECP384R1 => "secp384r1",
876
+ Group::SECP521R1 => "secp521r1",
877
+ }.freeze
878
+
879
+ CURVE_TO_GROUP = GROUP_TO_CURVE.invert.freeze
880
+
881
+ def self.cipher_suite_hash(cipher_suite)
882
+ CIPHER_SUITES[cipher_suite]
883
+ end
884
+
885
+ def self.decode_public_key(key_share)
886
+ case key_share[0]
887
+ when Group::X25519
888
+ OpenSSL::PKey.read(key_share[1])
889
+ when Group::X448
890
+ raise "X448 did not support yet."
891
+ else
892
+ if GROUP_TO_CURVE.key?(key_share[0])
893
+ group = OpenSSL::PKey::EC::Group.new(GROUP_TO_CURVE[key_share[0]])
894
+ OpenSSL::PKey::EC::Point.new(group, key_share[1])
895
+ end
896
+ end
897
+ end
898
+
899
+ # NOTE: X25519, X448 are not supported
900
+ def self.encode_public_key(public_key)
901
+ if public_key.respond_to?(:oid) && public_key.oid == "X25519"
902
+ [Group::X25519, public_key.public_to_der]
903
+ else
904
+ [CURVE_TO_GROUP[public_key.group.curve_name], public_key.to_octet_string(:uncompressed)]
905
+ end
906
+ end
907
+
908
+ def self.negotiate(supported:, offered: nil, exc: nil)
909
+ if offered
910
+ supported.each do |c|
911
+ return c if offered.include?(c)
912
+ end
913
+ end
914
+
915
+ raise exc if exc
916
+
917
+ return nil
918
+ end
919
+
920
+ def self.signature_algorithm_params(signature_algorithm)
921
+ raise NotImplementedError
922
+ end
923
+
924
+ def self.push_message(key_schedule:, buf:)
925
+ hash_start = buf.tell
926
+ yield
927
+ key_schedule.update_hash(buf.data_slice(start: hash_start, ends: buf.tell))
928
+ end
929
+
930
+ SessionTicket = _ = Struct.new( # rubocop:disable Naming/ConstantName
931
+ :age_add,
932
+ :cipher_suite,
933
+ :not_valid_after,
934
+ :not_valid_before,
935
+ :resumption_secret,
936
+ :server_name,
937
+ :ticket,
938
+ :max_early_data_size,
939
+ :other_extensions,
940
+ ) do
941
+ def is_valid
942
+ now = Time.now
943
+ now >= not_valid_before && now <= not_valid_after
944
+ end
945
+
946
+ def obfuscated_age
947
+ age = (Time.now - not_valid_before) * 1000
948
+ return (age + age_add) % (1 << 32)
949
+ end
950
+ end
951
+
952
+ # Represent TLS server-side or client-side peer
953
+ class Context
954
+ attr_reader :session_resumed
955
+ attr_reader :enc_key
956
+ attr_reader :dec_key
957
+ attr_reader :key_schedule
958
+ attr_reader :alpn_negotiated
959
+ attr_reader :received_extensions
960
+ attr_reader :early_data_accepted
961
+
962
+ attr_accessor :state
963
+ attr_accessor :handshake_extensions
964
+ attr_accessor :certificate
965
+ attr_accessor :certificate_chain
966
+ attr_accessor :certificate_private_key
967
+ attr_accessor :supported_groups
968
+ attr_accessor :supported_versions
969
+ attr_accessor :signature_algorithms
970
+ attr_accessor :new_session_ticket_cb
971
+ attr_accessor :get_session_ticket_cb
972
+ attr_accessor :session_ticket
973
+ attr_accessor :alpn_cb
974
+ attr_accessor :update_traffic_key_cb
975
+
976
+ def initialize(is_client:, alpn_protocols: [], cadata: nil, cafile: nil, capath: nil, cipher_suites: nil, logger: nil, max_early_data: nil, server_name: nil, verify_mode: nil) # rubocop:disable Layout/LineLength, Metrics/MethodLength
977
+ @alpn_protocols = alpn_protocols
978
+ @cadata = cadata
979
+ @cafile = cafile
980
+ @capath = capath
981
+ @certificate = nil
982
+ @certificate_chain = []
983
+ @certificate_private_key = nil
984
+ @handshake_extensions = []
985
+ @max_early_data = max_early_data
986
+ @session_ticket = nil
987
+ @server_name = server_name
988
+ @verify_mode = if verify_mode
989
+ verify_mode
990
+ else
991
+ is_client ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
992
+ end
993
+ @alpn_cb = nil
994
+ @get_session_ticket_cb = nil
995
+ @new_session_ticket_cb = nil
996
+ @update_traffic_key_cb = ->(direction: _direction, epoch: _epoch, cipher_suite: _cipher_suite, secret: _secret) {}
997
+ @cipher_suites =
998
+ cipher_suites || [
999
+ CipherSuite::AES_256_GCM_SHA384,
1000
+ CipherSuite::AES_128_GCM_SHA256,
1001
+ CipherSuite::CHACHA20_POLY1305_SHA256,
1002
+ ]
1003
+ @legacy_compression_methods = [CompressionMethod::NULL]
1004
+ @psk_key_exchange_modes = [PskKeyExchangeMode::PSK_DHE_KE]
1005
+ @signature_algorithms = [
1006
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA256,
1007
+ SignatureAlgorithm::ECDSA_SECP256R1_SHA256,
1008
+ SignatureAlgorithm::RSA_PKCS1_SHA256,
1009
+ SignatureAlgorithm::RSA_PKCS1_SHA1,
1010
+ ]
1011
+ @supported_groups = [Group::SECP256R1]
1012
+ @supported_versions = [TLS_VERSION_1_3]
1013
+
1014
+ # state
1015
+ @alpn_negotiated = nil
1016
+ @early_data_accepted = false
1017
+ @key_schedule = nil
1018
+ @key_schedule_psk = nil
1019
+ @received_extensions = nil
1020
+ @key_schedule_proxy = nil
1021
+ @new_session_ticket = nil
1022
+ @peer_certificate = nil
1023
+ @peer_certificate_chain = []
1024
+ @receive_buffer = ""
1025
+ @session_resumed = false
1026
+ @enc_key = nil
1027
+ @dec_key = nil
1028
+ @logger = logger
1029
+ @ec_key = nil
1030
+ @ec_private_key = nil
1031
+ @x25519_private_key = nil
1032
+ @x448_private_key = nil
1033
+
1034
+ if is_client
1035
+ @client_random = Random.urandom(32)
1036
+ @legacy_session_id = ""
1037
+ @state = State::CLIENT_HANDSHAKE_START
1038
+ else
1039
+ @client_random = nil
1040
+ @legacy_session_id = nil
1041
+ @state = State::SERVER_EXPECT_CLIENT_HELLO
1042
+ end
1043
+ end
1044
+
1045
+ # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Style/GuardClause
1046
+ def handle_message(input_data:, output_buf:)
1047
+ if @state == State::CLIENT_HANDSHAKE_START
1048
+ client_send_hello(output_buf[Epoch::INITIAL])
1049
+ return
1050
+ end
1051
+
1052
+ @receive_buffer += input_data
1053
+ while @receive_buffer.bytesize >= 4
1054
+ # determine message length
1055
+ message_type = @receive_buffer[0].bytes_to_int
1056
+ message_length = 4 + @receive_buffer[1...4].bytes_to_int
1057
+
1058
+ # check message is complete
1059
+ break if @receive_buffer.bytesize < message_length
1060
+
1061
+ message = @receive_buffer[0...message_length]
1062
+ @receive_buffer = @receive_buffer[message_length..]
1063
+
1064
+ input_buf = Buffer.new(data: message)
1065
+
1066
+ # client status
1067
+ case @state
1068
+ when State::CLIENT_EXPECT_SERVER_HELLO
1069
+ if message_type == HandshakeType::SERVER_HELLO
1070
+ client_handle_hello(input_buf: input_buf, output_buf: output_buf[Epoch::INITIAL])
1071
+ else
1072
+ raise AlertUnexpectedMessage
1073
+ end
1074
+ when State::CLIENT_EXPECT_ENCRYPTED_EXTENSIONS
1075
+ if message_type == HandshakeType::ENCRYPTED_EXTENSIONS
1076
+ client_handle_encrypted_extensions(input_buf)
1077
+ else
1078
+ raise AlertUnexpectedMessage
1079
+ end
1080
+ when State::CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE
1081
+ if message_type == HandshakeType::CERTIFICATE
1082
+ client_handle_certificate(input_buf)
1083
+ else
1084
+ # FIXME: handle certificate request
1085
+ raise AlertUnexpectedMessage
1086
+ end
1087
+ when State::CLIENT_EXPECT_CERTIFICATE_VERIFY
1088
+ if message_type == HandshakeType::CERTIFICATE_VERIFY
1089
+ client_handle_certificate_verify(input_buf)
1090
+ else
1091
+ raise AlertUnexpectedMessage
1092
+ end
1093
+ when State::CLIENT_EXPECT_FINISHED
1094
+ if message_type == HandshakeType::FINISHED
1095
+ client_handle_finished(input_buf: input_buf, output_buf: output_buf[Epoch::HANDSHAKE])
1096
+ else
1097
+ raise AlertUnexpectedMessage
1098
+ end
1099
+ when State::CLIENT_POST_HANDSHAKE
1100
+ if message_type == HandshakeType::NEW_SESSION_TICKET
1101
+ client_handle_new_session_ticket(input_buf)
1102
+ else
1103
+ raise AlertUnexpectedMessage
1104
+ end
1105
+
1106
+ # server state
1107
+ when State::SERVER_EXPECT_CLIENT_HELLO
1108
+ if message_type == HandshakeType::CLIENT_HELLO
1109
+ server_handle_hello(
1110
+ input_buf: input_buf,
1111
+ initial_buf: output_buf[Epoch::INITIAL],
1112
+ handshake_buf: output_buf[Epoch::HANDSHAKE],
1113
+ onertt_buf: output_buf[Epoch::ONE_RTT],
1114
+ )
1115
+ else
1116
+ raise AlertUnexpectedMessage
1117
+ end
1118
+ when State::SERVER_EXPECT_FINISHED
1119
+ if message_type == HandshakeType::FINISHED
1120
+ server_handle_finished(input_buf: input_buf, output_buf: output_buf[Epoch::ONE_RTT])
1121
+ else
1122
+ raise AlertUnexpectedMessage
1123
+ end
1124
+ when State::SERVER_POST_HANDSHAKE
1125
+ raise AlertUnexpectedMessage
1126
+ end
1127
+ end
1128
+ end
1129
+ # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Style/GuardClause
1130
+
1131
+ def build_session_ticket(new_session_ticket:, other_extensions:)
1132
+ resumption_master_secret = @key_schedule&.derive_secret("res master")
1133
+ resumption_secret = TTTLS13::KeySchedule.hkdf_expand_label(
1134
+ resumption_master_secret, "resumption", new_session_ticket.ticket_nonce, @key_schedule.hash.digest_length, @key_schedule.hash.name,
1135
+ )
1136
+
1137
+ timestamp = Time.now
1138
+ return SessionTicket.new.tap do |ticket|
1139
+ ticket.age_add = new_session_ticket.ticket_age_add
1140
+ ticket.cipher_suite = @key_schedule.cipher_suite
1141
+ ticket.max_early_data_size = new_session_ticket.max_early_data_size
1142
+ ticket.not_valid_after = timestamp + new_session_ticket.ticket_lifetime
1143
+ ticket.not_valid_before = timestamp
1144
+ ticket.other_extensions = other_extensions
1145
+ ticket.resumption_secret = resumption_secret
1146
+ ticket.server_name = @server_name
1147
+ ticket.ticket = new_session_ticket.ticket
1148
+ end
1149
+ end
1150
+
1151
+ # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1152
+ def client_send_hello(output_buf)
1153
+ key_share = []
1154
+ supported_groups = []
1155
+
1156
+ @supported_groups.each do |group|
1157
+ case group
1158
+ when Group::SECP256R1
1159
+ ec = OpenSSL::PKey::EC.generate(GROUP_TO_CURVE[Group::SECP256R1])
1160
+ @ec_key = ec
1161
+ @ec_private_key = ec.private_key
1162
+ key_share << TLS.encode_public_key(ec.public_key)
1163
+ supported_groups << Group::SECP256R1
1164
+ when Group::X25519, Group::X448
1165
+ raise "unsupported"
1166
+ when Group::GREASE
1167
+ key_share << [Group::GREASE, '\x00']
1168
+ supported_groups << Group::GREASE
1169
+ end
1170
+ end
1171
+ raise RuntimeError if key_share.size == 0
1172
+
1173
+ hello = ClientHello.new.tap do |h|
1174
+ h.random = @client_random
1175
+ h.legacy_session_id = @legacy_session_id
1176
+ h.cipher_suites = @cipher_suites
1177
+ h.legacy_compression_methods = @legacy_compression_methods
1178
+ h.alpn_protocols = @alpn_protocols
1179
+ h.early_data = false
1180
+ h.key_share = key_share
1181
+ h.psk_key_exchange_modes = (@session_ticket || @new_session_ticket_cb ? @psk_key_exchange_modes : nil)
1182
+ h.server_name = @server_name
1183
+ h.signature_algorithms = @signature_algorithms
1184
+ h.supported_groups = supported_groups
1185
+ h.supported_versions = @supported_versions
1186
+ h.other_extensions = @handshake_extensions
1187
+ end
1188
+
1189
+ # PSK
1190
+ if @session_ticket&.is_valid
1191
+ @key_schedule_psk = KeySchedule.new(@session_ticket.cipher_suite)
1192
+ @key_schedule_psk.extract(@session_ticket.resumption_secret)
1193
+ binder_key = @key_schedule_psk.derive_secret("res binder")
1194
+ binder_length = @key_schedule_psk.hash.digest_length
1195
+
1196
+ # update hello
1197
+ hello.early_data = true if @session_ticket.max_early_data_size
1198
+ hello.pre_shared_key = OfferedPsks.new.tap do |psks|
1199
+ psks.identities = [[@session_ticket.ticket, @session_ticket.obfuscated_age]]
1200
+ psks.binders = ["\x00" * binder_length]
1201
+ end
1202
+
1203
+ # serialize hello withouit binder
1204
+ tmp_buf = Buffer.new(capacity: 1024)
1205
+ TLS.push_client_hello(buf: tmp_buf, hello: hello)
1206
+
1207
+ # calculate binder
1208
+ hash_offset = tmp_buf.tell - binder_length - 3
1209
+ @key_schedule_psk.update_hash(tmp_buf.data_slice(start: 0, ends: hash_offset))
1210
+ binder = @key_schedule_psk.finished_verify_data(binder_key)
1211
+ hello.pre_shared_key.binders[0] = binder
1212
+ @key_schedule_psk.update_hash(tmp_buf.data_slice(start: hash_offset, ends: hash_offset + 3) + binder)
1213
+
1214
+ # calculate early data key
1215
+ if hello.early_data
1216
+ early_key = @key_schedule_psk.derive_secret("c e traffic")
1217
+ @update_traffic_key_cb.call(direction: Direction::ENCRYPT, epoch: Epoch::ZERO_RTT, cipher_suite: @key_schedule_psk.cipher_suite, secret: early_key)
1218
+ end
1219
+ end
1220
+
1221
+ @key_schedule_proxy = KeyScheduleProxy.new(@cipher_suites)
1222
+ @key_schedule_proxy.extract(nil)
1223
+
1224
+ TLS.push_message(key_schedule: @key_schedule_proxy, buf: output_buf) do
1225
+ TLS.push_client_hello(buf: output_buf, hello: hello)
1226
+ end
1227
+ set_state(State::CLIENT_EXPECT_SERVER_HELLO)
1228
+ end
1229
+ # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1230
+
1231
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Style/GuardClause
1232
+ def client_handle_hello(input_buf:, output_buf:) # rubocop:disable Lint/UnusedMethodArgument
1233
+ peer_hello = TLS.pull_server_hello(input_buf)
1234
+
1235
+ cipher_suite = TLS.negotiate(supported: @cipher_suites, offered: [peer_hello.cipher_suite], exc: AlertHandshakeFailure)
1236
+
1237
+ raise RuntimeError unless @legacy_compression_methods.include?(peer_hello.compression_method)
1238
+ raise RuntimeError unless @supported_versions.include?(peer_hello.supported_version)
1239
+
1240
+ # select key schedule
1241
+ if peer_hello.pre_shared_key
1242
+ raise AlertIllegalParameter if @key_schedule_psk.nil? || peer_hello.pre_shared_key != 0 || cipher_suite != @key_schedule_psk.cipher_suite
1243
+
1244
+ @key_schedule = @key_schedule_psk
1245
+ @session_resumed = true
1246
+ else
1247
+ @key_schedule = @key_schedule_proxy.select(cipher_suite)
1248
+ end
1249
+
1250
+ @key_schedule_psk = nil
1251
+ @key_schedule_proxy = nil
1252
+
1253
+ # perform key exchange
1254
+ peer_public_key = TLS.decode_public_key(peer_hello.key_share)
1255
+ shared_key = nil
1256
+
1257
+ # X25519 nad X448 is not supported yet
1258
+ if peer_public_key.is_a?(OpenSSL::PKey::EC::Point) && @ec_key && peer_public_key.group.curve_name == @ec_key.group.curve_name
1259
+ shared_key = @ec_key.dh_compute_key(peer_public_key)
1260
+ else
1261
+ raise "Did not support yet"
1262
+ end
1263
+ raise RuntimeError unless shared_key
1264
+
1265
+ @key_schedule.update_hash(input_buf.data)
1266
+ @key_schedule.extract(shared_key)
1267
+
1268
+ setup_traffic_protection(Direction::DECRYPT, Epoch::HANDSHAKE, "s hs traffic")
1269
+
1270
+ set_state(State::CLIENT_EXPECT_ENCRYPTED_EXTENSIONS)
1271
+ end
1272
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Style/GuardClause
1273
+
1274
+ def client_handle_encrypted_extensions(input_buf)
1275
+ encrypted_extensions = TLS.pull_encrypted_extensions(input_buf)
1276
+
1277
+ @alpn_negotiated = encrypted_extensions.alpn_protocol
1278
+ @early_data_accepted = encrypted_extensions.early_data
1279
+ @received_extensions = encrypted_extensions.other_extensions
1280
+ @alpn_cb&.call(@alpn_negotiated)
1281
+
1282
+ setup_traffic_protection(Direction::ENCRYPT, Epoch::HANDSHAKE, "c hs traffic")
1283
+ @key_schedule.update_hash(input_buf.data)
1284
+
1285
+ # if the server accepted our PSK we are done, other we want its certificate
1286
+ if @session_resumed
1287
+ set_state(State::CLIENT_EXPECT_FINISHED)
1288
+ else
1289
+ set_state(State::CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE)
1290
+ end
1291
+ end
1292
+
1293
+ def client_handle_certificate(input_buf)
1294
+ certificate = TLS.pull_certificate(input_buf)
1295
+
1296
+ @peer_certificate = TLS.load_der_x509_certificate(certificate.certificates[0][0])
1297
+ @peer_certificate_chain = certificate.certificates.map.with_index do |cert, i|
1298
+ next if i == 0
1299
+
1300
+ TLS.load_der_x509_certificate(cert[0])
1301
+ end
1302
+
1303
+ @key_schedule.update_hash(input_buf.data)
1304
+ set_state(State::CLIENT_EXPECT_CERTIFICATE_VERIFY)
1305
+ end
1306
+
1307
+ def client_handle_certificate_verify(input_buf)
1308
+ verify = TLS.pull_certificate_verify(input_buf)
1309
+ raise RuntimeError unless @signature_algorithms.include?(verify.algorithm)
1310
+
1311
+ # check signature
1312
+ begin
1313
+ result = verify_with_params(
1314
+ cert: @peer_certificate,
1315
+ signature_algorithm: verify.algorithm,
1316
+ signature: verify.signature,
1317
+ verify_data: @key_schedule.certificate_verify_data("TLS 1.3, server CertificateVerify"),
1318
+ )
1319
+ raise AlertDecryptError unless result
1320
+ rescue OpenSSL::PKey::PKeyError
1321
+ raise AlertDecryptError
1322
+ end
1323
+
1324
+ # check certificate
1325
+ if @verify_mode != OpenSSL::SSL::VERIFY_NONE
1326
+ TLS.verify_certificate(
1327
+ cadata: @cadata,
1328
+ cafile: @cafile,
1329
+ capath: @capath,
1330
+ certificate: @peer_certificate,
1331
+ chain: @peer_certificate_chain,
1332
+ server_name: @server_name,
1333
+ )
1334
+ end
1335
+
1336
+ @key_schedule.update_hash(input_buf.data)
1337
+ set_state(State::CLIENT_EXPECT_FINISHED)
1338
+ end
1339
+
1340
+ def client_handle_finished(input_buf:, output_buf:)
1341
+ finished = TLS.pull_finished(input_buf)
1342
+
1343
+ # check verify data
1344
+ expected_verify_data = @key_schedule.finished_verify_data(@dec_key)
1345
+ raise AlertDecryptError if finished.verify_data != expected_verify_data
1346
+
1347
+ @key_schedule.update_hash(input_buf.data)
1348
+
1349
+ # prepare traffic keys
1350
+ raise RuntimeError unless @key_schedule.generation == 2
1351
+
1352
+ @key_schedule.extract(nil)
1353
+ setup_traffic_protection(Direction::DECRYPT, Epoch::ONE_RTT, "s ap traffic")
1354
+ next_enc_key = @key_schedule.derive_secret("c ap traffic")
1355
+
1356
+ # send finished
1357
+ TLS.push_message(key_schedule: @key_schedule, buf: output_buf) do
1358
+ TLS.push_finished(buf: output_buf, finished: Finished.new.tap { |f| f.verify_data = @key_schedule.finished_verify_data(@enc_key) })
1359
+ end
1360
+
1361
+ # commit traffic key
1362
+ @enc_key = next_enc_key
1363
+ @update_traffic_key_cb.call(direction: Direction::ENCRYPT, epoch: Epoch::ONE_RTT, cipher_suite: @key_schedule.cipher_suite, secret: @enc_key)
1364
+ set_state(State::CLIENT_POST_HANDSHAKE)
1365
+ end
1366
+
1367
+ def client_handle_new_session_ticket(input_buf)
1368
+ new_session_ticket = TLS.pull_new_session_ticket(input_buf)
1369
+
1370
+ # notify application
1371
+ if @new_session_ticket_cb # rubocop:disable Style/GuardClause
1372
+ ticket = build_session_ticket(new_session_ticket: new_session_ticket, other_extensions: @received_extensions)
1373
+ @new_session_ticket_cb.call(ticket)
1374
+ end
1375
+ end
1376
+
1377
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
1378
+ def server_handle_hello(input_buf:, initial_buf:, handshake_buf:, onertt_buf:)
1379
+ peer_hello = TLS.pull_client_hello(input_buf)
1380
+
1381
+ # determine applicable signature algorithms
1382
+ signature_algorithms = if @certificate_private_key.is_a?(OpenSSL::PKey::RSA)
1383
+ [
1384
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA256,
1385
+ SignatureAlgorithm::RSA_PKCS1_SHA256,
1386
+ SignatureAlgorithm::RSA_PKCS1_SHA1,
1387
+ ]
1388
+ elsif @certificate_private_key.is_a?(OpenSSL::PKey::EC) && @certificate_private_key.group.curve_name == "prime256v1"
1389
+ [SignatureAlgorithm::ECDSA_SECP256R1_SHA256]
1390
+ # elsif @certificate_private_key.is_a?(:ed25519) # TODO: https://github.com/ruby/openssl/pull/329
1391
+ # [SignatureAlgorithm::ED25519]
1392
+ # elsif @certificate_private_key.is_a?(:ed448)
1393
+ # [SignatureAlgorithm::ED448]
1394
+ else
1395
+ []
1396
+ end
1397
+ # negotiate parameters
1398
+ cipher_suite = TLS.negotiate(supported: @cipher_suites, offered: peer_hello.cipher_suites, exc: AlertHandshakeFailure)
1399
+ compression_method =
1400
+ TLS.negotiate(supported: @legacy_compression_methods, offered: peer_hello.legacy_compression_methods, exc: AlertHandshakeFailure)
1401
+ psk_key_exchange_mode = TLS.negotiate(supported: @psk_key_exchange_modes, offered: peer_hello.psk_key_exchange_modes)
1402
+ signature_algorithm = TLS.negotiate(supported: signature_algorithms, offered: peer_hello.signature_algorithms, exc: AlertHandshakeFailure)
1403
+ supported_version = TLS.negotiate(supported: @supported_versions, offered: peer_hello.supported_versions, exc: AlertProtocolVersion)
1404
+
1405
+ # binding.irb
1406
+ # negotiate alpn
1407
+ if @alpn_protocols && !@alpn_protocols.empty?
1408
+ # binding.irb
1409
+ @alpn_negotiated = TLS.negotiate(supported: @alpn_protocols, offered: peer_hello.alpn_protocols, exc: AlertHandshakeFailure)
1410
+ end
1411
+ @alpn_cb&.call(@alpn_negotiated)
1412
+
1413
+ @client_random = peer_hello.random
1414
+ @server_random = Random.urandom(32)
1415
+ @legacy_session_id = peer_hello.legacy_session_id
1416
+ @received_extensions = peer_hello.other_extensions
1417
+
1418
+ # select key schedule
1419
+ pre_shared_key = nil
1420
+ if @get_session_ticket_cb &&
1421
+ psk_key_exchange_mode &&
1422
+ peer_hello.pre_shared_key &&
1423
+ peer_hello.pre_shared_key.identities.size == 1 &&
1424
+ peer_hello.pre_shared_key.binders.size == 1
1425
+ # ask application to find session ticket
1426
+ identity = peer_hello.pre_shared_key.identities[0]
1427
+ session_ticket = @get_session_ticket_cb.call(identity[0])
1428
+
1429
+ # validate session ticket
1430
+ if session_ticket&.is_valid && session_ticket&.cipher_suite == cipher_suite
1431
+ @key_schedule = KeySchedule.new(cipher_suite)
1432
+ @key_schedule.extract(session_ticket.resumption_secret)
1433
+
1434
+ binder_key = @key_schedule.derive_secret("res binder")
1435
+ binder_length = @key_schedule.hash.digest_length
1436
+
1437
+ hash_offset = input_buf.tell - binder_length - 3
1438
+ binder = input_buf.data_slice(start: hash_offset + 3, ends: hash_offset + 3 + binder_length)
1439
+
1440
+ @key_schedule.update_hash(input_buf.data_slice(start: 0, ends: hash_offset))
1441
+ expected_binder = @key_schedule.finished_verify_data(binder_key)
1442
+
1443
+ raise AlertHandshakeFailure if binder != expected_binder
1444
+
1445
+ @key_schedule.update_hash(input_buf.data_slice(start: hash_offset, ends: hash_offset + 3 + binder_length))
1446
+ @session_resumed = true
1447
+
1448
+ # calculate early data key
1449
+ if peer_hello.early_data
1450
+ early_key = @key_schedule.derive_secret("c e traffic")
1451
+ @early_data_accepted = true
1452
+ @update_traffic_key_cb.call(direction: Direction::DECRYPT, epoch: Epoch::ZERO_RTT, cipher_suite: @key_schedule.cipher_suite, secret: early_key)
1453
+ end
1454
+ pre_shared_key = 0
1455
+ end
1456
+ end
1457
+
1458
+ # if PSK is not used, initialize key schedule
1459
+ if pre_shared_key.nil?
1460
+ @key_schedule = KeySchedule.new(cipher_suite)
1461
+ @key_schedule.extract(nil)
1462
+ @key_schedule.update_hash(input_buf.data)
1463
+ end
1464
+
1465
+ # perform key exchange
1466
+ public_key = nil
1467
+ shared_key = nil
1468
+ peer_hello.key_share.each do |key_share|
1469
+ peer_public_key = TLS.decode_public_key(key_share)
1470
+ case peer_public_key
1471
+ when OpenSSL::PKey::EC::Point
1472
+ @ec_key = OpenSSL::PKey::EC.generate(GROUP_TO_CURVE[key_share[0]])
1473
+ @ec_private_key = @ec_key.private_key
1474
+ public_key = @ec_key.public_key
1475
+ shared_key = @ec_key.dh_compute_key(peer_public_key)
1476
+ end
1477
+ end
1478
+ raise RuntimeError unless shared_key
1479
+
1480
+ # send hello
1481
+ hello = ServerHello.new.tap do |h|
1482
+ h.random = @server_random
1483
+ h.legacy_session_id = @legacy_session_id
1484
+ h.cipher_suite = cipher_suite
1485
+ h.compression_method = compression_method
1486
+ h.key_share = TLS.encode_public_key(public_key)
1487
+ h.pre_shared_key = pre_shared_key
1488
+ h.supported_version = supported_version
1489
+ h.other_extensions = []
1490
+ end
1491
+ TLS.push_message(key_schedule: @key_schedule, buf: initial_buf) do
1492
+ TLS.push_server_hello(buf: initial_buf, hello: hello)
1493
+ end
1494
+ @key_schedule.extract(shared_key)
1495
+
1496
+ setup_traffic_protection(Direction::ENCRYPT, Epoch::HANDSHAKE, "s hs traffic")
1497
+ setup_traffic_protection(Direction::DECRYPT, Epoch::HANDSHAKE, "c hs traffic")
1498
+
1499
+ # send encrypted extensions
1500
+ TLS.push_message(key_schedule: @key_schedule, buf: handshake_buf) do
1501
+ ext = EncryptedExtensions.new.tap do |e|
1502
+ e.alpn_protocol = @alpn_negotiated
1503
+ e.early_data = @early_data_accepted
1504
+ e.other_extensions = @handshake_extensions
1505
+ end
1506
+ TLS.push_encrypted_extensions(buf: handshake_buf, extensions: ext)
1507
+ end
1508
+
1509
+ unless pre_shared_key
1510
+ # send certificate
1511
+ TLS.push_message(key_schedule: @key_schedule, buf: handshake_buf) do
1512
+ cert = Certificate.new.tap do |c|
1513
+ c.request_context = ""
1514
+ c.certificates = ([@certificate] + (@certificate_chain || [])).map { |x| [x.to_der, ""] }
1515
+ end
1516
+ TLS.push_certificate(buf: handshake_buf, certificate: cert)
1517
+ end
1518
+
1519
+ # send certificate verify
1520
+ signature = sign_with_params(
1521
+ priv_key: @certificate_private_key,
1522
+ signature_algorithm: signature_algorithm,
1523
+ verify_data: @key_schedule.certificate_verify_data("TLS 1.3, server CertificateVerify"),
1524
+ )
1525
+
1526
+ TLS.push_message(key_schedule: @key_schedule, buf: handshake_buf) do
1527
+ verify = CertificateVerify.new.tap do |cv|
1528
+ cv.algorithm = signature_algorithm
1529
+ cv.signature = signature
1530
+ end
1531
+ TLS.push_certificate_verify(buf: handshake_buf, verify: verify)
1532
+ end
1533
+ end
1534
+
1535
+ # send finished
1536
+ TLS.push_message(key_schedule: @key_schedule, buf: handshake_buf) do
1537
+ finished = Finished.new.tap do |f|
1538
+ f.verify_data = @key_schedule.finished_verify_data(@enc_key)
1539
+ end
1540
+ TLS.push_finished(buf: handshake_buf, finished: finished)
1541
+ end
1542
+
1543
+ # prepare traffic keys
1544
+ raise RuntimeError unless @key_schedule.generation == 2
1545
+
1546
+ @key_schedule.extract(nil)
1547
+ setup_traffic_protection(Direction::ENCRYPT, Epoch::ONE_RTT, "s ap traffic")
1548
+ @next_dec_key = @key_schedule.derive_secret("c ap traffic")
1549
+
1550
+ # anticipate client's FINISHED as we don't use client auth
1551
+ @expected_verify_data = @key_schedule.finished_verify_data(@dec_key)
1552
+ buf = Buffer.new(capacity: 64)
1553
+ TLS.push_finished(buf: buf, finished: Finished.new.tap { |f| f.verify_data = @expected_verify_data })
1554
+ @key_schedule.update_hash(buf.data)
1555
+
1556
+ # create a new session ticket
1557
+ if @new_session_ticket_cb && psk_key_exchange_mode
1558
+ @new_session_ticket = NewSessionTicket.new.tap do |st|
1559
+ st.ticket_lifetime = 86400
1560
+ st.ticket_age_add = Random.urandom(4).unpack1("I")
1561
+ st.ticket_nonce = ""
1562
+ st.ticket = Random.urandom(64)
1563
+ st.max_early_data_size = @max_early_data
1564
+ st.other_extensions = []
1565
+ end
1566
+
1567
+ # send message
1568
+ TLS.push_new_session_ticket(buf: onertt_buf, new_session_ticket: @new_session_ticket)
1569
+
1570
+ # notify application
1571
+ ticket = build_session_ticket(new_session_ticket: @new_session_ticket, other_extensions: @handshake_extensions)
1572
+ @new_session_ticket_cb.call(ticket)
1573
+ end
1574
+
1575
+ set_state(State::SERVER_EXPECT_FINISHED)
1576
+ end
1577
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
1578
+
1579
+ def server_handle_finished(input_buf:, output_buf:) # rubocop:disable Lint/UnusedMethodArgument
1580
+ finished = TLS.pull_finished(input_buf)
1581
+
1582
+ # ckeck verify data
1583
+ raise AlertDecryptError if finished.verify_data != @expected_verify_data
1584
+
1585
+ # commit traffic key
1586
+ @dec_key = @next_dec_key
1587
+ @next_dec_key = nil
1588
+ @update_traffic_key_cb.call(direction: Direction::DECRYPT, epoch: Epoch::ONE_RTT, cipher_suite: @key_schedule.cipher_suite, secret: @dec_key)
1589
+ set_state(State::SERVER_POST_HANDSHAKE)
1590
+ end
1591
+
1592
+ def setup_traffic_protection(direction, epoch, label)
1593
+ key = @key_schedule.derive_secret(label)
1594
+
1595
+ if direction == Direction::ENCRYPT
1596
+ @enc_key = key
1597
+ else
1598
+ @dec_key = key
1599
+ end
1600
+ @update_traffic_key_cb.call(direction: direction, epoch: epoch, cipher_suite: @key_schedule.cipher_suite, secret: key)
1601
+ end
1602
+
1603
+ def set_state(state)
1604
+ # TODO: logger
1605
+ @state = state
1606
+ end
1607
+
1608
+ def sign_with_params(priv_key:, signature_algorithm:, verify_data:)
1609
+ case signature_algorithm
1610
+ when SignatureAlgorithm::RSA_PKCS1_SHA256,
1611
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA256,
1612
+ SignatureAlgorithm::RSA_PSS_PSS_SHA256
1613
+ priv_key.sign_pss("SHA256", verify_data, salt_length: :digest, mgf1_hash: "SHA256")
1614
+ when SignatureAlgorithm::RSA_PKCS1_SHA384,
1615
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA384,
1616
+ SignatureAlgorithm::RSA_PSS_PSS_SHA384
1617
+ priv_key.sign_pss("SHA384", verify_data, salt_length: :digest, mgf1_hash: "SHA384")
1618
+ when SignatureAlgorithm::RSA_PKCS1_SHA512,
1619
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA512,
1620
+ SignatureAlgorithm::RSA_PSS_PSS_SHA512
1621
+ priv_key.sign_pss("SHA512", verify_data, salt_length: :digest, mgf1_hash: "SHA512")
1622
+ when SignatureAlgorithm::ECDSA_SECP256R1_SHA256
1623
+ priv_key.sign("SHA256", verify_data)
1624
+ when SignatureAlgorithm::ECDSA_SECP384R1_SHA384
1625
+ priv_key.sign("SHA384", verify_data)
1626
+ when SignatureAlgorithm::ECDSA_SECP521R1_SHA512
1627
+ priv_key.sign("SHA512", verify_data)
1628
+ else
1629
+ raise RuntimeError
1630
+ end
1631
+ end
1632
+
1633
+ def verify_with_params(cert:, signature_algorithm:, signature:, verify_data:)
1634
+ case signature_algorithm
1635
+ when SignatureAlgorithm::RSA_PKCS1_SHA256,
1636
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA256,
1637
+ SignatureAlgorithm::RSA_PSS_PSS_SHA256
1638
+ cert.public_key.verify_pss("SHA256", signature, verify_data, salt_length: :auto, mgf1_hash: "SHA256")
1639
+ when SignatureAlgorithm::RSA_PKCS1_SHA384,
1640
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA384,
1641
+ SignatureAlgorithm::RSA_PSS_PSS_SHA384
1642
+ cert.public_key.verify_pss("SHA384", signature, verify_data, salt_length: :auto, mgf1_hash: "SHA384")
1643
+ when SignatureAlgorithm::RSA_PKCS1_SHA512,
1644
+ SignatureAlgorithm::RSA_PSS_RSAE_SHA512,
1645
+ SignatureAlgorithm::RSA_PSS_PSS_SHA512
1646
+ cert.public_key.verify_pss("SHA512", signature, verify_data, salt_length: :auto, mgf1_hash: "SHA512")
1647
+ when SignatureAlgorithm::ECDSA_SECP256R1_SHA256
1648
+ cert.public_key.verify("SHA256", signature, verify_data)
1649
+ when SignatureAlgorithm::ECDSA_SECP384R1_SHA384
1650
+ cert.public_key.verify("SHA384", signature, verify_data)
1651
+ when SignatureAlgorithm::ECDSA_SECP521R1_SHA512
1652
+ cert.public_key.verify("SHA512", signature, verify_data)
1653
+ else
1654
+ raise RuntimeError
1655
+ end
1656
+ end
1657
+ end
1658
+ end
1659
+ end