Skip to content

Commit aead79d

Browse files
committed
Initial release — sear CLI for SSH signing and age encryption with YubiKey
Sign, verify, encrypt and decrypt files using ssh-keygen (SSHSIG) and age. Includes 106-test integration suite and GitHub Actions CI/release workflows.
1 parent 08de630 commit aead79d

File tree

7 files changed

+2351
-0
lines changed

7 files changed

+2351
-0
lines changed

.github/workflows/release.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
strategy:
15+
matrix:
16+
include:
17+
# Linux
18+
- goos: linux
19+
goarch: amd64
20+
- goos: linux
21+
goarch: arm64
22+
- goos: linux
23+
goarch: 386
24+
# macOS
25+
- goos: darwin
26+
goarch: amd64
27+
- goos: darwin
28+
goarch: arm64
29+
# FreeBSD
30+
- goos: freebsd
31+
goarch: amd64
32+
- goos: freebsd
33+
goarch: arm64
34+
# OpenBSD
35+
- goos: openbsd
36+
goarch: amd64
37+
- goos: openbsd
38+
goarch: arm64
39+
# Windows
40+
- goos: windows
41+
goarch: amd64
42+
- goos: windows
43+
goarch: arm64
44+
steps:
45+
- uses: actions/checkout@v4
46+
47+
- uses: actions/setup-go@v5
48+
with:
49+
go-version-file: go.mod
50+
51+
- name: Build
52+
env:
53+
GOOS: ${{ matrix.goos }}
54+
GOARCH: ${{ matrix.goarch }}
55+
run: |
56+
VERSION="${GITHUB_REF_NAME}"
57+
EXT=""
58+
if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi
59+
BINARY="sear-${GOOS}-${GOARCH}${EXT}"
60+
go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" -o "${BINARY}" .
61+
sha256sum "${BINARY}" > "${BINARY}.sha256"
62+
63+
- uses: actions/upload-artifact@v4
64+
with:
65+
name: sear-${{ matrix.goos }}-${{ matrix.goarch }}
66+
path: |
67+
sear-*
68+
!sear-*.sha256
69+
70+
release:
71+
needs: build
72+
runs-on: ubuntu-latest
73+
steps:
74+
- uses: actions/download-artifact@v4
75+
with:
76+
path: artifacts
77+
merge-multiple: true
78+
79+
- name: Generate checksums
80+
run: |
81+
cd artifacts
82+
sha256sum sear-* | grep -v '.sha256' > ../checksums.txt
83+
84+
- uses: softprops/action-gh-release@v2
85+
with:
86+
files: |
87+
artifacts/sear-*
88+
checksums.txt
89+
generate_release_notes: true

.github/workflows/test.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- uses: actions/setup-go@v5
16+
with:
17+
go-version-file: go.mod
18+
19+
- name: Build
20+
run: go build -o sear .
21+
22+
- name: Install dependencies
23+
run: |
24+
sudo apt-get update
25+
sudo apt-get install -y age
26+
27+
- name: Run tests
28+
run: ./test-sear.sh

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
sear
2+
*.age
3+
*.sig
4+
*.dec
5+
.claude/

