raioquic 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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