Class: TTTLS13::Endpoint

Inherits:
Object
  • Object
show all
Defined in:
lib/tttls1.3/endpoint.rb

Overview

rubocop: disable Metrics/ClassLength

Class Method Summary collapse

Class Method Details

.do_exporter(secret, digest, label, context, key_length) ⇒ String

Parameters:

  • secret (String)

    (early_)exporter_secret

  • digest (String)

    name of digest algorithm

  • label (String)
  • context (String)
  • key_length (Integer)

Returns:

  • (String)


235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/tttls1.3/endpoint.rb', line 235

def do_exporter(secret, digest, label, context, key_length)
  derived_secret = KeySchedule.hkdf_expand_label(
    secret,
    label,
    OpenSSL::Digest.digest(digest, ''),
    OpenSSL::Digest.new(digest).digest_length,
    digest
  )

  KeySchedule.hkdf_expand_label(
    derived_secret,
    'exporter',
    OpenSSL::Digest.digest(digest, context),
    key_length,
    digest
  )
end

.exporter(label, context, key_length, exporter_secret, cipher_suite) ⇒ String?

Parameters:

  • label (String)
  • context (String)
  • key_length (Integer)
  • exporter_secret (String)
  • cipher_suite (TTTLS13::CipherSuite)

Returns:

  • (String, nil)


14
15
16
17
18
19
# File 'lib/tttls1.3/endpoint.rb', line 14

def self.exporter(label, context, key_length, exporter_secret, cipher_suite)
  return nil if exporter_secret.nil? || cipher_suite.nil?

  digest = CipherSuite.digest(cipher_suite)
  do_exporter(exporter_secret, digest, label, context, key_length)
end

.gen_cipher(cipher_suite, write_key, write_iv) ⇒ TTTLS13::Cryptograph::Aead

Parameters:

Returns:



26
27
28
29
30
31
32
33
34
# File 'lib/tttls1.3/endpoint.rb', line 26

def self.gen_cipher(cipher_suite, write_key, write_iv)
  seq_num = SequenceNumber.new
  Cryptograph::Aead.new(
    cipher_suite:,
    write_key:,
    write_iv:,
    sequence_number: seq_num
  )
end

.matching_san?(cert, name) ⇒ Boolean

Parameters:

  • cert (OpenSSL::X509::Certificate)
  • name (String)

Returns:

  • (Boolean)


216
217
218
219
220
221
222
223
224
225
# File 'lib/tttls1.3/endpoint.rb', line 216

