forked from tangled.org/core
this repo has no description

crypto: helpers to verify commit signatures

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

Changed files
+74 -1
crypto
+70
crypto/verify.go
···
+
package crypto
+
+
import (
+
"bytes"
+
"crypto/sha256"
+
"encoding/base64"
+
"fmt"
+
"strings"
+
+
"github.com/hiddeco/sshsig"
+
"golang.org/x/crypto/ssh"
+
"tangled.sh/tangled.sh/core/types"
+
)
+
+
func VerifySignature(pubKey, signature, payload []byte) (error, bool) {
+
pub, _, _, _, err := ssh.ParseAuthorizedKey(pubKey)
+
if err != nil {
+
return fmt.Errorf("failed to parse public key: %w", err), false
+
}
+
+
sig, err := sshsig.Unarmor(signature)
+
if err != nil {
+
return fmt.Errorf("failed to parse signature: %w", err), false
+
}
+
+
buf := bytes.NewBuffer(payload)
+
sshsig.Verify(buf, sig, pub, sshsig.HashSHA256, "git")
+
return err, err == nil
+
}
+
+
// VerifyCommitSignature reconstructs the payload used to sign a commit. This is
+
// essentially the git cat-file output but without the gpgsig header.
+
//
+
// Caveats: signature verification will fail on commits with more than one parent,
+
// i.e. merge commits, because types.NiceDiff doesn't carry more than one Parent field
+
// and we are unable to reconstruct the payload correctly.
+
//
+
// Ideally this should directly operate on an *object.Commit.
+
func VerifyCommitSignature(pubKey string, commit types.NiceDiff) (error, bool) {
+
signature := commit.Commit.PGPSignature
+
+
author := bytes.NewBuffer([]byte{})
+
committer := bytes.NewBuffer([]byte{})
+
commit.Commit.Author.Encode(author)
+
commit.Commit.Committer.Encode(committer)
+
+
payload := strings.Builder{}
+
+
fmt.Fprintf(&payload, "tree %s\n", commit.Commit.Tree)
+
fmt.Fprintf(&payload, "parent %s\n", commit.Commit.Parent)
+
fmt.Fprintf(&payload, "author %s\n", author.String())
+
fmt.Fprintf(&payload, "committer %s\n", committer.String())
+
if commit.Commit.ChangedId != "" {
+
fmt.Fprintf(&payload, "change-id %s\n", commit.Commit.ChangedId)
+
}
+
fmt.Fprintf(&payload, "\n%s", commit.Commit.Message)
+
+
return VerifySignature([]byte(pubKey), []byte(signature), []byte(payload.String()))
+
}
+
+
// SSHFingerprint computes the fingerprint of the supplied ssh pubkey.
+
func SSHFingerprint(pubKey string) (string, error) {
+
pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubKey))
+
if err != nil {
+
return "", err
+
}
+
+
hash := sha256.Sum256(pk.Marshal())
+
return "SHA256:" + base64.StdEncoding.EncodeToString(hash[:]), nil
+
}
+2 -1
go.mod
···
github.com/go-git/go-git/v5 v5.14.0
github.com/google/uuid v1.6.0
github.com/gorilla/sessions v1.4.0
+
github.com/hiddeco/sshsig v0.2.0
github.com/ipfs/go-cid v0.5.0
github.com/lestrrat-go/jwx/v2 v2.1.6
github.com/mattn/go-sqlite3 v1.14.24
···
github.com/urfave/cli/v3 v3.3.3
github.com/whyrusleeping/cbor-gen v0.3.1
github.com/yuin/goldmark v1.4.13
+
golang.org/x/crypto v0.38.0
golang.org/x/net v0.39.0
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da
tangled.sh/icyphox.sh/atproto-oauth v0.0.0-20250526154904-3906c5336421
···
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
-
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.33.0 // indirect
+2
go.sum
···
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
+
github.com/hiddeco/sshsig v0.2.0 h1:gMWllgKCITXdydVkDL+Zro0PU96QI55LwUwebSwNTSw=
+
github.com/hiddeco/sshsig v0.2.0/go.mod h1:nJc98aGgiH6Yql2doqH4CTBVHexQA40Q+hMMLHP4EqE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=