Browse Source

Fix indent

master
aeris 2 months ago
parent
commit
31d1863914
67 changed files with 2780 additions and 2706 deletions
  1. +17
    -16
      CODE_OF_CONDUCT.md
  2. +0
    -8
      Gemfile
  3. +31
    -14
      README.md
  4. +6
    -6
      bin/rake
  5. +6
    -6
      bin/rspec
  6. +9
    -9
      lib/cryptcheck/engine.rb
  7. +71
    -71
      lib/cryptcheck/engine/buffer.rb
  8. +32
    -23
      lib/cryptcheck/engine/builder.rb
  9. +56
    -56
      lib/cryptcheck/engine/countable_buffer.rb
  10. +16
    -16
      lib/cryptcheck/engine/double_hash.rb
  11. +2
    -2
      lib/cryptcheck/engine/error.rb
  12. +42
    -44
      lib/cryptcheck/engine/fixtures/basic_socket.rb
  13. +25
    -25
      lib/cryptcheck/engine/fixtures/socket.rb
  14. +32
    -32
      lib/cryptcheck/engine/fixtures/string.rb
  15. +2
    -3
      lib/cryptcheck/engine/fixtures/string_io.rb
  16. +9
    -9
      lib/cryptcheck/engine/fixtures/symbol.rb
  17. +5
    -5
      lib/cryptcheck/engine/id_classes.rb
  18. +669
    -609
      lib/cryptcheck/engine/tls.rb
  19. +61
    -61
      lib/cryptcheck/engine/tls/alert.rb
  20. +14
    -14
      lib/cryptcheck/engine/tls/change_cipher_spec.rb
  21. +53
    -53
      lib/cryptcheck/engine/tls/handshake.rb
  22. +24
    -24
      lib/cryptcheck/engine/tls/handshake/certificate.rb
  23. +53
    -53
      lib/cryptcheck/engine/tls/handshake/client_hello.rb
  24. +58
    -58
      lib/cryptcheck/engine/tls/handshake/client_key_exchange.rb
  25. +109
    -109
      lib/cryptcheck/engine/tls/handshake/extension.rb
  26. +23
    -23
      lib/cryptcheck/engine/tls/handshake/extension/renegotiation_info.rb
  27. +40
    -40
      lib/cryptcheck/engine/tls/handshake/extension/server_name.rb
  28. +23
    -23
      lib/cryptcheck/engine/tls/handshake/extension/signature_algorithms.rb
  29. +23
    -23
      lib/cryptcheck/engine/tls/handshake/extension/supported_groups.rb
  30. +34
    -34
      lib/cryptcheck/engine/tls/handshake/extension/supported_versions.rb
  31. +11
    -12
      lib/cryptcheck/engine/tls/handshake/hello_request.rb
  32. +52
    -52
      lib/cryptcheck/engine/tls/handshake/server_hello.rb
  33. +11
    -12
      lib/cryptcheck/engine/tls/handshake/server_hello_done.rb
  34. +54
    -54
      lib/cryptcheck/engine/tls/handshake/server_key_exchange.rb
  35. +46
    -46
      lib/cryptcheck/engine/tls/record_header.rb
  36. +37
    -37
      lib/cryptcheck/engine/tls/signature.rb
  37. +3
    -3
      lib/cryptcheck/engine/version.rb
  38. +3
    -3
      spec/cryptcheck/engine/engine_spec.rb
  39. +28
    -28
      spec/cryptcheck/engine/fixtures/socket_spec.rb
  40. +48
    -48
      spec/cryptcheck/engine/fixtures/stringio_spec.rb
  41. +12
    -12
      spec/cryptcheck/engine/mock_io.rb
  42. +30
    -30
      spec/cryptcheck/engine/tls/alert_spec.rb
  43. +24
    -24
      spec/cryptcheck/engine/tls/change_cipher_spec_spec.rb
  44. +58
    -58
      spec/cryptcheck/engine/tls/handshake/certificate_spec.rb
  45. +25
    -25
      spec/cryptcheck/engine/tls/handshake/client_diffie_hellman_public_spec.rb
  46. +32
    -32
      spec/cryptcheck/engine/tls/handshake/client_elliptic_curve_diffie_hellman_public_spec.rb
  47. +68
    -68
      spec/cryptcheck/engine/tls/handshake/client_hello_spec.rb
  48. +103
    -103
      spec/cryptcheck/engine/tls/handshake/dh_server_key_exchange_spec.rb
  49. +81
    -81
      spec/cryptcheck/engine/tls/handshake/ecdh_server_key_exchange_spec.rb
  50. +36
    -36
      spec/cryptcheck/engine/tls/handshake/encrypted_pre_master_key_secret_spec.rb
  51. +26
    -26
      spec/cryptcheck/engine/tls/handshake/extension/renegotiation_info_spec.rb
  52. +27
    -27
      spec/cryptcheck/engine/tls/handshake/extension/server_name_spec.rb
  53. +27
    -27
      spec/cryptcheck/engine/tls/handshake/extension/signature_algorithms_spec.rb
  54. +27
    -27
      spec/cryptcheck/engine/tls/handshake/extension/supported_groups_spec.rb
  55. +27
    -27
      spec/cryptcheck/engine/tls/handshake/extension/supported_versions_spec.rb
  56. +40
    -40
      spec/cryptcheck/engine/tls/handshake/extension_spec.rb
  57. +22
    -22
      spec/cryptcheck/engine/tls/handshake/hello_record_spec.rb
  58. +23
    -23
      spec/cryptcheck/engine/tls/handshake/server_hello_done_spec.rb
  59. +60
    -60
      spec/cryptcheck/engine/tls/handshake/server_hello_spec.rb
  60. +26
    -26
      spec/cryptcheck/engine/tls/handshake_spec.rb
  61. +30
    -30
      spec/cryptcheck/engine/tls/record_header_spec.rb
  62. +33
    -33
      spec/cryptcheck/engine/tls_spec.rb
  63. +10
    -10
      spec/spec_helper.rb
  64. +3
    -3
      spec/support/klass.rb
  65. +37
    -37
      spec/support/matchers/buffer.rb
  66. +8
    -8
      spec/support/matchers/eq_hex.rb
  67. +47
    -47
      spec/support/network.rb

+ 17
- 16
CODE_OF_CONDUCT.md View File

@@ -5,9 +5,9 @@
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
size, disability, ethnicity, gender identity and expression, level of
experience, nationality, personal appearance, race, religion, or sexual identity
and orientation.

## Our Standards

@@ -23,7 +23,7 @@ include:
Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
@@ -37,11 +37,11 @@ Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, or to ban temporarily or permanently any
contributor for other behaviors that they deem inappropriate, threatening,
offensive, or harmful.

