
commit
fff6f74456
10 changed files with 400 additions and 0 deletions
@ -0,0 +1,4 @@ |
|||
name = "OpenSSL" |
|||
uuid = "13378672-9967-48ef-8ae3-b2866786e7a0" |
|||
authors = ["nixo <nicolo@nixo.xyz>"] |
|||
version = "0.1.0" |
@ -0,0 +1,43 @@ |
|||
module OpenSSL |
|||
|
|||
export ca_chain!, close |
|||
|
|||
const libssl = "/gnu/store/hcxpkksmbql6s4al8yy2myr25kh4cic0-openssl-1.1.1g/lib/libssl.so" |
|||
|
|||
include("consts.jl") |
|||
include("evp_md.jl") |
|||
include("types.jl") |
|||
export SSLContext, SSLClient |
|||
include("bio.jl") |
|||
|
|||
include("context.jl") |
|||
|
|||
include("ssl.jl") |
|||
|
|||
include("certs.jl") |
|||
|
|||
include("flow_control.jl") |
|||
|
|||
import Base.read |
|||
function read(client::SSLClient) |
|||
buf = Vector{UInt8}(undef, 64) |
|||
n = ssl_read!(client, buf) |
|||
return (n, buf) |
|||
end |
|||
|
|||
import Base.write |
|||
function write(client::SSLClient, data) |
|||
send_unencrypted_bytes(client, data) |
|||
do_encrypt(client) |
|||
do_sock_write(client) |
|||
end |
|||
write(c::SSLClient, s::String) = write(c, Vector{UInt8}(s)) |
|||
|
|||
import Base.close |
|||
function close(client::SSLClient) |
|||
shutdown(client) |
|||
close(client.sock) |
|||
free(client) |
|||
end |
|||
|
|||
end # module |
@ -0,0 +1,26 @@ |
|||
s_mem() = ccall((:BIO_s_mem, libssl), Ptr{Cvoid}, ()) |
|||
bio_new(mem::Ptr{Cvoid}) = ccall((:BIO_new, libssl), Ptr{Cvoid}, (Ptr{Cvoid},), mem) |
|||
bio_new() = bio_new(s_mem()) |
|||
|
|||
function set_bio!(client::SSLClient, rbio, wbio) |
|||
client.rbio = rbio |
|||
client.wbio = wbio |
|||
ccall((:SSL_set_bio, libssl), Ptr{Cvoid}, (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}), |
|||
client.ssl, rbio, wbio) |
|||
end |
|||
|
|||
function bio_write!(client::SSLClient, data) |
|||
ccall((:BIO_write, libssl), Cint, (Ptr{Cvoid},Ptr{Cvoid},Cint), |
|||
client.rbio, data, length(data)) |
|||
end |
|||
|
|||
function bio_read(client::SSLClient) |
|||
buff = Vector{UInt8}(undef, 64) |
|||
n = ccall((:BIO_read, libssl), Cint, (Ptr{Cvoid},Ptr{Cvoid},Cint), |
|||
client.wbio, buff, length(buff)) |
|||
n, n > 0 ? buff[1:n] : buff |
|||
end |
|||
|
|||
bio_test_flags(bio, flags) = |
|||
ccall((:BIO_test_flags, libssl), Cint, (Ptr{Cvoid},Cint), bio, flags) != 0 |
|||
bio_should_retry(bio) = bio_test_flags(bio, BIO_FLAGS_SHOULD_RETRY) |
@ -0,0 +1,46 @@ |
|||
function ca_chain!(context::SSLContext, crt_file, key_file) |
|||
res = ccall((:SSL_CTX_use_certificate_file, libssl), Cint, (Ptr{Cvoid}, Cstring, Cint), |
|||
context.ptr, crt_file, SSL_FILETYPE_PEM) |
|||
if res <= 0 |
|||
@error res |
|||
end |
|||
res = ccall((:SSL_CTX_use_PrivateKey_file, libssl), Cint, (Ptr{Cvoid}, Cstring, Cint), |
|||
context.ptr, key_file, SSL_FILETYPE_PEM) |
|||
if res <= 0 |
|||
@error res |
|||
end |
|||
if ccall((:SSL_CTX_check_private_key, libssl), Cint, (Ptr{Cvoid}, ), context.ptr) != 1 |
|||
@error "Private and certificate is not matching" |
|||
end |
|||
return nothing |
|||
end |
|||
|
|||
get_peer_certificate(client::SSLClient) = |
|||
ccall((:SSL_get_peer_certificate, libssl), Ptr{Cvoid}, (Ptr{Cvoid},), client.ssl) |
|||
|
|||
verify_certificate(client::SSLClient) = |
|||
ccall((:SSL_get_verify_result, libssl), Clong, (Ptr{Cvoid},), |
|||
client.ssl) |
|||
|
|||
function X509_get_serialNumber(X509) |
|||
asn_ptr = ccall((:X509_get_serialNumber, libssl), Ptr{Cvoid}, (Ptr{Cvoid},), X509) |
|||
res = Ref{Clong}(0) |
|||
# TODO: check return value (1:ok) |
|||
# TODO: Move to own func and call it |
|||
n = ccall((:ASN1_INTEGER_get_int64, libssl), Cint, (Ref{Clong},Ptr{Cvoid}), |
|||
res, asn_ptr) |
|||
res[] |
|||
end |
|||
|
|||
function X509_digest(X509; md_type = EVP_sha1()) |
|||
len = Ref{Cint}(0) |
|||
md = zeros(UInt8, EVP_MAX_MD_SIZE) |
|||
res = ccall((:X509_digest, libssl), Cint, |
|||
(Ptr{Cvoid}, Ptr{Cvoid}, Ref{Cuchar}, Ref{Cint}), |
|||
X509, md_type, md, len) == 1 |
|||
!res && throw("Error") |
|||
md[1:len[]] |
|||
end |
|||
|
|||
digest_to_string(d::Array{UInt8}) = join(string.(d, base = 16), ":") |
|||
digest_to_string_62(d::Vector{T}) where T = join(string.(reinterpret(UInt32, d), base = 62), ":") |
@ -0,0 +1,39 @@ |
|||
const DEFAULT_BUF_SIZE = 64 |
|||
|
|||
# TODO: Add here the missing related to those three |
|||
const SSL_FILETYPE_PEM = 1 |
|||
const SSL_OP_NO_SSLv3 = 0x02000000 |
|||
const EVP_MAX_MD_SIZE = 64 |
|||
|
|||
const BIO_FLAGS_READ = 0x01 |
|||
const BIO_FLAGS_WRITE = 0x02 |
|||
const BIO_FLAGS_IO_SPECIAL = 0x03 |
|||
const BIO_FLAGS_RWS = (BIO_FLAGS_READ|BIO_FLAGS_WRITE|BIO_FLAGS_IO_SPECIAL) |
|||
const BIO_FLAGS_SHOULD_RETRY = 0x08 |
|||
const BIO_FLAGS_MEM_RDONLY = 0x200 |
|||
const BIO_FLAGS_NONCLEAR_RST = 0x400 |
|||
const BIO_FLAGS_IN_EOF = 0x800 |
|||
|
|||
@enum SSL_ERRORS begin |
|||
SSL_ERROR = 1 |
|||
SSL_WANT_READ = 2 |
|||
SSL_WANT_WRITE = 3 |
|||
SSL_SYSCALL = 5 |
|||
SSL_ZERO_RETURN = 6 |
|||
end |
|||
|
|||
@enum SSL_VERIFY begin |
|||
VERIFY_NONE = 0x00 |
|||
VERIFY_PEER = 0x01 |
|||
VERIFY_FAIL_IF_NO_PEER_CERT = 0x02 |
|||
VERIFY_CLIENT_ONCE = 0x04 |
|||
VERIFY_POST_HANDSHAKE = 0x08 |
|||
end |
|||
|
|||
@enum SSL_VERSION begin |
|||
SSL3_VERSION = 0x0300 |
|||
TLS1_VERSION |
|||
TLS1_1_VERSION |
|||
TLS1_2_VERSION |
|||
TLS1_3_VERSION |
|||
end |
@ -0,0 +1,19 @@ |
|||
function set_options!(ctx::SSLContext, options::UInt32) |
|||
ccall((:SSL_CTX_set_options, libssl), Ptr{Cvoid}, (Ptr{Cvoid}, Clong), |
|||
ctx.ptr, options) |
|||
nothing |
|||
end |
|||
|
|||
function set_verify_mode(ctx::SSLContext, mode, callback = C_NULL) |
|||
ccall((:SSL_CTX_set_verify, libssl), Cvoid, (Ptr{Cvoid}, Cint, Ptr{Cvoid}), |
|||
ctx.ptr, mode, callback) |
|||
end |
|||
|
|||
post_handshake_auth(ctx::SSLContext; enable::Bool = true) = |
|||
ccall((:SSL_CTX_set_post_handshake_auth, libssl), Cvoid, (Ptr{Cvoid}, Cint), ctx.ptr, enable ? 1 : 0) |
|||
|
|||
function set_verify_callback(ctx::SSLContext, callback) |
|||
args = C_NULL |
|||
ccall((:SSL_CTX_set_cert_verify_callback, libssl), Cvoid, |
|||
(Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), ctx.ptr, callback, args) |
|||
end |
@ -0,0 +1,23 @@ |
|||
EVP_md_null() = ccall((:EVP_md_null, libssl), Ptr{Cvoid}, ()) |
|||
EVP_md4() = ccall((:EVP_md4, libssl), Ptr{Cvoid}, ()) |
|||
EVP_md5() = ccall((:EVP_md5, libssl), Ptr{Cvoid}, ()) |
|||
EVP_md5_sha1() = ccall((:EVP_md5_sha1, libssl), Ptr{Cvoid}, ()) |
|||
EVP_blake2b512() = ccall((:EVP_blake2b512, libssl), Ptr{Cvoid}, ()) |
|||
EVP_blake2s256() = ccall((:EVP_blake2s256, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha1() = ccall((:EVP_sha1, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha224() = ccall((:EVP_sha224, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha256() = ccall((:EVP_sha256, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha384() = ccall((:EVP_sha384, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha512() = ccall((:EVP_sha512, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha512_224() = ccall((:EVP_sha512_224, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha512_256() = ccall((:EVP_sha512_256, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha3_224() = ccall((:EVP_sha3_224, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha3_256() = ccall((:EVP_sha3_256, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha3_384() = ccall((:EVP_sha3_384, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sha3_512() = ccall((:EVP_sha3_512, libssl), Ptr{Cvoid}, ()) |
|||
EVP_shake128() = ccall((:EVP_shake128, libssl), Ptr{Cvoid}, ()) |
|||
EVP_shake256() = ccall((:EVP_shake256, libssl), Ptr{Cvoid}, ()) |
|||
EVP_mdc2() = ccall((:EVP_mdc2, libssl), Ptr{Cvoid}, ()) |
|||
EVP_ripemd160() = ccall((:EVP_ripemd160, libssl), Ptr{Cvoid}, ()) |
|||
EVP_whirlpool() = ccall((:EVP_whirlpool, libssl), Ptr{Cvoid}, ()) |
|||
EVP_sm3() = ccall((:EVP_sm3, libssl), Ptr{Cvoid}, ()) |
@ -0,0 +1,129 @@ |
|||
|
|||
function queue_encrypted_bytes(client, buf) |
|||
# println("Adding $(length(buf)) for a total of $(length(buf) + length(client.write_buf))") |
|||
append!(client.write_buf, buf) |
|||
end |
|||
|
|||
function do_ssl_handshake(client) |
|||
n = SSL_handshake(client) |
|||
status = ssl_status(client, n) |
|||
if status in (SSL_WANT_READ, SSL_WANT_WRITE) |
|||
while true |
|||
(n, buf) = bio_read(client) |
|||
# println("bio_read: $(n)") |
|||
if n > 0 |
|||
queue_encrypted_bytes(client, buf) |
|||
elseif !bio_should_retry(client.wbio) |
|||
return -1 |
|||
end |
|||
if n <= 0 |
|||
break |
|||
end |
|||
end |
|||
end |
|||
# println("End of ssl handshake") |
|||
return status |
|||
end |
|||
|
|||
function send_unencrypted_bytes(client, buf) |
|||
append!(client.encrypt_buf, buf) |
|||
end |
|||
|
|||
function do_encrypt(client) |
|||
ssl_init_finished(client) || return 0 |
|||
|
|||
while length(client.encrypt_buf) > 0 |
|||
n = ssl_write(client, client.encrypt_buf) |
|||
status = ccall((:SSL_get_error, libssl), SSL_ERRORS, (Ptr{Cvoid}, Cint), |
|||
client.ssl, n) |
|||
if n > 0 |
|||
client.encrypt_buf = client.encrypt_buf[(n+1):end] |
|||
while true |
|||
(n, buf) = bio_read(client) |
|||
if n > 0 |
|||
queue_encrypted_bytes(client, buf) |
|||
elseif !bio_should_retry(client.wbio) |
|||
return -1 |
|||
end |
|||
n > 0 || break |
|||
end |
|||
end |
|||
if status in (SSL_ERROR, SSL_SYSCALL) |
|||
@warn "SSL ERROR" |
|||
return -1 |
|||
end |
|||
if n == 0 |
|||
break |
|||
end |
|||
end |
|||
return 0 |
|||
end |
|||
|
|||
|
|||
function on_read_cb(client, buffer) |
|||
idx = 1 |
|||
len = length(buffer) |
|||
while len > 0 |
|||
n = bio_write!(client, buffer[idx:end]) |
|||
if n <= 0 |
|||
return -1 |
|||
end |
|||
idx += n |
|||
len -= n |
|||
|
|||
if ! ssl_init_finished(client) |
|||
# println("SSL not finished yet") |
|||
if (do_ssl_handshake(client) == -1) |
|||
return -1 |
|||
end |
|||
if !ssl_init_finished(client) |
|||
return 0 |
|||
end |
|||
end |
|||
# println("NOW IT FINISHED\n") |
|||
while true |
|||
(n, buf) = read(client) |
|||
if n > 0 |
|||
client.io_on_read(buf[1:n]) |
|||
else |
|||
break |
|||
end |
|||
end |
|||
status = ssl_status(client, n) |
|||
if status in (SSL_WANT_READ, SSL_WANT_WRITE) |
|||
while true |
|||
(n, buf) = bio_read(client) |
|||
if n > 0 |
|||
queue_encrypted_bytes(client, buf) |
|||
elseif !bio_should_retry(client.wbio) |
|||
return -1 |
|||
end |
|||
n < 0 && break |
|||
end |
|||
end |
|||
if status in (SSL_ERROR, SSL_SYSCALL) |
|||
return -1 |
|||
end |
|||
end |
|||
return 0 |
|||
end |
|||
|
|||
function do_sock_read(client::SSLClient) |
|||
buf = readavailable(client.sock) |
|||
if length(buf) > 0 |
|||
return on_read_cb(client, buf) |
|||
else |
|||
return -1 |
|||
end |
|||
end |
|||
|
|||
function do_sock_write(client::SSLClient) |
|||
n = write(client.sock, client.write_buf) |
|||
# println("Written to the client: $n") |
|||
if n > 0 |
|||
client.write_buf = client.write_buf[(n+1):end] |
|||
return 0 |
|||
else |
|||
return -1 |
|||
end |
|||
end |
@ -0,0 +1,34 @@ |
|||
ssl_version(client) = |
|||
ccall((:SSL_client_version, libssl), SSL_VERSION, (Ptr{Cvoid}, ), client.ssl) |
|||
|
|||
SSL_handshake(client::SSLClient) = |
|||
ccall((:SSL_do_handshake, libssl), Cint, (Ptr{Cvoid},), client.ssl) |
|||
|
|||
SSL_new(ctx::SSLContext) = |
|||
ccall((:SSL_new, libssl), Ptr{Cvoid}, (Ptr{Cvoid},), ctx.ptr) |
|||
|
|||
SSL_accept_state(client::SSLClient) = |
|||
ccall((:SSL_set_accept_state, libssl), Ptr{Cvoid}, (Ptr{Cvoid},), client.ssl) |
|||
|
|||
ssl_init_finished(client::SSLClient) = |
|||
ccall((:SSL_is_init_finished, libssl), Bool, (Ptr{Cvoid},), client.ssl) |
|||
|
|||
accept(client::SSLClient) = |
|||
ccall((:SSL_accept, libssl), Cint, (Ptr{Cvoid}, ), client.ssl) |
|||
|
|||
ssl_status(client::SSLClient, n) = |
|||
ccall((:SSL_get_error, libssl), SSL_ERRORS, (Ptr{Cvoid}, Cint), client.ssl, n) |
|||
|
|||
ssl_read!(client::SSLClient, buf::Vector{UInt8}) = |
|||
ccall((:SSL_read, libssl), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cint), |
|||
client.ssl, buf, length(buf)) |
|||
|
|||
ssl_write(client::SSLClient, buf::Vector{UInt8}) = |
|||
ccall((:SSL_write, libssl), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cint), |
|||
client.ssl, buf, length(buf)) |
|||
|
|||
shutdown(client::SSLClient) = |
|||
ccall((:SSL_shutdown, libssl), Cint, (Ptr{Cvoid},), client.ssl) |
|||
|
|||
free(client::SSLClient) = |
|||
ccall((:SSL_free, libssl), Cint, (Ptr{Cvoid},), client.ssl) |
@ -0,0 +1,37 @@ |
|||
mutable struct SSLContext <: IO |
|||
data |
|||
ptr::Ptr{Cvoid} |
|||
|
|||
function SSLContext() |
|||
ssl_context = new() |
|||
method = ccall((:TLS_server_method, libssl), Ptr{Cvoid}, ()) |
|||
ssl_context.ptr = ccall((:SSL_CTX_new, libssl), Ptr{Cvoid}, (Ptr{Cvoid},), method) |
|||
# if ssl_context.ptr == 0 |
|||
# end |
|||
ssl_context |
|||
end |
|||
end |
|||
|
|||
mutable struct SSLClient{T} |
|||
rbio::Ptr{Cvoid} |
|||
wbio::Ptr{Cvoid} |
|||
context::SSLContext |
|||
ssl::Ptr{Cvoid} |
|||
io_on_read |
|||
sock::T |
|||
write_buf::Vector{UInt8} |
|||
encrypt_buf::Vector{UInt8} |
|||
|
|||
function SSLClient(ctx::SSLContext, io::T) where T |
|||
client = new{T}() |
|||
client.context = ctx |
|||
client.ssl = SSL_new(ctx) |
|||
client.io_on_read = (data) -> nothing |
|||
SSL_accept_state(client) |
|||
set_bio!(client, bio_new(), bio_new()) |
|||
client.write_buf = UInt8[] |
|||
client.encrypt_buf = UInt8[] |
|||
client.sock = io |
|||
client |
|||
end |
|||
end |
Loading…
Reference in new issue