First version. Almost everything is implemented. Missing library

Right now I'm hadrcoding guix path for the library. Should find out how to use
BinaryBuilder
master
nixo 2020-01-06 14:20:08 +01:00
commit e1b004dbc4
5 changed files with 339 additions and 0 deletions

59
Manifest.toml Normal file
View File

@ -0,0 +1,59 @@
# This file is machine-generated - editing it directly is not advised
[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "b34d7cef7b337321e97d22242c3c2b91f476748e"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.0"
[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
[[Parsers]]
deps = ["Dates", "Test"]
git-tree-sha1 = "0139ba59ce9bc680e2925aec5b7db79065d60556"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "0.3.10"
[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
[[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
[[Test]]
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

8
Project.toml Normal file
View File

@ -0,0 +1,8 @@
name = "Olm"
uuid = "35e2252f-68e6-415a-955a-e84ed199fea9"
authors = ["nixo <nicolo@nixo.xyz>"]
version = "0.1.0"
[deps]
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

39
src/Olm.jl Normal file
View File

@ -0,0 +1,39 @@
module Olm # include("olm.jl")
using Random
using JSON
const libolm = "/gnu/store/ks2szjc82r1wqaxdq4vsy6sq9plwd4fs-libolm-3.1.4/lib/libolm.so"
const error = ccall((:olm_error, libolm), Csize_t, ())
struct OlmAccount
ptr::Ptr{Cvoid}
# Should I store it?
# memory::Vector{UInt}
end
struct OlmSession
ptr::Ptr{Cvoid}
end
struct OlmUtility
ptr::Ptr{Cvoid}
end
include("memory.jl")
include("wrapper.jl")
# # Save/Load account!
# a = OlmAccount()
# generate_one_time_keys(a, 10)
# # deleted after use
# enc_key = "pass" |> collect
# dec_key = deepcopy(enc_key)
# enc_key = dec_key = Char[]
# p = pickle!(a, enc_key)
# write("account.bin", p)
# d = read("account.bin", String) |> collect .|> UInt8
# @assert d == p
# n = OlmAccount(d, dec_key)
# o = OlmAccount(p, dec_key)
end # Olm

13
src/memory.jl Normal file
View File

@ -0,0 +1,13 @@
"Allocate an UInt8 array of length size, all zeros"
allocate(size) = zeros(UInt8, size)
const SRND = Random.RandomDevice()
"Allocate a criptographycally secure random UInt8 array of length size"
rallocate(size) = rand(SRND, UInt8, size)
"""Replace all elements of an array with zeros. A custom function accepting
"type" may be passed as FUNC argument used instead of ZERO"""
function erase!(array::Array{T}; func = zero) where T
for i in 1:length(array)
array[i] = func(T)
end
end

220
src/wrapper.jl Normal file
View File

@ -0,0 +1,220 @@
"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)
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{Char}, passphrase::Vector{Char})
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),
collect(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{Char}, passphrase::Vector{Char})
a = OlmAccount(account())
unpickle!(a, pickle, passphrase)
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{Char})
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
throw(last_error(a))
end
end
pickle!(a::OlmAccount) = pickle!(a, Char[])
# Base.getpass("Account encryption key")
"The size of the output buffer needed to hold the identity keys"
account_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)
erase!(message)
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