## Scope

@@ -55,11 +55,11 @@ further defined and clarified by project maintainers.
## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at aeris@imirhil.fr. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
reported by contacting the project team at aeris@imirhil.fr. All complaints will
be reviewed and investigated and will result in a response that is deemed
necessary and appropriate to the circumstances. The project team is obligated to
maintain confidentiality with regard to the reporter of an incident. Further
details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
@@ -67,8 +67,9 @@ members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org

[version]: http://contributor-covenant.org/version/1/4/

+ 0
- 8
Gemfile View File

@@ -2,16 +2,8 @@ source 'https://rubygems.org'
gemspec

group :test do
<<<<<<< HEAD
gem 'rspec', '~> 3.0'
gem 'simplecov', require: false
# gem 'fuubar', require: false
gem 'pry-byebug'
gem 'colorize'
=======
gem 'rspec', '~> 3.10.0'
gem 'simplecov', '~> 0.21.2', require: false
gem 'pry-byebug', '~> 3.9.0'
gem 'colorize', '~> 0.8.1'
>>>>>>> 7650590 (fixup! Upgrade dependencies)
end

+ 31
- 14
README.md View File

@@ -1,19 +1,24 @@
# Cryptcheck::Engine

CryptCheck needs support of old, unsecured and so deprecated protocols (SSLv2, SSLv3…) or cipher suite (RC4, 3DES…) to be able to fully cover SSL/TLS verifications.
CryptCheck needs support of old, unsecured and so deprecated protocols (SSLv2,
SSLv3…) or cipher suite (RC4, 3DES…) to be able to fully cover SSL/TLS
verifications.

OpenSSL 1.0 is necessary to support such deprecated things, but new TLS features like TLSv1.3 requires 1.2.
And currently, Ruby is tied to a single OpenSSL binding, only old Ruby version (2.3) supports OpenSSL 1.0 and at the opposite, only 2.5+ supports 1.2.
OpenSSL 1.0 is necessary to support such deprecated things, but new TLS features
like TLSv1.3 requires 1.2. And currently, Ruby is tied to a single OpenSSL
binding, only old Ruby version (2.3) supports OpenSSL 1.0 and at the opposite,
only 2.5+ supports 1.2.

It will be a real mess to use multiple binding to fully check a server

- multiple OpenSSL bindings
- multiple Ruby versions
- RPC or equivalent
- not totally ordered set for server preferences
- …
This project is a SSL/TLS pure Ruby implementation to remove CryptCheck OpenSSL dependency and so to support together old and new SSL/TLS features.
- multiple OpenSSL bindings
- multiple Ruby versions
- RPC or equivalent
- not totally ordered set for server preferences
- …

This project is a SSL/TLS pure Ruby implementation to remove CryptCheck OpenSSL
dependency and so to support together old and new SSL/TLS features.

__**/!\ DON'T USE IT IN PRODUCTION /!\**__
This is not a cryptographic safe implementation!
@@ -40,14 +45,26 @@ TODO: Write usage instructions here

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
After checking out the repo, run `bin/setup` to install dependencies. Then,
run `rake spec` to run the tests. You can also run `bin/console` for an
interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
To install this gem onto your local machine, run `bundle exec rake install`. To
release a new version, update the version number in `version.rb`, and then
run `bundle exec rake release`, which will create a git tag for the version,
push git commits and tags, and push the `.gem` file
to [rubygems.org](https://rubygems.org).

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/aeris/cryptcheck-tls. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
Bug reports and pull requests are welcome on GitHub
at https://github.com/aeris/cryptcheck-tls. This project is intended to be a
safe, welcoming space for collaboration, and contributors are expected to adhere
to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.

## Code of Conduct

Everyone interacting in the Cryptcheck::Engine project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://git.imirhil.fr/aeris/cryptcheck-engine/src/branch/master/CODE_OF_CONDUCT.md).
Everyone interacting in the Cryptcheck::Engine project’s codebases, issue
trackers, chat rooms and mailing lists is expected to follow
the [code of conduct](https://git.imirhil.fr/aeris/cryptcheck-engine/src/branch/master/CODE_OF_CONDUCT.md)
.

+ 6
- 6
bin/rake View File

@@ -10,17 +10,17 @@

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
Pathname.new(__FILE__).realpath)

bundle_binstub = File.expand_path("../bundle", __FILE__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
end

require "rubygems"


+ 6
- 6
bin/rspec View File

@@ -10,17 +10,17 @@

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
Pathname.new(__FILE__).realpath)

bundle_binstub = File.expand_path("../bundle", __FILE__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
end

require "rubygems"


+ 9
- 9
lib/cryptcheck/engine.rb View File

@@ -1,15 +1,15 @@
require 'cryptcheck/engine/version'

module Cryptcheck::Engine
autoload :Buffer, 'cryptcheck/engine/buffer'
autoload :CountableBuffer, 'cryptcheck/engine/countable_buffer'
autoload :Buffer, 'cryptcheck/engine/buffer'
autoload :CountableBuffer, 'cryptcheck/engine/countable_buffer'

fixtures = File.join __dir__, 'engine/fixtures/*.rb'
Dir[fixtures].each { |f| load f }
fixtures = File.join __dir__, 'engine/fixtures/*.rb'
Dir[fixtures].each { |f| load f }

autoload :Builder, 'cryptcheck/engine/builder'
autoload :Error, 'cryptcheck/engine/error'
autoload :DoubleHash, 'cryptcheck/engine/double_hash'
autoload :IdClasses, 'cryptcheck/engine/id_classes'
autoload :Tls, 'cryptcheck/engine/tls'
autoload :Builder, 'cryptcheck/engine/builder'
autoload :Error, 'cryptcheck/engine/error'
autoload :DoubleHash, 'cryptcheck/engine/double_hash'
autoload :IdClasses, 'cryptcheck/engine/id_classes'
autoload :Tls, 'cryptcheck/engine/tls'
end

+ 71
- 71
lib/cryptcheck/engine/buffer.rb View File

@@ -1,18 +1,18 @@
module Cryptcheck::Engine
module Buffer
{
uint8: [1, 'C'],
uint16: [2, 'S>'],
uint32: [4, 'L>'],
uint64: [8, 'Q>'],
module Buffer
{
uint8: [1, 'C'],
uint16: [2, 'S>'],
uint32: [4, 'L>'],
uint64: [8, 'Q>'],

int8: [1, 'c'],
int16: [2, 's>'],
int32: [4, 'l>'],
int64: [8, 'q>'],
}.each do |name, config|
size, type = config
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
int8: [1, 'c'],
int16: [2, 's>'],
int32: [4, 'l>'],
int64: [8, 'q>'],
}.each do |name, config|
size, type = config
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def read_#{name}
data = self.read #{size}
data.unpack('#{type}').first
@@ -22,70 +22,70 @@ module Cryptcheck::Engine
data = [value].pack '#{type}'
self.write data
end
RUBY_EVAL
end
RUBY_EVAL
end

def read_uint(size)
value = 0
size.times do
value *= 0x0100
value += self.read_uint8
end
value
end
def read_uint(size)
value = 0
size.times do
value *= 0x0100
value += self.read_uint8
end
value
end

def write_uint(size, value)
value = size.times.collect { t = value % 0x0100; value /= 0x0100; t }.reverse
value.each { |s| self.write_uint8 s }
size
end
def write_uint(size, value)
value = size.times.collect { t = value % 0x0100; value /= 0x0100; t }.reverse
value.each { |s| self.write_uint8 s }
size
end

def read_data(type)
length =
case type
when Symbol
self.send "read_#{type}"
else
self.read_uint type
end
data = if length == 0
nil
else
data = self.read length
size = data.size
raise EOFError, "Unable to read #{length} bytes, #{size} bytes read" unless size == length
data.b
end
data
end
def read_data(type)
length =
case type
when Symbol
self.send "read_#{type}"
else
self.read_uint type
end
data = if length == 0
nil
else
data = self.read length
size = data.size
raise EOFError, "Unable to read #{length} bytes, #{size} bytes read" unless size == length
data.b
end
data
end

def write_data(type, data)
data ||= ''
data = data.b
size = data.size
case type
when Symbol
self.send "write_#{type}", size
else
self.write_uint type, size
end
self.write data
end
def write_data(type, data)
data ||= ''
data = data.b
size = data.size
case type
when Symbol
self.send "write_#{type}", size
else
self.write_uint type, size
end
self.write data
end

def collect(length, &block)
CountableBuffer.collect self, length, &block
end
def collect(length, &block)
CountableBuffer.collect self, length, &block
end

def reads(&block)
CountableBuffer.reads self, &block
end
def reads(&block)
CountableBuffer.reads self, &block
end

def writes(&block)
CountableBuffer.writes self, &block
end
def writes(&block)
CountableBuffer.writes self, &block
end

def reads_writes(&block)
CountableBuffer.reads_writes self, &block
end
end
def reads_writes(&block)
CountableBuffer.reads_writes self, &block
end
end
end

+ 32
- 23
lib/cryptcheck/engine/builder.rb View File

@@ -1,27 +1,32 @@
module Cryptcheck::Engine
module Builder
def self.included(klass)
klass.extend ClassMethod
end
module Builder
def self.included(klass)
klass.extend ClassMethod
end

private
def resolve
binding.pry
@@build.call
end

module ClassMethod
def attribute(name)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
private

module ClassMethod
def attribute(name)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}(value)
@#{name} = value
self
end
RUBY_EVAL
end
RUBY_EVAL
end

def attributes(*names)
names.each { |n| self.attribute n }
end
def attributes(*names)
names.each { |n| self.attribute n }
end

def list(name)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def list(name)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}s(values)
@#{name}s += values
self
@@ -31,12 +36,16 @@ module Cryptcheck::Engine
@#{name}s << value
self
end
RUBY_EVAL
end

