master
nixo 2020-10-23 19:34:36 +02:00
commit fff6f74456
10 changed files with 400 additions and 0 deletions

4
Project.toml Normal file
View File

@ -0,0 +1,4 @@
name = "OpenSSL"
uuid = "13378672-9967-48ef-8ae3-b2866786e7a0"
authors = ["nixo <nicolo@nixo.xyz>"]
version = "0.1.0"

43
src/OpenSSL.jl Normal file
View File

@ -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

26
src/bio.jl Normal file
View File

@ -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)

46
src/certs.jl Normal file
View File

@ -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), ":")

39
src/consts.jl Normal file
View File

@ -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

19
src/context.jl Normal file
View File

@ -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

23
src/evp_md.jl Normal file
View File

@ -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}, ())

129
src/flow_control.jl Normal file
View File

@ -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

34
src/ssl.jl Normal file
View File

@ -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)

37
src/types.jl Normal file
View File

@ -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