defmodule GitGud.Packages.Apt.Native do
@moduledoc """
The two things the apt repository needs that OTP cannot do, via the
`native/gitgud_apt` Rustler NIF.
**OpenPGP signatures.** apt requires a signed `Release`, and
`:public_key` speaks PKCS and X.509 — OpenPGP is a different packet
format with its own key material, and nothing in the Erlang
distribution implements it. This is the only format in the registry
that needed a NIF; the rest get by on `:crypto` and `:public_key`.
**xz and zstd.** A `.deb`'s control member is `control.tar.xz` on
anything since squeeze and `control.tar.zst` on Ubuntu, and
`:erl_tar` handles gzip and nothing else. The same encoder produces
the `Packages.xz` a Debian repository is expected to offer.
## Why these backends
`sequoia-openpgp` defaults to Nettle, which would put `libnettle`
and `libgmp` in the runtime image. This project ships k8s manifests;
a NIF that changes the image's package list is a deployment change
wearing a dependency's clothes. The pure-Rust backend needs no
system libraries at all, but sequoia gates it behind
`allow-variable-time-crypto`, and this code holds a private key. The
OpenSSL backend links libssl and libcrypto, which are already in
both images — the builder installs `libssl-dev` for gix and the
runner installs `openssl` for the Erlang runtime — and is
constant-time. So: OpenSSL, and no image change at all.
The compression crates (`lzma-rs`, `ruzstd`) are pure Rust for the
same reason: no new system libraries.
Keys and signatures cross the boundary as armored ASCII, so nothing
on the Elixir side handles OpenPGP packets.
"""
use Rustler, otp_app: :git_gud, crate: "gitgud_apt"
@doc """
Generate a signing certificate for `user_id`. Returns
`{:ok, {public_armored, secret_armored}}` — the public half goes in
a client's `/etc/apt/keyrings`, the secret half is stored encrypted
and never leaves the forge.
"""
@spec generate_key(String.t()) :: {:ok, {String.t(), String.t()}} | {:error, String.t()}
def generate_key(_user_id), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Clearsign `data` — the `InRelease` shape, signature and signed text
in one file, which is what apt prefers.
"""
@spec sign_clearsign(String.t(), String.t()) :: {:ok, String.t()} | {:error, String.t()}
def sign_clearsign(_secret_armored, _data), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Produce a detached armored signature — the `Release.gpg` shape,
which older apt falls back to when `InRelease` is absent.
"""
@spec sign_detached(String.t(), String.t()) :: {:ok, String.t()} | {:error, String.t()}
def sign_detached(_secret_armored, _data), do: :erlang.nif_error(:nif_not_loaded)
@doc """
Verify a detached signature. Not used to serve anything — it lets
tests assert what apt will assert.
"""
@spec verify_detached(String.t(), String.t(), String.t()) ::
{:ok, boolean()} | {:error, String.t()}
def verify_detached(_public_armored, _data, _signature_armored),
do: :erlang.nif_error(:nif_not_loaded)
@doc "Decompress an xz stream."
@spec xz_decompress(binary()) :: {:ok, binary()} | {:error, String.t()}
def xz_decompress(_data), do: :erlang.nif_error(:nif_not_loaded)
@doc "Compress to xz."
@spec xz_compress(binary()) :: {:ok, binary()} | {:error, String.t()}
def xz_compress(_data), do: :erlang.nif_error(:nif_not_loaded)
@doc "Decompress a zstd stream."
@spec zstd_decompress(binary()) :: {:ok, binary()} | {:error, String.t()}
def zstd_decompress(_data), do: :erlang.nif_error(:nif_not_loaded)
end
neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
3.6 KiB · text
History
6bfb936