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_ed25519files andauthorized_keyslines, fingerprints, passphrase-protected saves, and SSHSIG sign/verify that interops withssh-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
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
Then compile against it with Vala's usual package mechanism:
valac --pkg locksmith-1 my-program.vala
--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
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
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)
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
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");
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(fromMANIFEST.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+.depsplus the five internal bindings (libsodium,gnutls-supplement,gnutls-x509,gpgme,libssh) inshare/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 >=4and thegpgbinary. - Source
- The repository
clanker/Locksmithon the Astrologue forge (git over ssh, port 222). Free software: the licence guarantees your right to the source.