Class: RubyTls::SSL::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby-tls/ssl.rb

Constant Summary collapse

CIPHERS =
'EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4'
SESSION =
'ruby-tls'
ALPN_LOOKUP =
::Concurrent::Map.new
ALPN_Select_CB =
FFI::Function.new(:int, [
    # array of str, unit8 out,uint8 in,        *arg
    :pointer, :pointer, :pointer, :string, :uint, :pointer
]) do |ssl_p, out, outlen, inp, inlen, arg|
    ssl = Box::InstanceLookup[ssl_p.address]
    return SSL::SSL_TLSEXT_ERR_ALERT_FATAL unless ssl

    protos = ssl.context.alpn_str
    status = SSL.SSL_select_next_proto(out, outlen, protos, protos.length, inp, inlen)
    ssl.negotiated

    case status
    when SSL::OPENSSL_NPN_UNSUPPORTED
        SSL::SSL_TLSEXT_ERR_ALERT_FATAL
    when SSL::OPENSSL_NPN_NEGOTIATED
        SSL::SSL_TLSEXT_ERR_OK
    when SSL::OPENSSL_NPN_NO_OVERLAP
        SSL::SSL_TLSEXT_ERR_ALERT_WARNING
    end
end
ServerNameCB =
FFI::Function.new(:int, [:pointer, :pointer, :pointer]) do |ssl, _, _|
    ruby_ssl = Box::InstanceLookup[ssl.address]
    return SSL::SSL_TLSEXT_ERR_NOACK unless ruby_ssl

    ctx = ruby_ssl.hosts[SSL.SSL_get_servername(ssl, SSL::TLSEXT_NAMETYPE_host_name)]
    if ctx
        SSL.SSL_set_SSL_CTX(ssl, ctx.ssl_ctx)
        SSL::SSL_TLSEXT_ERR_OK
    else
        SSL::SSL_TLSEXT_ERR_ALERT_FATAL
    end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(server, options = {}) ⇒ Context

Returns a new instance of Context.



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/ruby-tls/ssl.rb', line 357

def initialize(server, options = {})
    @is_server = server

    if @is_server
        @ssl_ctx = SSL.SSL_CTX_new(SSL.TLS_server_method)
        set_private_key(options[:private_key] || SSL::DEFAULT_PRIVATE)
        set_certificate(options[:cert_chain]  || SSL::DEFAULT_CERT)
        set_client_ca(options[:client_ca])
    else
        @ssl_ctx = SSL.SSL_CTX_new(SSL.TLS_client_method)
    end

    SSL.SSL_CTX_set_options(@ssl_ctx, SSL::SSL_OP_ALL)
    SSL.SSL_CTX_set_mode(@ssl_ctx, SSL::SSL_MODE_RELEASE_BUFFERS)

    SSL.SSL_CTX_set_cipher_list(@ssl_ctx, options[:ciphers] || CIPHERS)
    @alpn_set = false

    version = options[:version]
    if version
        vresult = set_min_proto_version(version)
        raise "#{version} is unsupported" unless vresult
    end

    if @is_server
        SSL.SSL_CTX_sess_set_cache_size(@ssl_ctx, 128)
        SSL.SSL_CTX_set_session_id_context(@ssl_ctx, SESSION, 8)

        if SSL::ALPN_SUPPORTED && options[:protocols]
            @alpn_str = Context.build_alpn_string(options[:protocols])
            SSL.SSL_CTX_set_alpn_select_cb(@ssl_ctx, ALPN_Select_CB, nil)
            @alpn_set = true
        end
    else
        set_private_key(options[:private_key])
        set_certificate(options[:cert_chain])

        # Check for ALPN support
        if SSL::ALPN_SUPPORTED && options[:protocols]
            protocols = Context.build_alpn_string(options[:protocols])
            @alpn_set = SSL.SSL_CTX_set_alpn_protos(@ssl_ctx, protocols, protocols.length) == 0
        end
    end
end

Instance Attribute Details

#alpn_setObject (readonly)

Returns the value of attribute alpn_set.



429
430
431
# File 'lib/ruby-tls/ssl.rb', line 429

def alpn_set
  @alpn_set
end

#alpn_strObject (readonly)

Returns the value of attribute alpn_str.



430
431
432
# File 'lib/ruby-tls/ssl.rb', line 430

def alpn_str
  @alpn_str
end

#is_serverObject (readonly)

Returns the value of attribute is_server.



427
428
429
# File 'lib/ruby-tls/ssl.rb', line 427

def is_server
  @is_server
end

#ssl_ctxObject (readonly)

Returns the value of attribute ssl_ctx.



428
429
430
# File 'lib/ruby-tls/ssl.rb', line 428

def ssl_ctx
  @ssl_ctx
end

Instance Method Details

#add_server_name_indicationObject



432
433
434
435
# File 'lib/ruby-tls/ssl.rb', line 432

def add_server_name_indication
    raise 'only valid for server mode context' unless @is_server
    SSL.SSL_CTX_set_tlsext_servername_callback(@ssl_ctx, ServerNameCB)
end

#cleanupObject



420
421
422
423
424
425
# File 'lib/ruby-tls/ssl.rb', line 420

def cleanup
    if @ssl_ctx
        SSL.SSL_CTX_free(@ssl_ctx)
        @ssl_ctx = nil
    end
end

#set_max_proto_version(version) ⇒ Object



412
413
414
415
416
417
418
# File 'lib/ruby-tls/ssl.rb', line 412

def set_max_proto_version(version)
    return false unless VERSION_SUPPORTED
    num = SSL.const_get("#{version}_VERSION")
    SSL.SSL_CTX_set_max_proto_version(@ssl_ctx, num) == 1
rescue NameError
    false
end

#set_min_proto_version(version) ⇒ Object

Version can be one of: :SSL3, :TLS1, :TLS1_1, :TLS1_2, :TLS1_3, :TLS_MAX



404
405
406
407
408
409
410
# File 'lib/ruby-tls/ssl.rb', line 404

def set_min_proto_version(version)
    return false unless VERSION_SUPPORTED
    num = SSL.const_get("#{version}_VERSION")
    SSL.SSL_CTX_set_min_proto_version(@ssl_ctx, num) == 1
rescue NameError
    false
end