def lists(*names)
names.each { |n| self.list n }
end
end
end
RUBY_EVAL
end

def lists(*names)
names.each { |n| self.list n }
end

def build(&block)
@@build = block
end
end
end
end

+ 56
- 56
lib/cryptcheck/engine/countable_buffer.rb View File

@@ -1,58 +1,58 @@
module Cryptcheck::Engine
class CountableBuffer
include Buffer
def initialize(io)
@io = io
@read = @written = 0
end
def count
[@read, @written]
end
def read(length)
result = @io.read length
@read += result.size
result
end
def write(value)
written = @io.write value
@written += written
written
end
def collect(length)
length = self.send "read_#{length}" if length.is_a? Symbol
results, pos = [], @read
loop do
results << yield
break unless @read - pos < length
end
results
end
def self.collect(io, length, &block)
buffer = self.new io
buffer.collect length, &block
end
def self.reads_writes(io, &block)
buffer = self.new io
result = block.call buffer
read, written = buffer.count
[read, written, result]
end
def self.reads(io, &block)
read, _, result = self.reads_writes io, &block
[read, result]
end
def self.writes(io, &block)
_, written, _ = self.reads_writes io, &block
written
end
end
class CountableBuffer
include Buffer
def initialize(io)
@io = io
@read = @written = 0
end
def count
[@read, @written]
end
def read(length)
result = @io.read length
@read += result.size
result
end
def write(value)
written = @io.write value
@written += written
written
end
def collect(length)
length = self.send "read_#{length}" if length.is_a? Symbol
results, pos = [], @read
loop do
results << yield
break unless @read - pos < length
end
results
end
def self.collect(io, length, &block)
buffer = self.new io
buffer.collect length, &block
end
def self.reads_writes(io, &block)
buffer = self.new io
result = block.call buffer
read, written = buffer.count
[read, written, result]
end
def self.reads(io, &block)
read, _, result = self.reads_writes io, &block
[read, result]
end
def self.writes(io, &block)
_, written, _ = self.reads_writes io, &block
written
end
end
end

+ 16
- 16
lib/cryptcheck/engine/double_hash.rb View File

@@ -1,22 +1,22 @@
module Cryptcheck::Engine
class DoubleHash
include Enumerable
class DoubleHash
include Enumerable

def initialize(hash = {})
@hash = hash.freeze
@inverse = hash.invert.freeze
end
def initialize(hash = {})
@hash = hash.freeze
@inverse = hash.invert.freeze
end

def [](key)
@hash[key]
end
def [](key)
@hash[key]
end

def each(&block)
@hash.each &block
end
def each(&block)
@hash.each &block
end

def inverse(key)
@inverse[key]
end
end
def inverse(key)
@inverse[key]
end
end
end

+ 2
- 2
lib/cryptcheck/engine/error.rb View File

@@ -1,4 +1,4 @@
module Cryptcheck::Engine
class Error < StandardError
end
class Error < StandardError
end
end

+ 42
- 44
lib/cryptcheck/engine/fixtures/basic_socket.rb View File

@@ -1,50 +1,48 @@
require 'socket'

