Skip to content

Commit

Permalink
feat: use internal OpenPGP implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
calmh committed Nov 24, 2024
1 parent eafb30c commit 014738c
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 35 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.23.3

require (
github.com/alecthomas/kong v1.4.0
golang.org/x/crypto v0.9.0
golang.org/x/mod v0.22.0
pault.ag/go/debian v0.17.0
)
Expand All @@ -12,6 +13,5 @@ require (
github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
golang.org/x/crypto v0.9.0 // indirect
pault.ag/go/topsort v0.1.1 // indirect
)
81 changes: 81 additions & 0 deletions internal/publish/pgp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package publish

import (
"crypto"
"encoding/hex"
"fmt"
"io"

_ "crypto/sha256"

_ "golang.org/x/crypto/ripemd160"

"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/clearsign"
"golang.org/x/crypto/openpgp/packet"
)

type signer struct {
keys []*packet.PrivateKey
}

func newSigner(keychain io.Reader) (*signer, error) {
pr := packet.NewReader(keychain)
s := &signer{}
for {
pkt, err := pr.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
switch key := pkt.(type) {
case *packet.PrivateKey:
if !key.IsSubkey && key.PublicKey.PublicKey != nil {
fmt.Println("private", hex.EncodeToString(key.PublicKey.Fingerprint[:]), key.IsSubkey)
s.keys = append(s.keys, key)
}
}
}
return s, nil
}

type seekable interface {
io.Reader
io.Seeker
}

func (s *signer) DetachSign(in seekable, out io.Writer) error {
if len(s.keys) == 0 {
return fmt.Errorf("no private keys found")
}
cfg := &packet.Config{
DefaultHash: crypto.SHA256,
}
for _, key := range s.keys {
if _, err := in.Seek(0, io.SeekStart); err != nil {
return err
}
signer := &openpgp.Entity{PrivateKey: key}
if err := openpgp.DetachSign(out, signer, in, cfg); err != nil {
return err
}
}
return nil
}

func (s *signer) ClearSign(in seekable, out io.Writer) error {
if len(s.keys) == 0 {
return fmt.Errorf("no private keys found")
}

w, err := clearsign.EncodeMulti(out, s.keys, nil)
if err != nil {
return err
}
if _, err := io.Copy(w, in); err != nil {
return err
}
return w.Close()
}
18 changes: 12 additions & 6 deletions internal/publish/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package publish

import (
"fmt"
"os"
"path/filepath"
)

Expand All @@ -13,11 +14,6 @@ type CLI struct {
}

func (c *CLI) Run() error {
keyring, err := filepath.Abs(c.Keyring)
if err != nil {
return fmt.Errorf("publish: %w", err)
}

pkgs, err := scanPackages(c.Dists)
if err != nil {
return fmt.Errorf("publish: %w", err)
Expand All @@ -33,11 +29,21 @@ func (c *CLI) Run() error {
return fmt.Errorf("publish: globbing: %w", err)
}

fd, err := os.Open(c.Keyring)
if err != nil {
return fmt.Errorf("publish: %w", err)
}
sign, err := newSigner(fd)
fd.Close()
if err != nil {
return fmt.Errorf("publish: %w", err)
}

for _, dist := range dists {
if err := writeRelease(dist); err != nil {
return fmt.Errorf("publish: %w", err)
}
if err := signRelease(dist, keyring, c.SignUser); err != nil {
if err := signRelease(dist, sign); err != nil {
return fmt.Errorf("publish: %w", err)
}
}
Expand Down
55 changes: 27 additions & 28 deletions internal/publish/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package publish

import (
"compress/gzip"
"fmt"
"io"
"log/slog"
"os"
"os/exec"
"path/filepath"
"slices"
"strings"
Expand Down Expand Up @@ -145,43 +143,44 @@ func writeRelease(dist string) error {
return nil
}

func signRelease(dist, keyring string, users []string) error {
opts := []string{
"-o", filepath.Join(dist, "InRelease"),
"--yes",
"--no-default-keyring",
"--keyring", keyring,
func signRelease(dist string, s *signer) error {
in, err := os.Open(filepath.Join(dist, "Release"))
if err != nil {
return err
}
for _, user := range users {
opts = append(opts, "-u", user)
defer in.Close()

out, err := os.Create(filepath.Join(dist, "Release.gpg"))
if err != nil {
return err
}
opts = append(opts, "--clear-sign", filepath.Join(dist, "Release"))
if bs, err := exec.Command("gpg", opts...).CombinedOutput(); err != nil {
return fmt.Errorf("Failed to sign release: %s", bs)
if err := s.DetachSign(in, out); err != nil {
return err
}
if err := compress(filepath.Join(dist, "InRelease")); err != nil {
if err := out.Close(); err != nil {
return err
}

opts = []string{
"-o", filepath.Join(dist, "Release.gpg"),
"-a",
"--yes",
"--no-default-keyring",
"--keyring", keyring,
if err := compress(out.Name()); err != nil {
return err
}
for _, user := range users {
opts = append(opts, "-u", user)

if _, err := in.Seek(0, io.SeekStart); err != nil {
return err
}
opts = append(opts, "--detach-sign", filepath.Join(dist, "Release"))

if bs, err := exec.Command("gpg", opts...).CombinedOutput(); err != nil {
return fmt.Errorf("Failed to sign release: %s", bs)
out, err = os.Create(filepath.Join(dist, "InRelease"))
if err != nil {
return err
}
if err := compress(filepath.Join(dist, "Release.gpg")); err != nil {
if err := s.ClearSign(in, out); err != nil {
return err
}
if err := out.Close(); err != nil {
return err
}
if err := compress(out.Name()); err != nil {
return err
}

return nil
}

Expand Down

0 comments on commit 014738c

Please sign in to comment.