Rename binary jpgp -> jpg
ober
2dcd4a87b8ae01bd8ae384852ed9a18378d73460
--- a/.gitignore +++ b/.gitignore @@ -9,11 +9,13 @@ pgp-native/Cargo.lock *.dll # Binary outputs +jpg-bin jpgp-bin -jpgp-musl -jpgp-macos -jpgp-musl.sha256 -jpgp-macos.sha256 +jpg-musl +jpg-macos +jpg-musl.sha256 +jpg-macos.sha256 +jpg jpgp # Binary build intermediates --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ help: @echo "" @echo "Development:" @echo " make build-native Build pure-Rust crypto backend" - @echo " make run ARGS='version' Run jpgp under the interpreter" + @echo " make run ARGS='version' Run jpg under the interpreter" @echo " make test Run smoke tests" @echo " make test-interop Run bidirectional gpg interop tests" @echo "" @@ -52,7 +52,7 @@ test-interop: build-native JERBOA_HOME=$(JERBOA_HOME) JPGP_REPO=$(CURDIR) SCHEME=$(SCHEME) \ bash test/interop-gpg.sh -BINARY_OUTPUT ?= jpgp-bin +BINARY_OUTPUT ?= jpg-bin binary: build-native @echo "==> Building self-contained $(BINARY_OUTPUT) binary" @@ -70,37 +70,37 @@ install-script: build-native mkdir -p $(BIN_DIR) $(MAN_DIR) printf '#!/bin/sh\nexec %s -q --libdirs %s:%s/lib --script %s/pgp/main.ss -- "$$@"\n' \ "$(SCHEME)" "$(CURDIR)" "$(JERBOA_HOME)" "$(CURDIR)" \ - > $(BIN_DIR)/jpgp - chmod +x $(BIN_DIR)/jpgp - cp doc/jpgp.1 $(MAN_DIR)/jpgp.1 - @echo "Installed jpgp launcher to $(BIN_DIR)/jpgp" - @echo "Installed man page to $(MAN_DIR)/jpgp.1" + > $(BIN_DIR)/jpg + chmod +x $(BIN_DIR)/jpg + cp doc/jpg.1 $(MAN_DIR)/jpg.1 + @echo "Installed jpg launcher to $(BIN_DIR)/jpg" + @echo "Installed man page to $(MAN_DIR)/jpg.1" @echo "" @echo "Shell completions are in completions/ — copy to your distro's path:" - @echo " bash: completions/jpgp.bash -> ~/.local/share/bash-completion/completions/jpgp" - @echo " zsh : completions/_jpgp -> any directory in \$$fpath" + @echo " bash: completions/jpg.bash -> ~/.local/share/bash-completion/completions/jpg" + @echo " zsh : completions/_jpg -> any directory in \$$fpath" @echo "" @echo "(This wraps the dev interpreter; for a static binary see PLAN.md)" install: binary mkdir -p $(BIN_DIR) $(LIB_DIR) $(MAN_DIR) - cp $(BINARY_OUTPUT) $(BIN_DIR)/jpgp - chmod +x $(BIN_DIR)/jpgp + cp $(BINARY_OUTPUT) $(BIN_DIR)/jpg + chmod +x $(BIN_DIR)/jpg cp $(NATIVE_LIB) $(LIB_DIR)/$(notdir $(NATIVE_LIB)) - cp doc/jpgp.1 $(MAN_DIR)/jpgp.1 + cp doc/jpg.1 $(MAN_DIR)/jpg.1 @echo "" @echo "Installed:" - @echo " $(BIN_DIR)/jpgp" + @echo " $(BIN_DIR)/jpg" @echo " $(LIB_DIR)/$(notdir $(NATIVE_LIB))" - @echo " $(MAN_DIR)/jpgp.1" + @echo " $(MAN_DIR)/jpg.1" @echo "" @echo "Shell completions are in completions/ — copy to your distro's path:" - @echo " bash: completions/jpgp.bash -> ~/.local/share/bash-completion/completions/jpgp" - @echo " zsh : completions/_jpgp -> any directory in \$$fpath" + @echo " bash: completions/jpg.bash -> ~/.local/share/bash-completion/completions/jpg" + @echo " zsh : completions/_jpg -> any directory in \$$fpath" clean: cd $(NATIVE_DIR) && cargo clean find . -name '*.so' -not -path './pgp-native/*' -delete find . -name '*.dylib' -not -path './pgp-native/*' -delete find . -name '*.wpo' -delete - rm -f jpgp-bin + rm -f jpg-bin jpgp-bin --- a/README.md +++ b/README.md @@ -4,15 +4,15 @@ A modern, friendlier replacement for GPG, written in [Jerboa] with a pure-Rust crypto backend. **No C dependencies.** ``` -jpgp keygen -jpgp encrypt -r alice.pub.jpgp -i secret.txt -o secret.txt.age -jpgp decrypt -i secret.txt.age -jpgp sign -i release.tar.gz -o release.tar.gz.sig -jpgp verify release.tar.gz.sig -i release.tar.gz +jpg keygen +jpg encrypt -r alice.pub.jpgp -i secret.txt -o secret.txt.age +jpg decrypt -i secret.txt.age +jpg sign -i release.tar.gz -o release.tar.gz.sig +jpg verify release.tar.gz.sig -i release.tar.gz ``` Native crypto is [age]-style (X25519 + ChaCha20-Poly1305) for encryption and -Ed25519 for signing. For interop with the few remaining GPG users, `jpgp +Ed25519 for signing. For interop with the few remaining GPG users, `jpg encrypt -r alice.asc` recognises an OpenPGP public key and encrypts to it in OpenPGP format using [rPGP] — so the recipient can decrypt with plain `gpg`. @@ -26,7 +26,7 @@ v1 implements: - `encrypt -r FOO.asc` — OpenPGP outbound (rPGP, SEIPDv1) - `decrypt --pgp-key SECRET.asc` — OpenPGP inbound - `sign` / `verify` (Ed25519 native, `--pgp-key` produces OpenPGP detached sigs) -- Self-contained `jpgp-bin` (Chez `compile-program` + boot-embedded) +- Self-contained `jpg-bin` (Chez `compile-program` + boot-embedded) - Bidirectional gpg interop tested end-to-end (`make test-interop`) See `PLAN.md` for the design and out-of-scope items. @@ -53,16 +53,16 @@ Requires Chez Scheme + [Jerboa] checked out at `~/mine/jerboa` (or set make run ARGS='version' # interpreter mode make test # smoke tests make test-interop # bidirectional gpg interop tests -make binary # native `jpgp` binary (5MB Mach-O / ELF) -make install # → ~/.local/bin/jpgp + ~/.local/lib/libjpgp_native +make binary # native `jpg` binary (5MB Mach-O / ELF) +make install # → ~/.local/bin/jpg + ~/.local/lib/libjpgp_native ``` ## Interop with gpg -`jpgp encrypt -r alice.asc -i secret.txt -o secret.asc` produces a SEIPDv1 +`jpg encrypt -r alice.asc -i secret.txt -o secret.asc` produces a SEIPDv1 (RFC 4880) OpenPGP message that any version of gpg can decrypt. -The reverse direction (`jpgp decrypt --pgp-key alice.sec.asc -i msg.asc`) +The reverse direction (`jpg decrypt --pgp-key alice.sec.asc -i msg.asc`) works when the gpg recipient key does **not** advertise AEAD preferences in its self-signature. Modern GnuPG 2.5 defaults to its proprietary OCB packet (tag 20), which rPGP cannot parse. To produce a compatible key in new file mode 100644 --- /dev/null +++ b/completions/_jpg @@ -0,0 +1,86 @@ +#compdef jpg +# zsh completion for jpg. +# Install to a directory in your $fpath, e.g. /usr/local/share/zsh/site-functions/_jpg, +# or add `fpath+=(<dir-containing-_jpg>)` before `compinit` in ~/.zshrc. + +_jpg() { + local -a commands + commands=( + 'keygen:Generate a new identity' + 'pubkey:Print or write the public-key line' + 'list:Show identity info' + 'fingerprint:Print SHA256: fingerprint' + 'encrypt:Encrypt data to recipients or a passphrase' + 'decrypt:Decrypt age, jpgp, or OpenPGP data' + 'sign:Produce a jpgp or OpenPGP signature' + 'verify:Verify a jpgp or OpenPGP signature' + 'version:Print version' + 'help:Show help' + ) + + if (( CURRENT == 2 )); then + _describe 'command' commands + return + fi + + local cmd="$words[2]" + case "$cmd" in + keygen) + _arguments \ + '--out[output path]:identity file:_files' \ + '-o[output path]:identity file:_files' + ;; + pubkey) + _arguments \ + '--identity[identity file]:file:_files' \ + '--out[output path]:file:_files' \ + '-o[output path]:file:_files' + ;; + fingerprint|list) + _arguments \ + '--identity[identity file]:file:_files' + ;; + encrypt) + _arguments \ + '*-r[recipient]:recipient:_files' \ + '*--recipient[recipient]:recipient:_files' \ + '-i[input file]:file:_files' \ + '--in[input file]:file:_files' \ + '-o[output file]:file:_files' \ + '--out[output file]:file:_files' \ + '-s[symmetric mode]' \ + '--symmetric[symmetric mode]' + ;; + decrypt) + _arguments \ + '-i[input file]:file:_files' \ + '--in[input file]:file:_files' \ + '-o[output file]:file:_files' \ + '--out[output file]:file:_files' \ + '--identity[identity file]:file:_files' \ + '--pgp-key[OpenPGP secret key]:file:_files' \ + '--pgp-pass[OpenPGP passphrase]:passphrase' \ + '-s[symmetric mode]' \ + '--symmetric[symmetric mode]' + ;; + sign) + _arguments \ + '-i[input file]:file:_files' \ + '--in[input file]:file:_files' \ + '-o[output file]:file:_files' \ + '--out[output file]:file:_files' \ + '--identity[identity file]:file:_files' \ + '--pgp-key[OpenPGP secret key]:file:_files' \ + '--pgp-pass[OpenPGP passphrase]:passphrase' + ;; + verify) + _arguments \ + '-i[input file]:file:_files' \ + '--in[input file]:file:_files' \ + '--pubkey[jpgp1 pubkey]:file:_files' \ + '--pgp-pubkey[OpenPGP pubkey]:file:_files' \ + '*:signature file:_files' + ;; + esac +} +_jpg "$@" deleted file mode 100644 --- a/completions/_jpgp +++ /dev/null @@ -1,86 +0,0 @@ -#compdef jpgp -# zsh completion for jpgp. -# Install to a directory in your $fpath, e.g. /usr/local/share/zsh/site-functions/_jpgp, -# or add `fpath+=(<dir-containing-_jpgp>)` before `compinit` in ~/.zshrc. - -_jpgp() { - local -a commands - commands=( - 'keygen:Generate a new identity' - 'pubkey:Print or write the public-key line' - 'list:Show identity info' - 'fingerprint:Print SHA256: fingerprint' - 'encrypt:Encrypt data to recipients or a passphrase' - 'decrypt:Decrypt age, jpgp, or OpenPGP data' - 'sign:Produce a jpgp or OpenPGP signature' - 'verify:Verify a jpgp or OpenPGP signature' - 'version:Print version' - 'help:Show help' - ) - - if (( CURRENT == 2 )); then - _describe 'command' commands - return - fi - - local cmd="$words[2]" - case "$cmd" in - keygen) - _arguments \ - '--out[output path]:identity file:_files' \ - '-o[output path]:identity file:_files' - ;; - pubkey) - _arguments \ - '--identity[identity file]:file:_files' \ - '--out[output path]:file:_files' \ - '-o[output path]:file:_files' - ;; - fingerprint|list) - _arguments \ - '--identity[identity file]:file:_files' - ;; - encrypt) - _arguments \ - '*-r[recipient]:recipient:_files' \ - '*--recipient[recipient]:recipient:_files' \ - '-i[input file]:file:_files' \ - '--in[input file]:file:_files' \ - '-o[output file]:file:_files' \ - '--out[output file]:file:_files' \ - '-s[symmetric mode]' \ - '--symmetric[symmetric mode]' - ;; - decrypt) - _arguments \ - '-i[input file]:file:_files' \ - '--in[input file]:file:_files' \ - '-o[output file]:file:_files' \ - '--out[output file]:file:_files' \ - '--identity[identity file]:file:_files' \ - '--pgp-key[OpenPGP secret key]:file:_files' \ - '--pgp-pass[OpenPGP passphrase]:passphrase' \ - '-s[symmetric mode]' \ - '--symmetric[symmetric mode]' - ;; - sign) - _arguments \ - '-i[input file]:file:_files' \ - '--in[input file]:file:_files' \ - '-o[output file]:file:_files' \ - '--out[output file]:file:_files' \ - '--identity[identity file]:file:_files' \ - '--pgp-key[OpenPGP secret key]:file:_files' \ - '--pgp-pass[OpenPGP passphrase]:passphrase' - ;; - verify) - _arguments \ - '-i[input file]:file:_files' \ - '--in[input file]:file:_files' \ - '--pubkey[jpgp1 pubkey]:file:_files' \ - '--pgp-pubkey[OpenPGP pubkey]:file:_files' \ - '*:signature file:_files' - ;; - esac -} -_jpgp "$@" new file mode 100644 --- /dev/null +++ b/completions/jpg.bash @@ -0,0 +1,59 @@ +# bash completion for jpg +# Source from ~/.bashrc or install to /etc/bash_completion.d/ (or your distro's path). + +_jpg() { + local cur prev words cword + _init_completion || return + + local commands="keygen pubkey list fingerprint encrypt decrypt sign verify version help" + + if [[ $cword -eq 1 ]]; then + COMPREPLY=( $(compgen -W "$commands" -- "$cur") ) + return + fi + + local cmd="${words[1]}" + case "$cmd" in + keygen) + COMPREPLY=( $(compgen -W "--out -o" -- "$cur") ) + ;; + pubkey) + case "$prev" in + --identity|--out|-o) _filedir ; return ;; + esac + COMPREPLY=( $(compgen -W "--identity --out -o" -- "$cur") ) + ;; + fingerprint|list) + case "$prev" in + --identity) _filedir ; return ;; + esac + COMPREPLY=( $(compgen -W "--identity" -- "$cur") ) + ;; + encrypt) + case "$prev" in + -r|--recipient|-i|--in|-o|--out) _filedir ; return ;; + esac + COMPREPLY=( $(compgen -W "-r --recipient -i --in -o --out -s --symmetric" -- "$cur") ) + ;; + decrypt) + case "$prev" in + -i|--in|-o|--out|--identity|--pgp-key) _filedir ; return ;; + esac + COMPREPLY=( $(compgen -W "-i --in -o --out --identity --pgp-key --pgp-pass -s --symmetric" -- "$cur") ) + ;; + sign) + case "$prev" in + -i|--in|-o|--out|--identity|--pgp-key) _filedir ; return ;; + esac + COMPREPLY=( $(compgen -W "-i --in -o --out --identity --pgp-key --pgp-pass" -- "$cur") ) + ;; + verify) + case "$prev" in + -i|--in|--pubkey|--pgp-pubkey) _filedir ; return ;; + verify) _filedir ; return ;; + esac + COMPREPLY=( $(compgen -W "-i --in --pubkey --pgp-pubkey" -- "$cur") ) + ;; + esac +} +complete -F _jpg jpg deleted file mode 100644 --- a/completions/jpgp.bash +++ /dev/null @@ -1,59 +0,0 @@ -# bash completion for jpgp -# Source from ~/.bashrc or install to /etc/bash_completion.d/ (or your distro's path). - -_jpgp() { - local cur prev words cword - _init_completion || return - - local commands="keygen pubkey list fingerprint encrypt decrypt sign verify version help" - - if [[ $cword -eq 1 ]]; then - COMPREPLY=( $(compgen -W "$commands" -- "$cur") ) - return - fi - - local cmd="${words[1]}" - case "$cmd" in - keygen) - COMPREPLY=( $(compgen -W "--out -o" -- "$cur") ) - ;; - pubkey) - case "$prev" in - --identity|--out|-o) _filedir ; return ;; - esac - COMPREPLY=( $(compgen -W "--identity --out -o" -- "$cur") ) - ;; - fingerprint|list) - case "$prev" in - --identity) _filedir ; return ;; - esac - COMPREPLY=( $(compgen -W "--identity" -- "$cur") ) - ;; - encrypt) - case "$prev" in - -r|--recipient|-i|--in|-o|--out) _filedir ; return ;; - esac - COMPREPLY=( $(compgen -W "-r --recipient -i --in -o --out -s --symmetric" -- "$cur") ) - ;; - decrypt) - case "$prev" in - -i|--in|-o|--out|--identity|--pgp-key) _filedir ; return ;; - esac - COMPREPLY=( $(compgen -W "-i --in -o --out --identity --pgp-key --pgp-pass -s --symmetric" -- "$cur") ) - ;; - sign) - case "$prev" in - -i|--in|-o|--out|--identity|--pgp-key) _filedir ; return ;; - esac - COMPREPLY=( $(compgen -W "-i --in -o --out --identity --pgp-key --pgp-pass" -- "$cur") ) - ;; - verify) - case "$prev" in - -i|--in|--pubkey|--pgp-pubkey) _filedir ; return ;; - verify) _filedir ; return ;; - esac - COMPREPLY=( $(compgen -W "-i --in --pubkey --pgp-pubkey" -- "$cur") ) - ;; - esac -} -complete -F _jpgp jpgp new file mode 100644 --- /dev/null +++ b/doc/jpg.1 @@ -0,0 +1,194 @@ +.Dd May 15, 2026 +.Dt JPG 1 +.Os +.Sh NAME +.Nm jpg +.Nd modern PGP replacement with age + Ed25519 + OpenPGP interop +.Sh SYNOPSIS +.Nm +.Ar command +.Op Ar options +.Sh DESCRIPTION +.Nm +is a small command-line tool for encrypting, signing, and verifying +data. +It uses +.Xr age 1 +for native encryption, +.Xr ed25519 7 +for native signatures, and rPGP for OpenPGP interoperability with +.Xr gpg 1 . +.Pp +By default, identities live in +.Pa ~/.jpgp/identity.age +encrypted with a user-supplied passphrase. +Each identity bundles one age secret key and one Ed25519 signing key. +.Sh COMMANDS +.Bl -tag -width "encrypt" +.It Cm keygen Op Fl -out Ar PATH +Generate a new identity. +Prompts for a passphrase. +.It Cm pubkey Oo Fl -identity Ar PATH Oc Oo Fl -out Ar PATH Oc +Print the +.Sq jpgp1 +public-key line for this identity. +.It Cm fingerprint Op Fl -identity Ar PATH +Print the SSH-style +.Sq SHA256: +fingerprint of this identity's public key line. +.It Cm list Op Fl -identity Ar PATH +Show identity paths, age pubkey, fingerprint, and full public line. +.It Xo +.Cm encrypt +.Op Fl r Ar RECIPIENT ... +.Op Fl s | -symmetric +.Op Fl i Ar IN +.Op Fl o Ar OUT +.Xc +Encrypt +.Ar IN +to one or more recipients. +.Ar RECIPIENT +may be a bare +.Sq age1... +string, a path to a +.Sq jpgp1 +public-key line, or a path to an armored OpenPGP public key. +Multiple +.Fl r +flags may be given for age/jpgp recipients (all share one ciphertext). +OpenPGP recipients require a single +.Fl r +and produce gpg-compatible output (SEIPDv1 / RFC 4880). +With +.Fl -symmetric , +prompts for a passphrase instead of using recipient keys. +.It Xo +.Cm decrypt +.Op Fl s | -symmetric +.Op Fl i Ar IN +.Op Fl o Ar OUT +.Op Fl -identity Ar PATH +.Op Fl -pgp-key Ar SECRET.asc +.Op Fl -pgp-pass Ar PASS +.Xc +Decrypt +.Ar IN . +Defaults to identity mode (age payload). +.Pp +With +.Fl -symmetric , +prompts for a passphrase. +With +.Fl -pgp-key , +decrypts an OpenPGP message using the armored secret key at the given +path. +.Nm +parses SEIPDv1 packets only (the format gpg emits when its recipient +keys do not advertise AEAD prefs); the gnupg-proprietary OCB packet +.Pq packet 20 +is not supported. +.It Xo +.Cm sign +.Op Fl i Ar IN +.Op Fl o Ar OUT +.Op Fl -identity Ar PATH +.Op Fl -pgp-key Ar SECRET.asc +.Op Fl -pgp-pass Ar PASS +.Xc +Sign +.Ar IN +with this identity. +Default output is a +.Sq jpgp +signature blob over +.Xr ed25519 7 . +With +.Fl -pgp-key , +produces a gpg-compatible detached OpenPGP signature. +.It Xo +.Cm verify +.Ar SIG +.Op Fl i Ar IN +.Op Fl -pubkey Ar FILE +.Op Fl -pgp-pubkey Ar PUB.asc +.Xc +Verify the signature at +.Ar SIG +against +.Ar IN . +The format is auto-detected by armor header: +jpgp blobs are verified with the embedded pubkey +.Pq or one supplied via Fl -pubkey , +and OpenPGP signatures with +.Fl -pgp-pubkey . +Exit status is 0 on success, 2 on bad signature. +.It Cm version +Print version. +.El +.Pp +For every command, +.Sq Fl i Sq - +means stdin and +.Sq Fl o Sq - +means stdout. +.Sh FILES +.Bl -tag -width "~/.jpgp/identity.age" +.It Pa ~/.jpgp/identity.age +Default identity file (age-armored, passphrase-encrypted). +.El +.Sh EXAMPLES +Generate an identity and print its fingerprint: +.Bd -literal -offset indent +$ jpg keygen +$ jpg fingerprint +SHA256:9p+rmTm... +.Ed +.Pp +Encrypt a file to a friend's age recipient: +.Bd -literal -offset indent +$ jpg encrypt -r age1xyz... -i secrets.txt -o secrets.age +.Ed +.Pp +Encrypt to a colleague who uses gpg: +.Bd -literal -offset indent +$ jpg encrypt -r colleague.asc -i memo.txt -o memo.asc +.Ed +.Pp +Sign a release with an OpenPGP key, gpg-compatible: +.Bd -literal -offset indent +$ jpg sign --pgp-key release.sec.asc \\ + -i release.tar.gz -o release.tar.gz.asc +.Ed +.Sh EXIT STATUS +.Ex -std +A bad signature in +.Cm verify +exits with status 2. +.Sh INTEROPERABILITY +.Nm +encrypts to OpenPGP recipients using SEIPDv1 +.Pq RFC 4880 . +GnuPG 2.5 defaults to its proprietary OCB encrypted data packet +.Pq packet 20 , +which rPGP cannot parse. +For +.Cm decrypt +to work on a gpg-produced ciphertext, the recipient key's +self-signature must not advertise AEAD support: either generate the gpg +key with +.Sq features +byte +.Sq 0x05 +.Pq instead of the default 0x07 , +or strip the +.Sq pref-aead-algos +subpacket from the key prefs. +See +.Pa test/interop-gpg.sh +in the source tree for a working configuration. +.Sh SEE ALSO +.Xr age 1 , +.Xr gpg 1 +.Sh AUTHORS +Written by Jaime Fournier and Claude. deleted file mode 100644 --- a/doc/jpgp.1 +++ /dev/null @@ -1,194 +0,0 @@ -.Dd May 15, 2026 -.Dt JPGP 1 -.Os -.Sh NAME -.Nm jpgp -.Nd modern PGP replacement with age + Ed25519 + OpenPGP interop -.Sh SYNOPSIS -.Nm -.Ar command -.Op Ar options -.Sh DESCRIPTION -.Nm -is a small command-line tool for encrypting, signing, and verifying -data. -It uses -.Xr age 1 -for native encryption, -.Xr ed25519 7 -for native signatures, and rPGP for OpenPGP interoperability with -.Xr gpg 1 . -.Pp -By default, identities live in -.Pa ~/.jpgp/identity.age -encrypted with a user-supplied passphrase. -Each identity bundles one age secret key and one Ed25519 signing key. -.Sh COMMANDS -.Bl -tag -width "encrypt" -.It Cm keygen Op Fl -out Ar PATH -Generate a new identity. -Prompts for a passphrase. -.It Cm pubkey Oo Fl -identity Ar PATH Oc Oo Fl -out Ar PATH Oc -Print the -.Sq jpgp1 -public-key line for this identity. -.It Cm fingerprint Op Fl -identity Ar PATH -Print the SSH-style -.Sq SHA256: -fingerprint of this identity's public key line. -.It Cm list Op Fl -identity Ar PATH -Show identity paths, age pubkey, fingerprint, and full public line. -.It Xo -.Cm encrypt -.Op Fl r Ar RECIPIENT ... -.Op Fl s | -symmetric -.Op Fl i Ar IN -.Op Fl o Ar OUT -.Xc -Encrypt -.Ar IN -to one or more recipients. -.Ar RECIPIENT -may be a bare -.Sq age1... -string, a path to a -.Sq jpgp1 -public-key line, or a path to an armored OpenPGP public key. -Multiple -.Fl r -flags may be given for age/jpgp recipients (all share one ciphertext). -OpenPGP recipients require a single -.Fl r -and produce gpg-compatible output (SEIPDv1 / RFC 4880). -With -.Fl -symmetric , -prompts for a passphrase instead of using recipient keys. -.It Xo -.Cm decrypt -.Op Fl s | -symmetric -.Op Fl i Ar IN -.Op Fl o Ar OUT -.Op Fl -identity Ar PATH -.Op Fl -pgp-key Ar SECRET.asc -.Op Fl -pgp-pass Ar PASS -.Xc -Decrypt -.Ar IN . -Defaults to identity mode (age payload). -.Pp -With -.Fl -symmetric , -prompts for a passphrase. -With -.Fl -pgp-key , -decrypts an OpenPGP message using the armored secret key at the given -path. -.Nm -parses SEIPDv1 packets only (the format gpg emits when its recipient -keys do not advertise AEAD prefs); the gnupg-proprietary OCB packet -.Pq packet 20 -is not supported. -.It Xo -.Cm sign -.Op Fl i Ar IN -.Op Fl o Ar OUT -.Op Fl -identity Ar PATH -.Op Fl -pgp-key Ar SECRET.asc -.Op Fl -pgp-pass Ar PASS -.Xc -Sign -.Ar IN -with this identity. -Default output is a -.Sq jpgp -signature blob over -.Xr ed25519 7 . -With -.Fl -pgp-key , -produces a gpg-compatible detached OpenPGP signature. -.It Xo -.Cm verify -.Ar SIG -.Op Fl i Ar IN -.Op Fl -pubkey Ar FILE -.Op Fl -pgp-pubkey Ar PUB.asc -.Xc -Verify the signature at -.Ar SIG -against -.Ar IN . -The format is auto-detected by armor header: -jpgp blobs are verified with the embedded pubkey -.Pq or one supplied via Fl -pubkey , -and OpenPGP signatures with -.Fl -pgp-pubkey . -Exit status is 0 on success, 2 on bad signature. -.It Cm version -Print version. -.El -.Pp -For every command, -.Sq Fl i Sq - -means stdin and -.Sq Fl o Sq - -means stdout. -.Sh FILES -.Bl -tag -width "~/.jpgp/identity.age" -.It Pa ~/.jpgp/identity.age -Default identity file (age-armored, passphrase-encrypted). -.El -.Sh EXAMPLES -Generate an identity and print its fingerprint: -.Bd -literal -offset indent -$ jpgp keygen -$ jpgp fingerprint -SHA256:9p+rmTm... -.Ed -.Pp -Encrypt a file to a friend's age recipient: -.Bd -literal -offset indent -$ jpgp encrypt -r age1xyz... -i secrets.txt -o secrets.age -.Ed -.Pp -Encrypt to a colleague who uses gpg: -.Bd -literal -offset indent -$ jpgp encrypt -r colleague.asc -i memo.txt -o memo.asc -.Ed -.Pp -Sign a release with an OpenPGP key, gpg-compatible: -.Bd -literal -offset indent -$ jpgp sign --pgp-key release.sec.asc \\ - -i release.tar.gz -o release.tar.gz.asc -.Ed -.Sh EXIT STATUS -.Ex -std -A bad signature in -.Cm verify -exits with status 2. -.Sh INTEROPERABILITY -.Nm -encrypts to OpenPGP recipients using SEIPDv1 -.Pq RFC 4880 . -GnuPG 2.5 defaults to its proprietary OCB encrypted data packet -.Pq packet 20 , -which rPGP cannot parse. -For -.Cm decrypt -to work on a gpg-produced ciphertext, the recipient key's -self-signature must not advertise AEAD support: either generate the gpg -key with -.Sq features -byte -.Sq 0x05 -.Pq instead of the default 0x07 , -or strip the -.Sq pref-aead-algos -subpacket from the key prefs. -See -.Pa test/interop-gpg.sh -in the source tree for a working configuration. -.Sh SEE ALSO -.Xr age 1 , -.Xr gpg 1 -.Sh AUTHORS -Written by Jaime Fournier and Claude. --- a/pgp/cli.ss +++ b/pgp/cli.ss @@ -25,7 +25,7 @@ (pgp identity) (pgp prompt)) - (def jpgp-version-string "jpgp 0.1.0") + (def jpgp-version-string "jpg 0.1.0") ;; ── Tiny option parser ───────────────────────────────────────────────── ;; Returns (alist . positional). known-flags is a list of @@ -69,13 +69,13 @@ [else (loop (cdr xs) acc)]))) (def (die fmt . args) - (apply fprintf (cons (current-error-port) (cons (string-append "jpgp: " fmt "\n") args))) + (apply fprintf (cons (current-error-port) (cons (string-append "jpg: " fmt "\n") args))) (exit 1)) ;; ── help ─────────────────────────────────────────────────────────────── (def (cmd-help . _) - (display "Usage: jpgp <command> [options] + (display "Usage: jpg <command> [options] Commands: keygen [--out PATH] @@ -194,7 +194,7 @@ Globally, -i/--in '-' means stdin and -o/--out '-' means stdout. [idp (opt-or opts "--identity" (default-identity-path))]) (cond [(not (file-exists? idp)) - (die "no identity at ~a — run `jpgp keygen`" idp)] + (die "no identity at ~a — run `jpg keygen`" idp)] [else (let* ([pass (read-passphrase (format "Passphrase for ~a: " idp))] [id (load-identity idp pass)]) --- a/support/binary-entry.ss +++ b/support/binary-entry.ss @@ -1,8 +1,8 @@ #!chezscheme -;;; binary-entry.ss — Entry point compiled into the static `jpgp` binary. +;;; binary-entry.ss — Entry point compiled into the static `jpg` binary. ;;; ;;; Compiled by support/build-binary.sh into a single ELF/Mach-O binary that -;;; bundles petite.boot, scheme.boot, and the whole-program-optimised jpgp +;;; bundles petite.boot, scheme.boot, and the whole-program-optimised jpg ;;; libraries. The native crypto remains in libjpgp_native.{dylib,so} and is ;;; loaded at runtime — set JPGP_NATIVE_LIB to override the search. --- a/support/build-binary.sh +++ b/support/build-binary.sh @@ -1,5 +1,5 @@ #!/bin/sh -# build-binary.sh — Build a self-contained jpgp binary. +# build-binary.sh — Build a self-contained jpg binary. # # Thin wrapper around Jerboa's support/build-binary.sh that passes both # the project libdirs ($CURDIR) and the Jerboa lib so WPO can resolve all @@ -15,7 +15,7 @@ SCHEME="${SCHEME:-$JERBOA_HOME/.chez/bin/scheme}" JERBOA_CHEZ_PREFIX="${JERBOA_CHEZ_PREFIX:-$JERBOA_HOME/.chez}" ENTRY="${1:-$JPGP_REPO/support/binary-entry.ss}" -OUTPUT="${2:-jpgp-bin}" +OUTPUT="${2:-jpg-bin}" # Detect OS and toolchain (mirrors Jerboa's build-binary.sh exactly). OS=$(uname -s) @@ -52,7 +52,7 @@ if [ -z "$CSV_DIR" ]; then exit 1 fi -echo "=== jpgp binary build: $ENTRY -> $OUTPUT ===" +echo "=== jpg binary build: $ENTRY -> $OUTPUT ===" echo " OS: $OS" echo " CC: $CC" echo " Chez: $CSV_DIR" @@ -61,7 +61,7 @@ echo "" # Step 1: WPO compile — needs BOTH $JPGP_REPO (for (pgp ...) libs) and # $JERBOA_HOME/lib (for (jerboa prelude), (std ...), etc.). WPO_SO="${OUTPUT}.wp.so" -OBJ_DIR=$(mktemp -d "/tmp/jpgp-bin-obj.XXXXXX") +OBJ_DIR=$(mktemp -d "/tmp/jpg-bin-obj.XXXXXX") trap 'rm -rf "$OBJ_DIR" "$WPO_SO" petite_boot.h scheme_boot.h program_boot.h "${OUTPUT}-main.c"' EXIT echo "==> [1/4] WPO compile" @@ -88,7 +88,7 @@ echo "" # Step 3: generate main.c echo "==> [3/4] Generate ${OUTPUT}-main.c" cat > "${OUTPUT}-main.c" <<'CMAIN' -/* jpgp binary entry point */ +/* jpg binary entry point */ #include "scheme.h" #include <stdio.h> #include <stdlib.h> @@ -102,7 +102,7 @@ cat > "${OUTPUT}-main.c" <<'CMAIN' #include "program_boot.h" static const char *write_program_tmpfile(void) { - static char path[] = "/tmp/jpgp-prog-XXXXXX"; + static char path[] = "/tmp/jpg-prog-XXXXXX"; int fd = mkstemp(path); if (fd < 0) { perror("mkstemp"); exit(1); } ssize_t n = write(fd, program_boot_data, program_boot_size);