class BasicSocket < IO
def recvmsg(*args, **kwargs)
timeout = kwargs.delete :timeout
begin
self.recvmsg_nonblock *args, **kwargs
rescue IO::WaitReadable
IO.select [self], nil, nil, timeout
retry
end
end
def recvmsg(*args, **kwargs)
timeout = kwargs.delete :timeout
begin
self.recvmsg_nonblock *args, **kwargs
rescue IO::WaitReadable
IO.select [self], nil, nil, timeout
retry
end
end

def sendmsg(*args, **kwargs)
timeout = kwargs.delete :timeout
begin
self.sendmsg_nonblock *args, **kwargs
rescue Errno::EINPROGRESS
IO.select nil, [self], nil, timeout
retry
end
end
def sendmsg(*args, **kwargs)
timeout = kwargs.delete :timeout
begin
self.sendmsg_nonblock *args, **kwargs
rescue Errno::EINPROGRESS
IO.select nil, [self], nil, timeout
retry
end
end

def collect(length)
results, read = [], 0
loop do
size, result = yield
results << result
read += size
break unless read < length
end
results
end
def collect(length)
results, read = [], 0
loop do
size, result = yield
results << result
read += size
break unless read < length
end
results
end

{
uint8: [1, 'C'],
uint16: [2, 'S>'],
uint32: [4, 'L>'],
uint64: [8, 'Q>'],
{
uint8: [1, 'C'],
uint16: [2, 'S>'],
uint32: [4, 'L>'],
uint64: [8, 'Q>'],

int8: [1, 'c'],
int16: [2, 's>'],
int32: [4, 'l>'],
int64: [8, 'q>'],
}.each do |name, config|
size, type = config
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
int8: [1, 'c'],
int16: [2, 's>'],
int32: [4, 'l>'],
int64: [8, 'q>'],
}.each do |name, config|
size, type = config
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def read_#{name}
data = self.recvmsg #{size}
data.unpack('#{type}').first
@@ -54,6 +52,6 @@ class BasicSocket < IO
data = [value].pack '#{type}'
self.sendmsg data
end
RUBY_EVAL
end
RUBY_EVAL
end
end

+ 25
- 25
lib/cryptcheck/engine/fixtures/socket.rb View File

@@ -1,29 +1,29 @@
require 'socket'

class Socket < BasicSocket
def connect(addr, **kwargs)
timeout = kwargs.delete :timeout
retried = false
begin
self.connect_nonblock addr, **kwargs
rescue IO::WaitReadable
IO.select [self], nil, nil, timeout
retried = true
retry
rescue IO::WaitWritable
IO.select nil, [self], nil, timeout
retried = true
retry
rescue Errno::EALREADY
# First call to `#connect_nonblock` can also raise this
# But if it's not after a retry, this is a real error,
# not a timeout related one
raise unless retried
raise Errno::ETIMEDOUT
rescue Errno::EISCONN
# First call to `#connect_nonblock` can also raise this
# Will be raised by `#connect_nonblock` after a retry in case of success
raise unless retried
end
end
def connect(addr, **kwargs)
timeout = kwargs.delete :timeout
retried = false
begin
self.connect_nonblock addr, **kwargs
rescue IO::WaitReadable
IO.select [self], nil, nil, timeout
retried = true
retry
rescue IO::WaitWritable
IO.select nil, [self], nil, timeout
retried = true
retry
rescue Errno::EALREADY
# First call to `#connect_nonblock` can also raise this
# But if it's not after a retry, this is a real error,
# not a timeout related one
raise unless retried
raise Errno::ETIMEDOUT
rescue Errno::EISCONN
# First call to `#connect_nonblock` can also raise this
# Will be raised by `#connect_nonblock` after a retry in case of success
raise unless retried
end
end
end

+ 32
- 32
lib/cryptcheck/engine/fixtures/string.rb View File

@@ -1,41 +1,41 @@
class String
def from_hex
self.b.gsub(/\s/, '').scan(/../).collect { |h| h.hex.chr }.join.b
end
def from_hex
self.b.gsub(/\s/, '').scan(/../).collect { |h| h.hex.chr }.join.b
end

def to_hex
self.b.each_byte.map { |b| "%02x" % b.ord }.join.upcase
end
def to_hex
self.b.each_byte.map { |b| "%02x" % b.ord }.join.upcase
end

# From https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize
def constantize
names = self.split '::'
# From https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize
def constantize
names = self.split '::'

# Trigger a built-in NameError exception including the ill-formed constant in the message.
Object.const_get camel_cased_word if names.empty?
# Trigger a built-in NameError exception including the ill-formed constant in the message.
Object.const_get camel_cased_word if names.empty?

# Remove the first blank element in case of '::ClassName' notation.
names.shift if names.size > 1 && names.first.empty?
# Remove the first blank element in case of '::ClassName' notation.
names.shift if names.size > 1 && names.first.empty?

names.inject Object do |constant, name|
if constant == Object
constant.const_get name
else
candidate = constant.const_get name
next candidate if constant.const_defined? name, false
next candidate unless Object.const_defined? name
names.inject Object do |constant, name|
if constant == Object
constant.const_get name
else
candidate = constant.const_get name
next candidate if constant.const_defined? name, false
next candidate unless Object.const_defined? name

# Go down the ancestors to check if it is owned directly. The check
# stops when we reach Object or the end of ancestors tree.
constant = constant.ancestors.inject constant do |const, ancestor|
break const if ancestor == Object
break ancestor if ancestor.const_defined? name, false
const
end
# Go down the ancestors to check if it is owned directly. The check
# stops when we reach Object or the end of ancestors tree.
constant = constant.ancestors.inject constant do |const, ancestor|
break const if ancestor == Object
break ancestor if ancestor.const_defined? name, false
const
end

# owner is in Object, so raise
constant.const_get name, false
end
end
end
# owner is in Object, so raise
constant.const_get name, false
end
end
end
end

+ 2
- 3
lib/cryptcheck/engine/fixtures/string_io.rb View File

@@ -1,3 +1,2 @@
class StringIO
include Cryptcheck::Engine::Buffer
end
require 'stringio'
StringIO.include Cryptcheck::Engine::Buffer

+ 9
- 9
lib/cryptcheck/engine/fixtures/symbol.rb View File

@@ -1,12 +1,12 @@
class Symbol
# From https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-camelize
def camelize
self.to_s.sub(/^[a-z\d]*/) { |m| m.capitalize }
.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
.gsub('/', '::')
end
# From https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-camelize
def camelize
self.to_s.sub(/^[a-z\d]*/) { |m| m.capitalize }
.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
.gsub('/', '::')
end

def constantize
self.camelize.constantize
end
def constantize
self.camelize.constantize
end
end

+ 5
- 5
lib/cryptcheck/engine/id_classes.rb View File

