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.



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
401
# File 'lib/ruby-tls/ssl.rb', line 358

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.



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

def alpn_set
  @alpn_set
end

#alpn_strObject (readonly)

Returns the value of attribute alpn_str.



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

def alpn_str
  @alpn_str
end

#is_serverObject (readonly)

Returns the value of attribute is_server.



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

def is_server
  @is_server
end

#ssl_ctxObject (readonly)

Returns the value of attribute ssl_ctx.



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

def ssl_ctx
  @ssl_ctx
end

Instance Method Details

#add_server_name_indicationObject



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

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



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

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

#set_max_proto_version(version) ⇒ Object



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

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



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

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