README.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# sear
2+
3+
Sign, verify, encrypt and decrypt files with SSH keys and age.
4+
5+
`sear` wraps `ssh-keygen` and `age` into a single tool for YubiKey-based
6+
file signing and encryption. SSH signing uses FIDO2 ed25519-sk keys
7+
(touch to sign), age encryption uses PIV via
8+
[age-plugin-yubikey](https://github.com/str4d/age-plugin-yubikey)
9+
(PIN + touch).
10+
11+
## Install
12+
13+
Download a binary from [Releases](../../releases), or build from source:
14+
15+
```
16+
go build -o sear .
17+
```
18+
19+
### Dependencies
20+
21+
| | Required | Optional |
22+
|---|---|---|
23+
| Signing | `ssh-keygen` | |
24+
| Encryption | `age` | `age-plugin-yubikey` (YubiKey PIV) |
25+
| Key management | | `ykman` (YubiKey admin) |
26+
27+
```
28+
# macOS
29+
brew install age age-plugin-yubikey ykman libfido2
30+
31+
# FreeBSD
32+
pkg install age age-plugin-yubikey yubikey-manager libfido2
33+
34+
# Linux
35+
apt install age yubikey-manager libfido2-tools
36+
```
37+
38+
## Usage
39+
40+
```
41+
sear <command> [flags] FILE...
42+
43+
Signing & Verification:
44+
sign Sign files with SSH key (via ssh-agent)
45+
verify Verify file signatures
46+
seal Encrypt + sign in one step
47+
unseal Verify + decrypt in one step
48+
49+
Encryption:
50+
encrypt Encrypt files with age
51+
decrypt Decrypt files with age
52+
53+
Key Management:
54+
keygen Generate ed25519-sk signing key on YubiKey
55+
age-keygen Generate age encryption key on YubiKey
56+
```
57+
58+
Run `sear <command> -h` for per-command help.
59+
60+
### Quick start
61+
62+
```bash
63+
# 1. Create SSH signing key on YubiKey
64+
sear keygen -C mykey
65+
66+
# 2. Sign a file (touch YubiKey)
67+
sear sign document.pdf
68+
69+
# 3. Verify
70+
sear verify document.pdf
71+
72+
# 4. Create age encryption key on YubiKey (one-time)
73+
sear age-keygen -o age-identity.txt
74+
75+
# 5. Encrypt for a recipient
76+
sear encrypt -r age1yubikey1q... secrets.env
77+
78+
# 6. Decrypt (PIN + touch)
79+
sear decrypt -i age-identity.txt secrets.env.age
80+
```
81+
82+
### Seal / Unseal (encrypt + sign in one step)
83+
84+
For CI/CD delivery — encrypt and sign on your machine, verify and decrypt
85+
on the server:
86+
87+
```bash
88+
# Sender
89+
sear seal -r age1... secrets.env
90+
# produces secrets.env.age + secrets.env.age.sig
91+
92+
# Receiver
93+
sear unseal -p deploy.pub -i ci-identity.txt secrets.env.age
94+
# verifies signature, then decrypts
95+
```
96+
97+
### Key naming
98+
99+
The `-C` flag to `sear keygen` sets both the SSH key comment and the
100+
FIDO2 application ID (`ssh:NAME`) on the YubiKey. This lets you match
101+
key files on disk to credentials on the hardware:
102+
103+
```bash
104+
sear keygen -C prod-deploy -f ~/.ssh/deploy_sk
105+
# Comment: prod-deploy
106+
# App ID: ssh:prod-deploy
107+
# Visible: ykman fido credentials list
108+
```
109+
110+
## How it works
111+
112+
- **Signing** uses `ssh-keygen -Y sign/verify` (the SSHSIG protocol).
113+
Signatures are compatible with `ssh-keygen` — you can sign with `sear`
114+
and verify with `ssh-keygen`, or vice versa.
115+
116+
- **Encryption** uses `age` with optional YubiKey PIV support via
117+
`age-plugin-yubikey`.
118+
119+
- **No private keys on disk** for signing. FIDO2 keys live on the
120+
YubiKey; `ssh-keygen` talks to the hardware directly.
121+
122+
## Test
123+
124+
```
125+
go build -o sear . && ./test-sear.sh
126+
```
127+
128+
Runs 106 tests covering signing, verification, encryption, decryption,
129+
attack scenarios (tampered files, wrong keys, replayed signatures,
130+
namespace mismatches, corrupted data), edge cases (binary files, empty
131+
files, spaces in names, 1MB files), and cross-compatibility with
132+
`ssh-keygen`.
133+
134+
## License
135+
136+
GPL-3.0

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/8ff/sear
2+
3+
go 1.26.1

0 commit comments

Comments
 (0)