@@ -1,7 +1,7 @@
module Cryptcheck::Engine
class IdClasses < DoubleHash
def initialize(*classes)
super classes.collect { |c| [c::ID, c] }.to_h
end
end
class IdClasses < DoubleHash
def initialize(*classes)
super classes.collect { |c| [c::ID, c] }.to_h
end
end
end

+ 669
- 609
lib/cryptcheck/engine/tls.rb
File diff suppressed because it is too large
View File


+ 61
- 61
lib/cryptcheck/engine/tls/alert.rb View File

@@ -1,63 +1,63 @@
module Cryptcheck::Engine
module Tls
class Alert
ID = 0x15
LEVELS = DoubleHash.new(
0x01 => :warning,
0x02 => :fatal
)
DESCRIPTIONS = DoubleHash.new(
0x00 => :close_notify,
0x0A => :unexpected_message,
0x14 => :bad_record_mac,
0x15 => :decryption_failed,
0x16 => :record_overflow,
0x1E => :decompression_failure,
0x28 => :handshake_failure,
0x29 => :no_certificate,
0x2B => :unsupported_certificate,
0x2C => :certificate_revoked,
0x2D => :certificate_expired,
0x2E => :certificate_unknown,
0x2F => :illegal_parameter,
0x30 => :unknown_ca,
0x31 => :access_denied,
0x32 => :decode_error,
0x33 => :decrypt_error,
0x3C => :export_restriction,
0x46 => :protocol_version,
0x47 => :insufficient_security,
0x50 => :internal_error,
0x5A => :user_canceled,
0x64 => :no_renegociation,
0x6E => :unsupported_extension,
)
attr_reader :level, :description
def initialize(level, description)
@level = level
@description = description
end
def self.read(_, io)
tmp = io.read_uint8
level = LEVELS[tmp]
raise ProtocolError, 'Unknown alert level 0x%02X' % tmp unless level
tmp = io.read_uint8
description = DESCRIPTIONS[tmp]
raise ProtocolError, 'Unknown alert description 0x%02X' % tmp unless description
self.new level, description
end
def write(_, io)
io.write_uint8 LEVELS.inverse @level
io.write_uint8 DESCRIPTIONS.inverse @description
end
end
end
module Tls
class Alert
ID = 0x15
LEVELS = DoubleHash.new(
0x01 => :warning,
0x02 => :fatal
)
DESCRIPTIONS = DoubleHash.new(
0x00 => :close_notify,
0x0A => :unexpected_message,
0x14 => :bad_record_mac,
0x15 => :decryption_failed,
0x16 => :record_overflow,
0x1E => :decompression_failure,
0x28 => :handshake_failure,
0x29 => :no_certificate,
0x2B => :unsupported_certificate,
0x2C => :certificate_revoked,
0x2D => :certificate_expired,
0x2E => :certificate_unknown,
0x2F => :illegal_parameter,
0x30 => :unknown_ca,
0x31 => :access_denied,
0x32 => :decode_error,
0x33 => :decrypt_error,
0x3C => :export_restriction,
0x46 => :protocol_version,
0x47 => :insufficient_security,
0x50 => :internal_error,
0x5A => :user_canceled,
0x64 => :no_renegociation,
0x6E => :unsupported_extension,
)
attr_reader :level, :description
def initialize(level, description)
@level = level
@description = description
end
def self.read(_, io)
tmp = io.read_uint8
level = LEVELS[tmp]
raise ProtocolError, 'Unknown alert level 0x%02X' % tmp unless level
tmp = io.read_uint8
description = DESCRIPTIONS[tmp]
raise ProtocolError, 'Unknown alert description 0x%02X' % tmp unless description
self.new level, description
end
def write(_, io)
io.write_uint8 LEVELS.inverse @level
io.write_uint8 DESCRIPTIONS.inverse @description
end
end
end
end

+ 14
- 14
lib/cryptcheck/engine/tls/change_cipher_spec.rb View File

@@ -1,18 +1,18 @@
module Cryptcheck::Engine
module Tls
class ChangeCipherSpec
ID = 0x14
PAYLOAD = 0x01
module Tls
class ChangeCipherSpec
ID = 0x14
PAYLOAD = 0x01

def self.read(_, io)
payload = io.read_uint8
raise ProtocolError, 'Expect change cipher spec payload to be 0x%02X, got 0x%02X' % [PAYLOAD, payload] unless payload == PAYLOAD
self.new
end
def self.read(_, io)
payload = io.read_uint8
raise ProtocolError, 'Expect change cipher spec payload to be 0x%02X, got 0x%02X' % [PAYLOAD, payload] unless payload == PAYLOAD
self.new
end

def write(_, io)
io.write_uint8 PAYLOAD
end
end
end
def write(_, io)
io.write_uint8 PAYLOAD
end
end
end
end

+ 53
- 53
lib/cryptcheck/engine/tls/handshake.rb View File

