Olm.jl/src/wrapper.jl

233 lines
8.5 KiB
Julia

"The size of an account object in bytes"
account_size() = ccall((:olm_account_size, libolm), Csize_t, ())
"""Initialise an account object using the supplied memory
The supplied memory must be at least olm_account_size() bytes"""
function account()
memory = allocate(account_size())
(ccall((:olm_account, libolm), Ptr{Cvoid}, (Ptr{Cvoid},), memory), memory)
end
"The number of random bytes needed to create an account."
create_account_random_length(a::OlmAccount) =
ccall((:olm_create_account_random_length, libolm), Csize_t,
(Ptr{Cvoid},), a.ptr)
"""Creates a new account. Returns olm_error() on failure. If there weren't
enough random bytes then olm_account_last_error() will be
NOT_ENOUGH_RANDOM"""
function create(a::OlmAccount)
random_length = create_account_random_length(a)
random = allocate(random_length)
res = ccall((:olm_create_account, libolm), Csize_t,
(Ptr{Cvoid}, Ptr{Cvoid}, Csize_t),
a.ptr, random, random_length)
return res != error
end
function OlmAccount()
a = OlmAccount(account()...)
create(a)
a
end
"""Loads an account from a pickled base64 string. Decrypts the account using the
supplied key. Returns olm_error() on failure. If the key doesn't match the one
used to encrypt the account then olm_account_last_error() will be
"BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
olm_account_last_error() will be "INVALID_BASE64". The input pickled buffer is
destroyed"""
function unpickle!(a::OlmAccount, pickle::Vector{UInt8}, passphrase::Vector{UInt8})
memlength = pickle_length(a)
res = ccall((:olm_unpickle_account, libolm), Csize_t,
(Ptr{Cvoid},
Ptr{Cvoid}, Csize_t,
Ptr{Cvoid}, Csize_t,),
a.ptr,
passphrase, length(passphrase),
pickle, length(pickle))
# If passphrase is empty, pickle is not encrypted, delete it.
# Else, deleting the key is fine
erase!(length(passphrase) == 0 ? passphrase : pickle, func = rand)
if res == error
throw(last_error(a))
else
a
end
end
"""Initialize a pickled account. Note htat passphrase is cleared after use.
"""
function OlmAccount(pickle::Vector{UInt8}, passphrase::Vector{UInt8})
a = OlmAccount(account()...)
unpickle!(a, pickle, passphrase)
end
OlmAccount(pickle::Vector{UInt8}) = OlmAccount(pickle, Char[])
function OlmAccount(pickle::Vector{UInt8}, b::Base.SecretBuffer)
res = OlmAccount(pickle, b.data)
Base.shred!(b)
res
end
"""A null terminated string describing the most recent error to happen to an
account"""
last_error(a::OlmAccount) =
ccall((:olm_account_last_error, libolm), Cstring, (Ptr{Cvoid},), a.ptr) |>
unsafe_string
"""A null terminated string describing the most recent error to happen to a
session"""
last_error(s::OlmSession) =
ccall((:olm_session_last_error, libolm), Cstring, (Ptr{Cvoid},), s.ptr) |>
unsafe_string
"Returns the number of bytes needed to store an account"
pickle_length(a::OlmAccount) =
ccall((:olm_pickle_account_length, libolm), Csize_t, (Ptr{Cvoid},), a.ptr)
"Returns the number of bytes needed to store a session"
pickle_length(s::OlmSession) =
ccall((:olm_pickle_session_length, libolm), Csize_t, (Ptr{Cvoid},), s.ptr)
"""Stores an account as a base64 string. Encrypts the account using the
supplied key. Returns the length of the pickled account on success.
Returns olm_error() on failure. If the pickle output buffer
is smaller than olm_pickle_account_length() then olm_account_last_error()
will be "OUTPUT_BUFFER_TOO_SMALL"."""
function pickle!(a::OlmAccount, passphrase::Vector{UInt8})
memlength = pickle_length(a)
memory = allocate(memlength)
res = ccall((:olm_pickle_account, libolm), Csize_t,
(Ptr{Cvoid},
Ptr{Cvoid}, Csize_t,
Ptr{Cvoid}, Csize_t,),
a.ptr,
passphrase, length(passphrase),
memory, memlength)
# zero(Char) does not extists, but rand does
erase!(passphrase, func = rand)
# Check results
if res == memlength
memory
else
# TODO: Custom exceptions
throw(last_error(a))
end
end
pickle!(a::OlmAccount) = pickle!(a, UInt8[])
function pickle!(a::OlmAccount, s::Base.SecretBuffer)
res = pickle!(a, s.data)
Base.shred!(s)
res
end
# Base.getpass("Account encryption key")
"The size of the output buffer needed to hold the identity keys"
identity_keys_length(a::OlmAccount) =
ccall((:olm_account_identity_keys_length, libolm), Csize_t,
(Ptr{Cvoid},), a.ptr)
"The largest number of one time keys this account can store."
max_number_of_one_time_keys() =
ccall((:olm_account_max_number_of_one_time_keys, libolm), Csize_t, ())
"""The number of random bytes needed to generate a given number of new one time
keys."""
function generate_one_time_keys_random_length(a::OlmAccount, count::Integer)
ccall((:olm_account_generate_one_time_keys_random_length, libolm), Csize_t,
(Ptr{Cvoid}, Csize_t),
a.ptr, count)
end
"""Marks the current set of one time keys as being published."""
mark_keys_as_published(a::OlmAccount) =
ccall((:olm_account_mark_keys_as_published, libolm), Csize_t,
(Ptr{Cvoid},), a.ptr)
"The length of an ed25519 signature encoded as base64."
signature_length(a::OlmAccount) =
ccall((:olm_account_signature_length, libolm), Csize_t,
(Ptr{Cvoid},), a.ptr)
"The size of the output buffer needed to hold the one time keys"
one_time_keys_length(a::OlmAccount) =
ccall((:olm_account_one_time_keys_length, libolm), Csize_t,
(Ptr{Cvoid},), a.ptr)
"""Writes the public parts of the identity keys for the account into the
identity_keys output buffer. Returns olm_error() on failure. If the
identity_keys buffer was too small then olm_account_last_error() will be
OUTPUT_BUFFER_TOO_SMALL."""
function identity_keys(a::OlmAccount)
identity_key_length = identity_keys_length(a)
identity_keys = allocate(identity_key_length)
res = ccall((:olm_account_identity_keys, libolm), Csize_t,
(Ptr{Cvoid}, Ptr{Cvoid}, Csize_t),
a.ptr, identity_keys, identity_key_length)
identity_keys |> String
end
"""Signs a message with the ed25519 key for this account. Returns olm_error() on
failure. If the signature buffer was too small then olm_account_last_error()
will be OUTPUT_BUFFER_TOO_SMALL"""
function sign!(a::OlmAccount, message)
siglength = signature_length(a)
output = allocate(siglength)
ccall((:olm_account_sign, libolm), Csize_t,
(Ptr{Cvoid},
Ptr{Cvoid}, Csize_t,
Ptr{Cvoid}, Csize_t),
a.ptr,
message, length(message),
output, siglength)
# zero(Char) does not extists, but rand does
erase!(message, func = rand)
String(deepcopy(output))
end
"""Generates a number of new one time keys. If the total number of keys stored
by this account exceeds max_number_of_one_time_keys() then the old keys are
discarded. Returns olm_error() on error. If the number of random bytes is
too small then olm_account_last_error() will be NOT_ENOUGH_RANDOM."""
function generate_one_time_keys(a::OlmAccount, count::Integer)
0 < count <= max_number_of_one_time_keys() ||
throw("Error: invalid number of keys")
len = generate_one_time_keys_random_length(a, count)
random = rallocate(len)
res = ccall((:olm_account_generate_one_time_keys, libolm), Csize_t,
(Ptr{Cvoid}, Csize_t, Ptr{Cvoid}, Csize_t),
a.ptr, count, random, len)
erase!(random)
Int(res)
end
"""Writes the public parts of the unpublished one time keys for the account into
the one_time_keys output buffer.
<p>
The returned data is a JSON-formatted object with the single property
<tt>curve25519</tt>, which is itself an object mapping key id to
base64-encoded Curve25519 key. For example:
<pre>
{
curve25519: {
"AAAAAA": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
"AAAAAB": "LRvjo46L1X2vx69sS9QNFD29HWulxrmW11Up5AfAjgU"
}
}
</pre>
Returns olm_error() on failure.
<p>
If the one_time_keys buffer was too small then olm_account_last_error()
will be "OUTPUT_BUFFER_TOO_SMALL"."""
function one_time_keys(a::OlmAccount)
one_time_keys_len = one_time_keys_length(a)
one_time_keys = allocate(one_time_keys_len)
res = ccall((:olm_account_one_time_keys, libolm), Csize_t,
(Ptr{Cvoid}, Ptr{Cvoid}, Csize_t),
a.ptr, one_time_keys, one_time_keys_len)
if res == error
throw(last_error(a))
end
JSON.parse(String(deepcopy(one_time_keys)))
end