Skip to content

About

Get your vanity SSH ed25519 keypair

Resources

Stars

11 stars

Watchers

1 watching

Forks

Latest commit

 

History

7 Commits

Folders and files

Repository files navigation

vanity-keygen

Finds a vanity SSH ed25519 keypair on your GPU, using OpenCL. If you want your public key to contain something like pCaRR1er, run with:

$ vanity-keygen '[pP][cC][aA][rR][rR][iI1][eE][rR]'

and wait for the program to terminate, depending on your luck after tens of billions (>1e10) of generations in this instance, fewer than a hundred millions (<1e8) for [Pp]ierre, millions (1e7) for love$, fewer than a million (<1e6) for love.

Assuming you saved the private key in vanity, remember to set a passphrase with:

$ ssh-keygen -p -f vanity

Alternatively, use the comment (third) field in your .pub and authorized_keys. But where's the fun in that?

Building and running

With Nix (flakes enabled):

$ nix build          # binary in ./result/bin/vanity-keygen
$ nix develop        # dev shell: cargo, OpenCL headers/ICD loader, pocl (CPU OpenCL)
$ cargo build --release

At runtime the program needs an OpenCL ICD for your device:

  • NVIDIA: the proprietary driver (on NixOS, hardware.nvidia provides /run/opengl-driver/etc/OpenCL/vendors/nvidia.icd).
  • AMD: ROCm (rocmPackages.clr) or rusticl (Mesa).
  • Intel: intel-compute-runtime or rusticl (Mesa).
  • CPU: pocl (included in the dev shell; much slower than any GPU).

Check with clinfo. Use -device N to pick a device (default: first GPU-type device), -batch N for candidates per kernel launch (default: 1048576). On servers, tmux is your friend.

How it works

Each OpenCL work item derives a batch of candidate keypairs from scratch: SHA-512 of the seed, clamping, ed25519 scalar multiplication against the basepoint (ref10-style field arithmetic mod 2^255-19, fixed-window precomputed table with mixed twisted-Edwards additions, one field inversion shared per batch), and base64 encoding of the SSH blob — all in kernels/vanity.cl.

A GPU can't run an arbitrary regex, so the host compiles your pattern into a necessary-condition prefilter: a short sequence of per-position bitmasks over the base64 alphabet, matched on-device against the candidate authorized_keys lines. Only prefilter survivors are read back, re-derived with ed25519-dalek, and verified with the real regex, so a kernel filter bug can cost speed, never correctness.

Consequently, patterns must be a concatenation of literals and character classes over the base64 alphabet ([A-Za-z0-9+/]), optionally anchored with ^ and/or $. That covers typical vanity patterns (love$, pCaRR1er, [pP][cC][aA][rR][rR][iI1][eE][rR]). Alternation of multi-character sequences, repetitions, ., negated classes, etc. are rejected with an error. Matching happens over the base64 pubkey portion of the authorized_keys line.

Performance

The heavy lifting is the ed25519 arithmetic, and it shows in the numbers:

  • NVIDIA RTX 4090: ~300M keypairs/s.
  • pocl on a 32-core Ryzen 7950X3D: ~3M keypairs/s.

The Go version this was ported from managed ~250k keypairs/s on the same 32 cores. Use a real GPU.

The speed comes from classic tricks, all in the kernel: field arithmetic in radix 2^51 (5×64-bit limbs, 128-bit products via mul_hi), a fixed-window basepoint table generated on-device at startup (16 signed 16-bit windows, 32769 precomputed points per window in the (y+x, y-x, 2d·xy) format, cutting scalar multiplication to 16 lookups and mixed additions), addition-chain inversion, and Montgomery's batch inversion shared across the 32 keys each work item derives.

Testing

cargo test runs unit tests for the encodings and the pattern compiler, and — when an OpenCL device is available (pocl from the dev shell works) — integration tests comparing the kernel against ed25519-dalek/sha2 (pubkeys, scalars, scalar multiplications incl. edge cases, and the matcher).

Security

This is reminiscent of bitcoin mining. Finding a SHA-256 hash with a lot of zeroes through trial and error doesn't make it any more prone to collisions. We are similarly using brute force to find a public key of a particular shape, which shouldn't make the private key any more discoverable nor its security any weaker.

Do worry about a potential capture of the program's output, and cleartext storage of the private key.

As such, don't run this on a multi-user system for a personal keypair.

At the risk of repeating myself, do not use persistent storage that's not encrypted or physically secured throughout its lifetime to buffer the program's output or store the plaintext private key (however temporarily).

Of course, don't blindly trust random strangers on the Internet. The interesting parts are small and should make for a quick read: kernels/vanity.cl and src/. Cargo.lock pins every dependency hash for inspection.

Not much else for me to say about your Rust standard library, toolchain, OpenCL runtime, OS, and hardware. Open is better. Security is hard.

Example output

$ vanity-keygen 'zz$'
2026/07/18 14:54:41Z Looking for a public key matching zz$ on NVIDIA GeForce RTX 4090
2026/07/18 14:54:41Z Public key:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICYa3IvvAmleOZndPRGUmZGHM6gE3G1LtMd+0zeB45zz
2026/07/18 14:54:41Z Private key:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAmGtyL7wJpXjmZ3T0RlJmRhzOoBNxtS7THftM3geOc8wAAAIhnGzwOZxs8
DgAAAAtzc2gtZWQyNTUxOQAAACAmGtyL7wJpXjmZ3T0RlJmRhzOoBNxtS7THftM3geOc8w
AAAEAA+T0oFoYH7ZALcy6O85/8jIYSFBvWOmBtDOjlSU/D6yYa3IvvAmleOZndPRGUmZGH
M6gE3G1LtMd+0zeB45zzAAAAAAECAwQF
-----END OPENSSH PRIVATE KEY-----

About

Get your vanity SSH ed25519 keypair

Resources

Stars

11 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages