Skip to content

Commit

Permalink
Swap out Zig with Rust for NIF (#4)
Browse files Browse the repository at this point in the history
* add rustler
* Remove zigler
* add key_info/1
* update ex_doc
  • Loading branch information
silbermm authored Nov 18, 2022
1 parent 8515a14 commit b9492b0
Show file tree
Hide file tree
Showing 21 changed files with 777 additions and 607 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
uses: erlef/setup-beam@v1
with:
elixir-version: '1.13.2' # define the elixir version [required]
otp-version: '24.1' # define the otp version [required]
otp-version: '24.0' # define the otp version [required]

- uses: actions/cache@v2
with:
Expand All @@ -51,7 +51,7 @@ jobs:
run: MIX_ENV=devci mix compile --warnings-as-errors

- name: Compile-time Dependencies
run: MIX_ENV=ci mix xref graph --label compile-connected --fail-above 0
run: MIX_ENV=ci mix xref graph --label compile-connected --fail-above 2

- name: Static Analysis
run: MIX_ENV=devci mix dialyzer --halt-exit-status
Expand Down
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]

## [0.0.7]
### Updated
- Refactor NIF to use Rustler with the [gpgme crate](https://docs.rs/gpgme/latest/gpgme/index.html)
- the GPG.get_public_key/1 function now just returns the fingerprint of the public key in the system

### Added
- key_info function that
- returns the data (fingerprint, uid, email) about a text key passed into the function

## [0.0.6]
### Updated
- Fixed some tests
Expand Down Expand Up @@ -33,5 +42,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Encrypt
- Decrypt

[Unreleased]: https://github.com/silbermm/gpgmex/compare/v0.0.1...HEAD
[Unreleased]: https://github.com/silbermm/gpgmex/compare/v0.0.7...HEAD
[0.0.7]: https://github.com/silbermm/gpgmex/releases/tag/v0.0.7
[0.0.1]: https://github.com/silbermm/gpgmex/releases/tag/v0.0.1
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ Native Elixir bindings for GnuPG.

## Getting Started

> This has only been tested on Linux - It likely won't work for
> Mac OSX or Windows yet.
> This has only been tested on Linux
You'll need:
* a working version of [gpg](https://gnupg.org/) installed
Expand All @@ -28,8 +27,8 @@ Add this to `config.exs` in your app

```elixir
config :gpgmex,
include_dir: ["/usr/include/x86_64-linux-gnu", "/usr/include"],
libs_dir: ["/usr/lib/x86_64-linux-gnu/libgpgme.so"]
gpg_home: "~/.gnupg", # where your gpg home path is
gpg_path: "/usr/bin/gpg" # where your gpg binary lives
```

### Arch based (Arch, Manjaro, etc)
Expand All @@ -46,8 +45,8 @@ Add this to `config.exs` in your app

```elixir
config :gpgmex,
include_dir: ["/usr/include"],
libs_dir: ["/usr/lib/libgpgme.so"]
gpg_home: "~/.gnupg", # where your gpg home path is
gpg_path: "/usr/bin/gpg" # where your gpg binary lives
```

### Finally
Expand All @@ -56,7 +55,7 @@ Add gpgmex to your dependencies
```elixir
defp deps do
[
{:gpgmex, github: "silbermm/gpgmex"}
{:gpgmex, "~> 0.0.7"}
]
end
```
Expand Down
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Config

config :gpgmex,
native_api: GPG.NIF
native_api: GPG.Rust.GPG

import_config "#{config_env()}.exs"
83 changes: 24 additions & 59 deletions lib/gpg.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ defmodule GPG do
```elixir
config :gpgmex,
include_dir: ["/usr/include/x86_64-linux-gnu", "/usr/include"],
lib_dir: ["/usr/lib/x86_64-linux-gnu/libgpgme.so"]
gpg_home: "~/.gnupg", # where your gpg home path is
gpg_path: "/usr/bin/gpg" # where your gpg binary lives
```
### Arch based (Arch, Manjaro, etc)
Expand All @@ -66,8 +66,8 @@ defmodule GPG do
```elixir
config :gpgmex,
include_dir: ["/usr/include"],
lib_dir: ["/usr/lib/libgpgme.so"]
gpg_home: "~/.gnupg", # where your gpg home path is
gpg_path: "/usr/bin/gpg" # where your gpg binary lives
```
## Add to your Dependencies
Expand All @@ -76,7 +76,7 @@ defmodule GPG do
```elixir
defp deps do
[
{:gpgmex, "~> 0.0.1"}
{:gpgmex, "~> 0.0.7"}
]
end
```
Expand Down Expand Up @@ -104,40 +104,26 @@ defmodule GPG do
## Examples
iex> GPG.get_engine_info()
%{filename: "/usr/bin/gpg"}
%{bin: "/usr/bin/gpg", directory: "~/.gnupg"}
"""
@spec get_engine_info() :: map() | :error
def get_engine_info() do
ref = GPG.NativeAPI.engine_info()
filename = GPG.NativeAPI.get_filename(ref)
%{filename: to_string(filename)}
GPG.NativeAPI.engine_info()
catch
_e -> :error
end

@spec create_context() :: reference() | {:error, any()}
defp create_context() do
# must check_version before creating a context
_version = GPG.NativeAPI.check_version()
GPG.NativeAPI.create_context()
catch
e -> {:error, e}
end

@doc """
Get the public key for an email if the public key is on your system
Get the fingerprint of the public key for an email if the public key is on your system
## Examples
iex> GPG.get_public_key("[email protected]")
"4rZSsLVhrs1JCPtRWmUl1F2q2S5+MqBjTCYkS2Rk\\nphuo6u4XQ"
{:ok, "80C8F7AE64E589449FB0A03974DB6708422DD33B"}
"""
@spec get_public_key(binary()) :: binary() | :error
@spec get_public_key(binary()) :: {:ok, binary()} | :error
def get_public_key(email) do
create_context()
|> GPG.NativeAPI.public_key(email)
|> Enum.take_while(&(&1 != 170))
|> to_string()
GPG.NativeAPI.public_key(email)
catch
_e -> :error
end
Expand All @@ -156,20 +142,7 @@ defmodule GPG do
"""
@spec encrypt(String.t(), binary()) :: {:ok, binary()} | {:error, atom()}
def encrypt(email, data) do
create_context()
|> GPG.NativeAPI.encrypt(email, data)
|> then(fn
{:ok, result} ->
data =
result
|> Enum.take_while(&(&1 != 170))
|> to_string()

{:ok, data}

e ->
e
end)
GPG.NativeAPI.encrypt(email, data)
catch
_e -> {:error, :unknown}
end
Expand All @@ -188,20 +161,7 @@ defmodule GPG do
"""
@spec decrypt(binary()) :: {:ok, binary()} | {:error, binary()}
def decrypt(data) do
create_context()
|> GPG.NativeAPI.decrypt(data)
|> then(fn
{:ok, result} ->
data =
result
|> Enum.take_while(&(&1 != 170))
|> to_string()

{:ok, data}

e ->
e
end)
GPG.NativeAPI.decrypt(data)
catch
e -> {:error, to_string(e)}
end
Expand All @@ -219,8 +179,7 @@ defmodule GPG do
"""
@spec generate_key(String.t()) :: :ok | :error
def generate_key(email) do
create_context()
|> GPG.NativeAPI.generate_key(email)
GPG.NativeAPI.generate_key(email)
catch
_e -> :error
end
Expand All @@ -230,8 +189,7 @@ defmodule GPG do
"""
@spec delete_key(binary()) :: number() | :error
def delete_key(email) do
create_context()
|> GPG.NativeAPI.delete_key(email)
GPG.NativeAPI.delete_key(email)
catch
_e -> :error
end
Expand All @@ -241,7 +199,14 @@ defmodule GPG do
"""
@spec import_key(binary()) :: :ok | {:error, binary()}
def import_key(data) do
create_context()
|> GPG.NativeAPI.import_key(data)
GPG.NativeAPI.import_key(data)
end

@doc """
Gets data about a public key
"""
@spec key_info(binary()) :: map() | {:error, binary()}
def key_info(public_key) do
GPG.NativeAPI.key_info(public_key)
end
end
44 changes: 23 additions & 21 deletions lib/gpg_native_api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,46 @@ defmodule GPG.NativeAPI do
@callback engine_info() :: any()

@doc "Return the filename from engine_info"
@callback get_filename(reference()) :: any()
@callback get_filename() :: any()

@doc "Return the homedir from engine_info"
@callback get_homedir(reference()) :: any()

@doc "Creates a reference to the GPG context"
@callback create_context() :: any()
@callback get_homedir() :: any()

@doc "Encrypt some text"
@callback encrypt(reference(), binary(), binary()) :: any()
@callback encrypt(binary(), binary()) :: any()

@doc "Decrypt some encrypted text"
@callback decrypt(reference(), binary()) :: any()
@callback decrypt(binary()) :: any()

@doc "Get your public key"
@callback public_key(reference(), binary()) :: any()
@callback public_key(binary()) :: any()

@doc "Generate a key"
@callback generate_key(reference(), binary()) :: any()
@callback generate_key(binary()) :: any()

@doc "Delete a key"
@callback delete_key(reference(), binary()) :: any()
@callback delete_key(binary()) :: any()

@doc "Import a key"
@callback import_key(reference(), binary()) :: any()
@callback import_key(binary()) :: any()

@doc """
Get information about the passed in public key
"""
@callback key_info(binary()) :: any()

defp impl, do: Application.get_env(:gpgmex, :native_api)
defp impl, do: Application.get_env(:gpgmex, :native_api, GPG.Rust.GPG)

def check_version(), do: impl().check_version()
def check_openpgp_supported(), do: impl().check_openpgp_supported()
def engine_info(), do: impl().engine_info()
def get_filename(ref), do: impl().get_filename(ref)
def get_homedir(ref), do: impl().get_homedir(ref)
def create_context(), do: impl().create_context()
def encrypt(ref, email, text), do: impl().encrypt(ref, email, text)
def decrypt(ref, text), do: impl().decrypt(ref, text)
def public_key(ref, email), do: impl().public_key(ref, email)
def generate_key(ref, email), do: impl().generate_key(ref, email)
def delete_key(ref, email), do: impl().delete_key(ref, email)
def import_key(ref, data), do: impl().import_key(ref, data)
def get_filename(), do: impl().get_filename()
def get_homedir(), do: impl().get_homedir()
def encrypt(email, text), do: impl().encrypt(email, text)
def decrypt(text), do: impl().decrypt(text)
def public_key(email), do: impl().public_key(email)
def generate_key(email), do: impl().generate_key(email)
def delete_key(email), do: impl().delete_key(email)
def import_key(data), do: impl().import_key(data)
def key_info(public_key), do: impl().key_info(public_key)
end
Loading

0 comments on commit b9492b0

Please sign in to comment.