def self.matching_san?(cert, name)
  san = cert.extensions.find { |ex| ex.oid == 'subjectAltName' }
  return false if san.nil?

  ostr = OpenSSL::ASN1.decode(san.to_der).value.last
  OpenSSL::ASN1.decode(ostr.value)
               .map(&:value)
               .map { |s| s.gsub('.', '\.').gsub('*', '.*') }
               .any? { |s| name.match(/#{s}/) }
end

.select_signature_algorithms(signature_algorithms, crt) ⇒ Array of TTTLS13::Message::Extension::SignatureAlgorithms

Parameters:

  • signature_algorithms (Array of SignatureAlgorithms)
  • crt (OpenSSL::X509::Certificate)

Returns:



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/tttls1.3/endpoint.rb', line 186

def self.select_signature_algorithms(signature_algorithms, crt)
  pka = OpenSSL::ASN1.decode(crt.public_key.to_der)
                     .value.first.value.first.value
  signature_algorithms.select do |sa|
    case sa
    when SignatureScheme::ECDSA_SECP256R1_SHA256,
         SignatureScheme::ECDSA_SECP384R1_SHA384,
         SignatureScheme::ECDSA_SECP521R1_SHA512
      pka == 'id-ecPublicKey'
    when SignatureScheme::RSA_PSS_PSS_SHA256,
         SignatureScheme::RSA_PSS_PSS_SHA384,
         SignatureScheme::RSA_PSS_PSS_SHA512
      pka == 'rsassaPss'
    when SignatureScheme::RSA_PSS_RSAE_SHA256,
         SignatureScheme::RSA_PSS_RSAE_SHA384,
         SignatureScheme::RSA_PSS_RSAE_SHA512
      pka == 'rsaEncryption'
    else
      # RSASSA-PKCS1-v1_5 algorithms refer solely to signatures which appear
      # in certificates and are not defined for use in signed TLS handshake
      # messages
      false
    end
  end
end

.sign_certificate_verify(key:, signature_scheme:, context:, hash:) ⇒ String

Parameters:

Returns:

  • (String)

Raises:



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/tttls1.3/endpoint.rb', line 63

def self.sign_certificate_verify(key:, signature_scheme:, context:, hash:)
  content = "\x20" * 64 + context + "\x00" + hash

  # RSA signatures MUST use an RSASSA-PSS algorithm, regardless of whether
  # RSASSA-PKCS1-v1_5 algorithms appear in "signature_algorithms".
  case signature_scheme
  when SignatureScheme::RSA_PKCS1_SHA256,
       SignatureScheme::RSA_PSS_RSAE_SHA256,
       SignatureScheme::RSA_PSS_PSS_SHA256
    key.sign_pss('SHA256', content, salt_length: :digest,
                                    mgf1_hash: 'SHA256')
  when SignatureScheme::RSA_PKCS1_SHA384,
       SignatureScheme::RSA_PSS_RSAE_SHA384,
       SignatureScheme::RSA_PSS_PSS_SHA384
    key.sign_pss('SHA384', content, salt_length: :digest,
                                    mgf1_hash: 'SHA384')
  when SignatureScheme::RSA_PKCS1_SHA512,
       SignatureScheme::RSA_PSS_RSAE_SHA512,
       SignatureScheme::RSA_PSS_PSS_SHA512
    key.sign_pss('SHA512', content, salt_length: :digest,
                                    mgf1_hash: 'SHA512')
  when SignatureScheme::ECDSA_SECP256R1_SHA256
    key.sign('SHA256', content)
  when SignatureScheme::ECDSA_SECP384R1_SHA384
    key.sign('SHA384', content)
  when SignatureScheme::ECDSA_SECP521R1_SHA512
    key.sign('SHA512', content)
  else # TODO: ED25519, ED448
    terminate(:internal_error)
  end
end

.sign_finished(digest:, finished_key:, hash:) ⇒ String

Parameters:

  • digest (String)

    name of digest algorithm

  • finished_key (String)
  • hash (String)

Returns:

  • (String)


142
143
144
# File 'lib/tttls1.3/endpoint.rb', line 142

def self.sign_finished(digest:, finished_key:, hash:)
  OpenSSL::HMAC.digest(digest, finished_key, hash)
end

.sign_psk_binder(ch1:, hrr:, ch:, binder_key:, digest:) ⇒ String

Parameters:

Returns:

  • (String)


43
44
45
46
47
48
49
50
51
52
53
# File 'lib/tttls1.3/endpoint.rb', line 43

def self.sign_psk_binder(ch1:, hrr:, ch:, binder_key:, digest:)
  # TODO: ext binder
  hash_len = OpenSSL::Digest.new(digest).digest_length
  tt = Transcript.new
  tt[CH1] = [ch1, ch1.serialize] unless ch1.nil?
  tt[HRR] = [hrr, hrr.serialize] unless hrr.nil?
  tt[CH] = [ch, ch.serialize]
  # transcript-hash (CH1 + HRR +) truncated-CH
  hash = tt.truncate_hash(digest, CH, hash_len + 3)
  OpenSSL::HMAC.digest(digest, binder_key, hash)
end

.trusted_certificate?(certificate_list, ca_file = nil, hostname = nil) ⇒ Boolean

Parameters:

  • certificate_list (Array of CertificateEntry)
  • ca_file (String) (defaults to: nil)

    path to ca.crt

  • hostname (String) (defaults to: nil)

Returns:

  • (Boolean)


162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/tttls1.3/endpoint.rb', line 162

def self.trusted_certificate?(certificate_list,
                              ca_file = nil,
                              hostname = nil)
  chain = certificate_list.map(&:cert_data).map do |c|
    OpenSSL::X509::Certificate.new(c)
  end
  cert = chain.shift

  # not support CN matching, only support SAN matching
  return false if !hostname.nil? && !matching_san?(cert, hostname)

  store = OpenSSL::X509::Store.new
  store.set_default_paths
  store.add_file(ca_file) unless ca_file.nil?
  # TODO: parse authorityInfoAccess::CA Issuers
  ctx = OpenSSL::X509::StoreContext.new(store, cert, chain)
  now = Time.now
  ctx.verify && cert.not_before < now && now < cert.not_after
end

.verified_certificate_verify?(public_key:, signature_scheme:, signature:, context:, hash:) ⇒ Boolean

Parameters:

  • public_key (OpenSSL::PKey::PKey)
  • signature_scheme (TTTLS13::SignatureScheme)
  • signature (String)
  • context (String)
  • hash (String)

Returns:

  • (Boolean)

Raises:



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/tttls1.3/endpoint.rb', line 104

def self.verified_certificate_verify?(public_key:, signature_scheme:,
                                      signature:, context:, hash:)
  content = "\x20" * 64 + context + "\x00" + hash

  # RSA signatures MUST use an RSASSA-PSS algorithm, regardless of whether
  # RSASSA-PKCS1-v1_5 algorithms appear in "signature_algorithms".
  case signature_scheme
  when SignatureScheme::RSA_PKCS1_SHA256,
       SignatureScheme::RSA_PSS_RSAE_SHA256,
       SignatureScheme::RSA_PSS_PSS_SHA256
    public_key.verify_pss('SHA256', signature, content, salt_length: :auto,
                                                        mgf1_hash: 'SHA256')
  when SignatureScheme::RSA_PKCS1_SHA384,
       SignatureScheme::RSA_PSS_RSAE_SHA384,
       SignatureScheme::RSA_PSS_PSS_SHA384
    public_key.verify_pss('SHA384', signature, content, salt_length: :auto,
                                                        mgf1_hash: 'SHA384')
  when SignatureScheme::RSA_PKCS1_SHA512,
       SignatureScheme::RSA_PSS_RSAE_SHA512,
       SignatureScheme::RSA_PSS_PSS_SHA512
    public_key.verify_pss('SHA512', signature, content, salt_length: :auto,
                                                        mgf1_hash: 'SHA512')
  when SignatureScheme::ECDSA_SECP256R1_SHA256
    public_key.verify('SHA256', signature, content)
  when SignatureScheme::ECDSA_SECP384R1_SHA384
    public_key.verify('SHA384', signature, content)
  when SignatureScheme::ECDSA_SECP521R1_SHA512
    public_key.verify('SHA512', signature, content)
  else # TODO: ED25519, ED448
    terminate(:internal_error)
  end
end

.verified_finished?(finished:, digest:, finished_key:, hash:) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


152
153
154
155
# File 'lib/tttls1.3/endpoint.rb', line 152

def self.verified_finished?(finished:, digest:, finished_key:, hash:)
  sign_finished(digest:, finished_key:, hash:) \
  == finished.verify_data
end