-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
openssl.cr
137 lines (127 loc) · 4 KB
/
openssl.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
94
95
96
97
98
99
100
101
102
103
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
136
137
require "./openssl/lib_ssl"
# ## OpenSSL Integration
#
# - TLS sockets need a context, potentially with keys (required for servers) and configuration.
# - TLS sockets will wrap the underlying TCP socket, and any further communication must happen through the `OpenSSL::SSL::Socket` only.
#
# ## Usage Example
#
# Recommended ciphers can be taken from:
# - [OWASP Wiki](https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Cryptographic_Ciphers)
# - [Cipherli.st](https://cipherli.st/)
# - A full list is available at the [OpenSSL Docs](https://www.openssl.org/docs/man1.1.0/apps/ciphers.html#CIPHER-STRINGS)
#
# Do note that:
# - Crystal does its best to provide sane configuration defaults (see [Mozilla-Intermediate](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29)).
# - Linked version of OpenSSL need to be checked for supporting specific protocols and ciphers.
# - If any configurations or choices in Crystal regarding SSL settings and security are found to be lacking or need
# improvement please [open an issue](https://github.com/crystal-lang/crystal/issues/new) and let us know.
#
# ### Server side
#
# NOTE: For the below example to work, a key pair should be attained.
#
# ```
# require "socket"
# require "openssl"
#
# def server
# # Bind new TCPSocket to port 5555
# socket = TCPServer.new(5555)
#
# context = OpenSSL::SSL::Context::Server.new
# context.private_key = "/path/to/private.key"
# context.certificate_chain = "/path/to/public.cert"
#
# puts "Server is up"
#
# socket.accept do |client|
# puts "Got client"
#
# bytes = Bytes.new(20)
#
# ssl_socket = OpenSSL::SSL::Socket::Server.new(client, context)
# ssl_socket.read(bytes)
#
# puts String.new(bytes)
# end
# end
# ```
#
# ### Client side
#
# ```
# require "socket"
# require "openssl"
#
# def client
# socket = TCPSocket.new("127.0.0.1", 5555)
# context = OpenSSL::SSL::Context::Client.new
#
# ssl_socket = OpenSSL::SSL::Socket::Client.new(socket, context)
# ssl_socket << "Testing"
# end
# ```
module OpenSSL
class Error < Exception
getter! code : LibCrypto::ULong
def initialize(message = nil, fetched = false, cause : Exception? = nil)
@code ||= LibCrypto::ULong.new(0)
if fetched
super(message, cause: cause)
else
@code, error = fetch_error_details
super(message ? "#{message}: #{error}" : error, cause: cause)
end
end
protected def fetch_error_details
code = LibCrypto.err_get_error
message = String.new(LibCrypto.err_error_string(code, nil)) unless code == 0
{code, message || "Unknown or no error"}
end
end
module SSL
alias Modes = LibSSL::Modes
alias Options = LibSSL::Options
alias VerifyMode = LibSSL::VerifyMode
alias ErrorType = LibSSL::SSLError
{% if compare_versions(LibSSL::OPENSSL_VERSION, "1.0.2") >= 0 %}
alias X509VerifyFlags = LibCrypto::X509VerifyFlags
{% end %}
class Error < OpenSSL::Error
getter error : ErrorType
def initialize(ssl : LibSSL::SSL, return_code : LibSSL::Int, func = nil)
@error = LibSSL.ssl_get_error(ssl, return_code)
case @error
when .none?
message = "Raised erroneously"
when .syscall?
@code, message = fetch_error_details
if @code == 0
case return_code
when 0
message = "Unexpected EOF"
when -1
cause = Errno.new(func || "OpenSSL")
message = "I/O error"
else
message = "Unknown error"
end
end
when .ssl?
@code, message = fetch_error_details
else
message = @error.to_s
end
super(func ? "#{func}: #{message}" : message, true, cause: cause)
end
end
end
end
require "./openssl/bio"
require "./openssl/ssl/*"
require "./openssl/digest/*"
require "./openssl/md5"
require "./openssl/x509/x509"
require "./openssl/pkcs5"
require "./openssl/cipher"