init
This commit is contained in:
commit
fff6f74456
|
@ -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