@@ -1,55 +1,55 @@
module Cryptcheck::Engine
module Tls
class Handshake
autoload :HelloRequest, 'cryptcheck/engine/tls/handshake/hello_request'
autoload :Extension, 'cryptcheck/engine/tls/handshake/extension'
autoload :ClientHello, 'cryptcheck/engine/tls/handshake/client_hello'
autoload :ServerHello, 'cryptcheck/engine/tls/handshake/server_hello'
autoload :Certificate, 'cryptcheck/engine/tls/handshake/certificate'
autoload :ServerKeyExchange, 'cryptcheck/engine/tls/handshake/server_key_exchange'
autoload :DhServerKeyExchange, 'cryptcheck/engine/tls/handshake/server_key_exchange'
autoload :EcdhServerKeyExchange, 'cryptcheck/engine/tls/handshake/server_key_exchange'
autoload :ServerHelloDone, 'cryptcheck/engine/tls/handshake/server_hello_done'
autoload :ClientKeyExchange, 'cryptcheck/engine/tls/handshake/client_key_exchange'
ID = 0x16
# 0x0d => :certificate_request,
# 0x0f => :certificate_verify,
# 0x14 => :finished
TYPES = IdClasses.new(
HelloRequest, # 0x00
ClientHello, # 0x01
ServerHello, # 0x02
Certificate, # 0x0B
ServerKeyExchange, # 0x0C
ServerHelloDone, # 0x0E
ClientKeyExchange, # 0x10
).freeze
def self.read(context, io)
tmp = io.read_uint8
type = TYPES[tmp]
raise ProtocolError, 'Unknown handshake type 0x%02X' % tmp unless type
io.read_uint 3
record = type.read context, io
self.new record
end
def write(context, io)
io2 = StringIO.new
@record.write context, io2
io.write_uint8 @record.class::ID
io.write_data 3, io2.string
end
attr_reader :record
def initialize(record)
@record = record
end
end
end
module Tls
class Handshake
autoload :HelloRequest, 'cryptcheck/engine/tls/handshake/hello_request'
autoload :Extension, 'cryptcheck/engine/tls/handshake/extension'
autoload :ClientHello, 'cryptcheck/engine/tls/handshake/client_hello'
autoload :ServerHello, 'cryptcheck/engine/tls/handshake/server_hello'
autoload :Certificate, 'cryptcheck/engine/tls/handshake/certificate'
autoload :ServerKeyExchange, 'cryptcheck/engine/tls/handshake/server_key_exchange'
autoload :DhServerKeyExchange, 'cryptcheck/engine/tls/handshake/server_key_exchange'
autoload :EcdhServerKeyExchange, 'cryptcheck/engine/tls/handshake/server_key_exchange'
autoload :ServerHelloDone, 'cryptcheck/engine/tls/handshake/server_hello_done'
autoload :ClientKeyExchange, 'cryptcheck/engine/tls/handshake/client_key_exchange'
ID = 0x16
# 0x0d => :certificate_request,
# 0x0f => :certificate_verify,
# 0x14 => :finished
TYPES = IdClasses.new(
HelloRequest, # 0x00
ClientHello, # 0x01
ServerHello, # 0x02
Certificate, # 0x0B
ServerKeyExchange, # 0x0C
ServerHelloDone, # 0x0E
ClientKeyExchange, # 0x10
).freeze
def self.read(context, io)
tmp = io.read_uint8
type = TYPES[tmp]
raise ProtocolError, 'Unknown handshake type 0x%02X' % tmp unless type
io.read_uint 3
record = type.read context, io
self.new record
end
def write(context, io)
io2 = StringIO.new
@record.write context, io2
io.write_uint8 @record.class::ID
io.write_data 3, io2.string
end
attr_reader :record
def initialize(record)
@record = record
end
end
end
end

+ 24
- 24
lib/cryptcheck/engine/tls/handshake/certificate.rb View File

@@ -1,31 +1,31 @@
require 'openssl'
module Cryptcheck::Engine
module Tls
class Handshake
class Certificate
ID = 0x0B
module Tls
class Handshake
class Certificate
ID = 0x0B

def self.read(_, io)
length = io.read_uint 3
certs = io.collect length do
der = io.read_data 3
OpenSSL::X509::Certificate.new der
end
self.new certs
end
def self.read(_, io)
length = io.read_uint 3
certs = io.collect length do
der = io.read_data 3
OpenSSL::X509::Certificate.new der
end
self.new certs
end

def write(_, io)
io2 = StringIO.new
@certificates.each { |c| io2.write_data 3, c.to_der }
io.write_data 3, io2.string
end
def write(_, io)
io2 = StringIO.new
@certificates.each { |c| io2.write_data 3, c.to_der }
io.write_data 3, io2.string
end

attr_reader :certificates
attr_reader :certificates

def initialize(certificates)
@certificates = certificates
end
end
end
end
def initialize(certificates)
@certificates = certificates
end
end
end
end
end

+ 53
- 53
lib/cryptcheck/engine/tls/handshake/client_hello.rb View File

@@ -1,66 +1,66 @@
require 'securerandom'

module Cryptcheck::Engine
module Tls
class Handshake
class ClientHello
ID = 0x01
module Tls
class Handshake
class ClientHello
ID = 0x01

def self.read(_, io)
version = Tls.read_version io
random = io.read 32
session = io.read_data :uint8
ciphers = Tls.read_ciphers io
compressions = Tls.read_compressions io
extensions = Extension.read_all io
self.new version, random, session, ciphers, compressions, extensions
end
def self.read(_, io)
version = Tls.read_version io
random = io.read 32
session = io.read_data :uint8
ciphers = Tls.read_ciphers io
compressions = Tls.read_compressions io
extensions = Extension.read_all io
self.new version, random, session, ciphers, compressions, extensions
end

def write(_, io)
Tls.write_version io, @version
io.write @random
io.write_data :uint8, @session
Tls.write_ciphers io, @ciphers
Tls.write_compressions io, @compressions
Extension.write_all io, @extensions
end
def write(_, io)
Tls.write_version io, @version
io.write @random
io.write_data :uint8, @session
Tls.write_ciphers io, @ciphers
Tls.write_compressions io, @compressions
Extension.write_all io, @extensions
end

attr_reader :version, :random, :session, :ciphers, :compressions, :extensions
attr_reader :version, :random, :session, :ciphers, :compressions, :extensions

def initialize(version, random, session, ciphers, compressions, extensions)
@version = version
@random = random
@session = session
@ciphers = ciphers
@compressions = compressions
@extensions = extensions
end
def initialize(version, random, session, ciphers, compressions, extensions)
@version = version
@random = random
@session = session
@ciphers = ciphers
@compressions = compressions
@extensions = extensions
end

private
private

class Builder_
include Builder
attributes :version, :random, :session
lists :cipher, :compression, :extension
class Builder_
include Builder
attributes :version, :random, :session
lists :cipher, :compression, :extension

def initialize
@ciphers = []
@compressions = %i[NULL]
@extensions = []
end
def initialize
@ciphers = []
@compressions = %i[NULL]
@extensions = []
end

def get
@random ||= SecureRandom.bytes 32
ClientHello.new @version, @random, @session, @ciphers, @compressions, @extensions
end
end
def get
@random ||= SecureRandom.bytes 32
ClientHello.new @version, @random, @session, @ciphers, @compressions, @extensions
end
end

def self.build(&block)
builder = Builder_.new
builder.instance_eval &block if block_given?
builder.get
end
end
end
end
def self.build(&block)
builder = Builder_.new
builder.instance_eval &block if block_given?
builder.get
end
end
end
end
end

+ 58
- 58
lib/cryptcheck/engine/tls/handshake/client_key_exchange.rb View File

