Security · Keys, seals and signatures

Locksmith

Developer-friendly cryptography for Vala, in objects rather than byte arrays. It wraps the battle-tested libraries you already have — libsodium, GnuTLS, GPGME and libssh — and implements no cryptography itself.

Version
0.1.0
Licence
LGPL-3.0-or-later
Package
usm install locksmith
Language
Vala

Overview

Why it exists

Locksmith is an all-purpose cryptography library for Vala. It wraps the crypto libraries a machine already carries — libsodium, GnuTLS, GPGME and libssh — behind simple, object-oriented APIs, so an application gets secure cryptography without touching a byte array unless it wants to.

The pledge is the design: Locksmith implements no cryptography itself. Every primitive comes from those four libraries; Locksmith contributes the object model, the wire formats and the defaults. Nothing new can be wrong in a way its underlying libraries are not, and an audit of the cryptography is an audit of software that has already been audited.

Its wire formats are the ones the rest of the Astrologue stack already uses: Ed25519 attached signatures are signature||message (USM's format), seals use the X25519 ephemeral-header form, and keys are their raw bytes — so USM and Statum signed slots can move onto Locksmith without changing a byte on disk. A compatibility test suite proves both directions by compiling the original hand-rolled bindings alongside this library.

Features

What it hands you

  • Signing. Ed25519 keypairs and public keys; attached (signature||message, the USM wire format) and detached signatures.
  • Sealing. X25519 anonymous seals and authenticated two-party boxes.
  • Symmetric. Secretbox keys and ciphers; the nonce travels inside the ciphertext, so it cannot be forgotten.
  • Hashing. One-call and streaming digests (SHA-1/2/3, SHAKE, MD5, BLAKE2b), HMAC, keyed BLAKE2b, PBKDF2, and argon2id password hashing — the only API pointed at passwords.
  • Certificates. X.509 via GnuTLS: RSA/ECDSA/Ed25519 keypairs, DistinguishedNames, certificates, PKCS#10 requests, and Authority objects that issue and verify.
  • Ssh. OpenSSH keys via libssh: generate, load id_ed25519 files and authorized_keys lines, fingerprints, passphrase-protected saves, and SSHSIG sign/verify that interops with ssh-keygen -Y.
  • Pgp. OpenPGP via GPGME: hermetic keyrings, key generation, sign/verify, encrypt/decrypt, import/export, armored output.
  • WebPush. RFC 8291 message encryption and RFC 8292 VAPID headers via GnuTLS: browser subscription JSON in, POST-ready body and headers out.

Installation

Getting the library

With USM on your machine, one command acquires, builds and installs the library with its header, Vala binding and introspection:

usm install locksmith
USM resolves invercargill-1 and invercargill-json itself.

Building by hand needs the four crypto libraries plus the stack's foundation — on Fedora:

sudo dnf install meson ninja-build vala libsodium-devel gnutls-devel \
    gpgme-devel libssh-devel libgpg-error-devel json-glib-devel \
    invercargill-devel

git clone <the Locksmith repository>
cd Locksmith
meson setup build && ninja -C build && meson test -C build
The Fedora line and the build commands are the README's own.

Then compile against it with Vala's usual package mechanism:

valac --pkg locksmith-1 my-program.vala
The package ships its five internal bindings alongside, so --pkg locksmith-1 resolves everything.

Usage & documentation

Quick start, from the README

Signing, the way USM signs a package listing — but with objects:

var pair = new Locksmith.Signing.KeyPair.generate();
var signed = pair.sign(new ByteBuffer.from_byte_array(listing.data));
var recovered = pair.public_key.verify(signed);   // null when invalid
Verbatim from the README's Quickstart. Verification returns null rather than throwing — invalid input is a normal outcome.

Sealing (anonymous: only the recipient can open) and symmetric encryption (the nonce travels inside):

var recipient = new Locksmith.Sealing.KeyPair.generate();
var sealed = recipient.public_key.seal(message);
var opened = recipient.unseal(sealed);

var key = new Locksmith.Symmetric.Key.generate();
var cipher = new Locksmith.Symmetric.Cipher();
var stored = cipher.encrypt(key, secret);
var back = cipher.decrypt(key, stored);   // throws CIPHER when tampered
From the README. Tamper detection is proven by the test suite, not just round-trips.

A password is never hashed with a digest — it goes to argon2id, and verification is a method on the same object:

var phc = new Locksmith.Hashing.PasswordHash().hash(password);
// verify later: hasher.verify(phc, attempt)
Digested data is labelled integrity-only, never password-safe.

Certificates and SSH keys

An Authority issues and verifies leaves; an SSH key saves and signs in OpenSSH's own formats:

var root = Locksmith.Certificates.Authority.generate(
    Locksmith.Certificates.KeyType.ED25519, subject, 3650);
var leaf = root.issue_for_public_key(leaf_subject, server.public_key,
    365, sans);
string reason;
leaf.verify(root.certificate, out reason);

var key = new Locksmith.Ssh.Key.generate(Locksmith.Ssh.KeyType.ED25519,
    "me@host");
print("%s\n", key.public_string);   // authorized_keys line
var signature = key.sign(file_contents);   // SSHSIG
From the README's Quickstart — the SSHSIG output verifies with ssh-keygen -Y.

OpenPGP and WebPush

var ctx = new Locksmith.Pgp.Context.create();
ctx.home_directory = keyring_dir;
var key = ctx.generate_key("Alice <alice@example.org>");
var signed = ctx.sign(document);

var subscription = new Locksmith.WebPush.Subscription.from_json(json);
var vapid = new Locksmith.WebPush.ApplicationServerKeys.generate();
var message = Locksmith.WebPush.encrypt(subscription, payload);
var headers = Locksmith.WebPush.Vapid.headers(vapid,
    "https://push.example.net", "mailto:admin@example.org");
A hermetic GPGME keyring for the first; the application-server half of RFC 8291 + 8292 for the second — POST-ready output.

The API philosophy

Every key, nonce, signature, digest and ciphertext is an Invercargill.BinaryData (concretely ByteBuffer), so to_base64(), to_hex() and equality come free. What a constructor accepts, a method gives back: from_base64/from_hex/from_bytes against to_base64/to_hex/to_der/to_pem. Malformed input throws a Locksmith.Error code; verification — where invalid input is a normal outcome — returns bool or null, so control flow reads like intent.

Runnable versions of every snippet above live in examples/ — one standalone program per subsystem (signing_example.vala, sealing_example.vala, symmetric_example.vala, hashing_example.vala, certificates_example.vala, ssh_example.vala, pgp_example.vala, webpush_example.vala), each smoke-tested by meson test. The library suite itself runs round-trips, tamper detection and RFC vectors through both meson test and USM's test exec, so what is documented here is what is exercised.

Package & source

The ledger

Version
0.1.0 (from MANIFEST.usm)
Licence
LGPL-3.0-or-later — free to run, study, share and improve; the LGPL keeps it free to link from any program. The vapi/ libsodium binding is derived from Billy Barrow's ISC-licensed binding, credited in-file.
Package
usm install locksmith
Provides
liblocksmith-1.so, locksmith.h, locksmith-1.vapi + .deps plus the five internal bindings (libsodium, gnutls-supplement, gnutls-x509, gpgme, libssh) in share/vala/vapi, locksmith-1.gir, locksmith-1.typelib, locksmith-1.pc
Needs at runtime
GLib, GObject, GIO, libinvercargill-1.so, libinvercargill-json.so, libsodium.so >=26, libgnutls.so >=30, libgpgme.so >=45, libssh.so >=4 and the gpg binary.
Source
The repository clanker/Locksmith on the Astrologue forge (git over ssh, port 222). Free software: the licence guarantees your right to the source.