forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
Monorepo for Tangled
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package crypto
2
3import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/base64"
7 "fmt"
8 "strings"
9
10 "github.com/hiddeco/sshsig"
11 "golang.org/x/crypto/ssh"
12 "tangled.sh/tangled.sh/core/types"
13)
14
15func VerifySignature(pubKey, signature, payload []byte) (error, bool) {
16 pub, _, _, _, err := ssh.ParseAuthorizedKey(pubKey)
17 if err != nil {
18 return fmt.Errorf("failed to parse public key: %w", err), false
19 }
20
21 sig, err := sshsig.Unarmor(signature)
22 if err != nil {
23 return fmt.Errorf("failed to parse signature: %w", err), false
24 }
25
26 buf := bytes.NewBuffer(payload)
27 // we use sha-512 because ed25519 keys require it internally; rsa keys support
28 // multiple algorithms but sha-512 is most secure, and git's ssh signing defaults
29 // to sha-512 for all key types anyway.
30 err = sshsig.Verify(buf, sig, pub, sshsig.HashSHA512, "git")
31 return err, err == nil
32}
33
34// VerifyCommitSignature reconstructs the payload used to sign a commit. This is
35// essentially the git cat-file output but without the gpgsig header.
36//
37// Caveats: signature verification will fail on commits with more than one parent,
38// i.e. merge commits, because types.NiceDiff doesn't carry more than one Parent field
39// and we are unable to reconstruct the payload correctly.
40//
41// Ideally this should directly operate on an *object.Commit.
42func VerifyCommitSignature(pubKey string, commit types.NiceDiff) (error, bool) {
43 signature := commit.Commit.PGPSignature
44
45 author := bytes.NewBuffer([]byte{})
46 committer := bytes.NewBuffer([]byte{})
47 commit.Commit.Author.Encode(author)
48 commit.Commit.Committer.Encode(committer)
49
50 payload := strings.Builder{}
51
52 fmt.Fprintf(&payload, "tree %s\n", commit.Commit.Tree)
53 if commit.Commit.Parent != "" {
54 fmt.Fprintf(&payload, "parent %s\n", commit.Commit.Parent)
55 }
56 fmt.Fprintf(&payload, "author %s\n", author.String())
57 fmt.Fprintf(&payload, "committer %s\n", committer.String())
58 if commit.Commit.ChangedId != "" {
59 fmt.Fprintf(&payload, "change-id %s\n", commit.Commit.ChangedId)
60 }
61 fmt.Fprintf(&payload, "\n%s", commit.Commit.Message)
62
63 return VerifySignature([]byte(pubKey), []byte(signature), []byte(payload.String()))
64}
65
66// SSHFingerprint computes the fingerprint of the supplied ssh pubkey.
67func SSHFingerprint(pubKey string) (string, error) {
68 pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubKey))
69 if err != nil {
70 return "", err
71 }
72
73 hash := sha256.Sum256(pk.Marshal())
74 return "SHA256:" + base64.StdEncoding.EncodeToString(hash[:]), nil
75}