@@ -1,60 +1,60 @@
module Cryptcheck::Engine
module Tls
class Handshake
class ClientKeyExchange
ID = 0x10
end
class ClientEllipticCurveDiffieHellmanPublic < ClientKeyExchange
def self.read(_, io)
public_key = io.read_data :uint8
self.new public_key
end
def write(_, io)
io.write_data :uint8, @public_key
end
attr_reader :public_key
def initialize(public_key)
@public_key = public_key
end
end
class ClientDiffieHellmanPublic < ClientKeyExchange
def self.read(_, io)
public_key = io.read_data :uint16
self.new public_key
end
def write(_, io)
io.write_data :uint16, @public_key
end
attr_reader :public_key
def initialize(public_key)
@public_key = public_key
end
end
class EncryptedPreMasterKeySecret < ClientKeyExchange
def self.read(_, io)
encrypted_pre_master_secret = io.read_data :uint16
self.new encrypted_pre_master_secret
end
def write(_, io)
io.write_data :uint16, @encrypted_pre_master_secret
end
attr_reader :encrypted_pre_master_secret
def initialize(encrypted_pre_master_secret)
@encrypted_pre_master_secret = encrypted_pre_master_secret
end
end
end
end
module Tls
class Handshake
class ClientKeyExchange
ID = 0x10
end
class ClientEllipticCurveDiffieHellmanPublic < ClientKeyExchange
def self.read(_, io)
public_key = io.read_data :uint8
self.new public_key
end
def write(_, io)
io.write_data :uint8, @public_key
end
attr_reader :public_key
def initialize(public_key)
@public_key = public_key
end
end
class ClientDiffieHellmanPublic < ClientKeyExchange
def self.read(_, io)
public_key = io.read_data :uint16
self.new public_key
end
def write(_, io)
io.write_data :uint16, @public_key
end
attr_reader :public_key
def initialize(public_key)
@public_key = public_key
end
end
class EncryptedPreMasterKeySecret < ClientKeyExchange
def self.read(_, io)
encrypted_pre_master_secret = io.read_data :uint16
self.new encrypted_pre_master_secret
end
def write(_, io)
io.write_data :uint16, @encrypted_pre_master_secret
end
attr_reader :encrypted_pre_master_secret
def initialize(encrypted_pre_master_secret)
@encrypted_pre_master_secret = encrypted_pre_master_secret
end
end
end
end
end

+ 109
- 109
lib/cryptcheck/engine/tls/handshake/extension.rb View File

@@ -1,123 +1,123 @@
module Cryptcheck::Engine
module Tls
class Handshake
class Extension
autoload :ServerName, 'cryptcheck/engine/tls/handshake/extension/server_name'
autoload :SupportedGroups, 'cryptcheck/engine/tls/handshake/extension/supported_groups'
autoload :SupportedVersions, 'cryptcheck/engine/tls/handshake/extension/supported_versions'
autoload :SignatureAlgorithms, 'cryptcheck/engine/tls/handshake/extension/signature_algorithms'
autoload :RenegotiationInfo, 'cryptcheck/engine/tls/handshake/extension/renegotiation_info'
module Tls
class Handshake
class Extension
autoload :ServerName, 'cryptcheck/engine/tls/handshake/extension/server_name'
autoload :SupportedGroups, 'cryptcheck/engine/tls/handshake/extension/supported_groups'
autoload :SupportedVersions, 'cryptcheck/engine/tls/handshake/extension/supported_versions'
autoload :SignatureAlgorithms, 'cryptcheck/engine/tls/handshake/extension/signature_algorithms'
autoload :RenegotiationInfo, 'cryptcheck/engine/tls/handshake/extension/renegotiation_info'

EXTENSIONS = IdClasses.new(
ServerName,
SupportedGroups,
SupportedVersions,
SignatureAlgorithms,
RenegotiationInfo
).freeze
EXTENSIONS = IdClasses.new(
ServerName,
SupportedGroups,
SupportedVersions,
SignatureAlgorithms,
RenegotiationInfo
).freeze

# region Types
TYPES = DoubleHash.new(
0x0000 => :server_name,
0x0001 => :max_fragment_length,
0x0002 => :client_certificate_url,
0x0003 => :trusted_ca_keys,
0x0004 => :truncated_hmac,
0x0005 => :status_request,
0x0006 => :user_mapping,
0x0007 => :client_authz,
0x0008 => :server_authz,
0x0009 => :cert_type,
0x000A => :supported_groups,
0x000B => :ec_point_formats,
0x000C => :srp,
0x000D => :signature_algorithms,
0x000E => :use_srtp,
0x000F => :heartbeat,
0x0010 => :application_layer_protocol_negotiation,
0x0011 => :status_request_v2,
0x0012 => :signed_certificate_timestamp,
0x0013 => :client_certificate_type,
0x0014 => :server_certificate_type,
0x0015 => :padding,
0x0016 => :encrypt_then_mac,
0x0017 => :extended_master_secret,
0x0018 => :token_binding,
0x0019 => :cached_info,
0x001A => :tls_lts,
0x001B => :compress_certificate,
0x001C => :record_size_limit,
0x001D => :pwd_protect,
0x001E => :pwd_clear,
0x001F => :password_salt,
0x0023 => :session_ticket,
0x0029 => :pre_shared_key,
0x002A => :early_data,
0x002B => :supported_versions,
0x002C => :cookie,
0x002D => :psk_key_exchange_modes,
0x002F => :certificate_authorities,
0x0030 => :oid_filters,
0x0031 => :post_handshake_auth,
0x0032 => :signature_algorithms_cert,
0x0033 => :key_share,
0x0034 => :transparency_info,
0xFF01 => :renegotiation_info,
).freeze
# endregion
# region Types
TYPES = DoubleHash.new(
0x0000 => :server_name,
0x0001 => :max_fragment_length,
0x0002 => :client_certificate_url,
0x0003 => :trusted_ca_keys,
0x0004 => :truncated_hmac,
0x0005 => :status_request,
0x0006 => :user_mapping,
0x0007 => :client_authz,
0x0008 => :server_authz,
0x0009 => :cert_type,
0x000A => :supported_groups,
0x000B => :ec_point_formats,
0x000C => :srp,
0x000D => :signature_algorithms,
0x000E => :use_srtp,
0x000F => :heartbeat,
0x0010 => :application_layer_protocol_negotiation,
0x0011 => :status_request_v2,
0x0012 => :signed_certificate_timestamp,
0x0013 => :client_certificate_type,
0x0014 => :server_certificate_type,
0x0015 => :padding,
0x0016 => :encrypt_then_mac,
0x0017 => :extended_master_secret,
0x0018 => :token_binding,
0x0019 => :cached_info,
0x001A => :tls_lts,
0x001B => :compress_certificate,
0x001C => :record_size_limit,
0x001D => :pwd_protect,
0x001E => :pwd_clear,
0x001F => :password_salt,
0x0023 => :session_ticket,
0x0029 => :pre_shared_key,
0x002A => :early_data,
0x002B => :supported_versions,
0x002C => :cookie,
0x002D => :psk_key_exchange_modes,
0x002F => :certificate_authorities,
0x0030 => :oid_filters,
0x0031 => :post_handshake_auth,
0x0032 => :signature_algorithms_cert,
0x0033 => :key_share,
0x0034 => :transparency_info,
0xFF01 => :renegotiation_info,
).freeze
# endregion

def self.read(io)
tmp = io.read_uint16
id = TYPES[tmp]
raise ProtocolError, 'Unknown extension 0x%04X' % tmp unless id
length = io.read_uint16
def self.read(io)
tmp = io.read_uint16
id = TYPES[tmp]
raise ProtocolError, 'Unknown extension 0x%04X' % tmp unless id
length = io.read_uint16

clazz = EXTENSIONS[id]
if clazz
clazz.read io
else
data = io.read length
Extension.new id, data
end
end
clazz = EXTENSIONS[id]
if clazz
clazz.read io
else
data = io.read length
Extension.new id, data
end
end

def self.read_all(io)
io.collect(:uint16) { self.read io }
end
def self.read_all(io)
io.collect(:uint16) { self.read io }
end

def self.write(io, extension)
io2 = StringIO.new
extension.write io2
def self.write(io, extension)
io2 = StringIO.new
extension.write io2

id = case extension
when Extension
extension.id
else
extension.class::ID
end
id = TYPES.inverse id
id = case extension
when Extension
extension.id
else
extension.class::ID
end
id = TYPES.inverse id

io.write_uint16 id
io.write_data :uint16, io2.string
end
io.write_uint16 id
io.write_data :uint16, io2.string
end

def self.write_all(io, extensions)
io2 = StringIO.new
extensions.each { |e| self.write io2, e }
io.write_data :uint16, io2.string
end
def self.write_all(io, extensions)
io2 = StringIO.new
extensions.each { |e| self.write io2, e }
io.write_data :uint16, io2.string
end

def write(io)
io.write @data
end
def write(io)
io.write @data
end

attr_reader :id, :data
attr_reader :id, :data

def initialize(id, data)
@id = id
@data = data
end
end
end
end
def initialize(id, data)
@id = id
@data = data
end
end
end
end
end

+ 23
- 23
lib/cryptcheck/engine/tls/handshake/extension/renegotiation_info.rb View File

@@ -1,30 +1,30 @@
module Cryptcheck::Engine
module Tls
class Handshake
class Extension
class RenegotiationInfo
ID = :renegotiation_info
module Tls
class Handshake
class Extension
class RenegotiationInfo
ID = :renegotiation_info

def self.read(io)
verify_data = io.read_data :uint8
self.new verify_data
end
def self.read(io)
verify_data = io.read_data :uint8
self.new verify_data
end

def write(io)
io.write_data :uint8, @verify_data
end
def write(io)
io.write_data :uint8, @verify_data
end

attr_reader :verify_data
attr_reader :verify_data

def initialize(verify_data)
@verify_data = verify_data
end
def initialize(verify_data)
@verify_data = verify_data
end

def self.build(verify_data = nil)
self.new verify_data
end
end
end
end
end
def self.build(verify_data = nil)
self.new verify_data
end
end
end
end
end
end

+ 40
- 40
lib/cryptcheck/engine/tls/handshake/extension/server_name.rb View File

@@ -1,49 +1,49 @@
module Cryptcheck::Engine
module Tls
class Handshake
class Extension
class ServerName
ID = :server_name
module Tls
class Handshake
class Extension
class ServerName
ID = :server_name

NAME_TYPE = DoubleHash.new(
0x00 => :hostname
).freeze
NAME_TYPE = DoubleHash.new(
0x00 => :hostname
).freeze

def self.read(io)
names = io.collect :uint16 do
tmp = io.read_uint8
type = NAME_TYPE[tmp]
raise ProtocolError, 'Unknown name type 0x%02X' % tmp unless type
def self.read(io)
names = io.collect :uint16 do
tmp = io.read_uint8
type = NAME_TYPE[tmp]
raise ProtocolError, 'Unknown name type 0x%02X' % tmp unless type

hostname = io.read_data :uint16
{ type: type, hostname: hostname }
end
self.new names
end
hostname = io.read_data :uint16
{ type: type, hostname: hostname }
end
self.new names
end

def write(io)
io2 = StringIO.new
@names.each do |name|
type = name[:type]
type = NAME_TYPE.inverse type
io2.write_uint8 type
hostname = name[:hostname]
io2.write_data :uint16, hostname
end
io.write_data :uint16, io2.string
end
def write(io)
io2 = StringIO.new
@names.each do |name|
type = name[:type]
type = NAME_TYPE.inverse type
io2.write_uint8 type
hostname = name[:hostname]
io2.write_data :uint16, hostname
end
io.write_data :uint16, io2.string
end

attr_reader :names
attr_reader :names

def initialize(names)
@names = names
end
def initialize(names)
@names = names
end

def self.build(*hostnames)
self.new hostnames.collect { |h| { type: :hostname, hostname: h } }
end
end
end
end
end
def self.build(*hostnames)
self.new hostnames.collect { |h| { type: :hostname, hostname: h } }
end
end
end
end
end
end

+ 23
- 23
lib/cryptcheck/engine/tls/handshake/extension/signature_algorithms.rb View File

@@ -1,30 +1,30 @@
module Cryptcheck::Engine
module Tls
class Handshake
class Extension
class SignatureAlgorithms
ID = :signature_algorithms
module Tls
class Handshake
class Extension
class SignatureAlgorithms
ID = :signature_algorithms

def self.read(io)
schemes = Signature.read_schemes io
self.new schemes
end
def self.read(io)
schemes = Signature.read_schemes io
self.new schemes
end

def write(io)
Signature.write_schemes io, @schemes
end
def write(io)
Signature.write_schemes io, @schemes
end

attr_reader :schemes
attr_reader :schemes

def initialize(schemes)
@schemes = schemes
end
def initialize(schemes)
@schemes = schemes
end

def self.build(*schemes)
self.new schemes
end
end
end
end
end
def self.build(*schemes)
self.new schemes
end
end
end
end
end
end

+ 23
- 23
lib/cryptcheck/engine/tls/handshake/extension/supported_groups.rb View File

@@ -1,30 +1,30 @@
module Cryptcheck::Engine
module Tls
class Handshake
class Extension
class SupportedGroups
ID = :supported_groups
module Tls
class Handshake
class Extension
class SupportedGroups
ID = :supported_groups

def self.read(io)
groups = Tls.read_groups io
self.new groups
end
def self.read(io)
groups = Tls.read_groups io
self.new groups
end

def write(io)
Tls.write_groups io, @groups
end
def write(io)
Tls.write_groups io, @groups
end

attr_reader :groups
attr_reader :groups

def initialize(groups)
@groups = groups
end
def initialize(groups)
@groups = groups
end

def self.build(*curves)
self.new curves
end
end
end