1package commitverify
2
3import (
4 "log"
5
6 "github.com/go-git/go-git/v5/plumbing/object"
7 "tangled.sh/tangled.sh/core/appview/db"
8 "tangled.sh/tangled.sh/core/crypto"
9 "tangled.sh/tangled.sh/core/types"
10)
11
12type verifiedCommit struct {
13 fingerprint string
14 hash string
15}
16
17type VerifiedCommits map[verifiedCommit]struct{}
18
19func (vcs VerifiedCommits) IsVerified(hash string) bool {
20 for vc := range vcs {
21 if vc.hash == hash {
22 return true
23 }
24 }
25 return false
26}
27
28func (vcs VerifiedCommits) Fingerprint(hash string) string {
29 for vc := range vcs {
30 if vc.hash == hash {
31 return vc.fingerprint
32 }
33 }
34 return ""
35}
36
37func GetVerifiedObjectCommits(e db.Execer, emailToDid map[string]string, commits []*object.Commit) (VerifiedCommits, error) {
38 ndCommits := []types.NiceDiff{}
39 for _, commit := range commits {
40 ndCommits = append(ndCommits, ObjectCommitToNiceDiff(commit))
41 }
42 return GetVerifiedCommits(e, emailToDid, ndCommits)
43}
44
45func GetVerifiedCommits(e db.Execer, emailToDid map[string]string, ndCommits []types.NiceDiff) (VerifiedCommits, error) {
46 vcs := VerifiedCommits{}
47
48 didPubkeyCache := make(map[string][]db.PublicKey)
49
50 for _, commit := range ndCommits {
51 c := commit.Commit
52
53 committerEmail := c.Committer.Email
54 if did, exists := emailToDid[committerEmail]; exists {
55 // check if we've already fetched public keys for this did
56 pubKeys, ok := didPubkeyCache[did]
57 if !ok {
58 // fetch and cache public keys
59 keys, err := db.GetPublicKeysForDid(e, did)
60 if err != nil {
61 log.Printf("failed to fetch pubkey for %s: %v", committerEmail, err)
62 continue
63 }
64 pubKeys = keys
65 didPubkeyCache[did] = pubKeys
66 }
67
68 // try to verify with any associated pubkeys
69 for _, pk := range pubKeys {
70 if _, ok := crypto.VerifyCommitSignature(pk.Key, commit); ok {
71
72 fp, err := crypto.SSHFingerprint(pk.Key)
73 if err != nil {
74 log.Println("error computing ssh fingerprint:", err)
75 }
76
77 vc := verifiedCommit{fingerprint: fp, hash: c.This}
78 vcs[vc] = struct{}{}
79 break
80 }
81 }
82
83 }
84 }
85
86 return vcs, nil
87}
88
89// ObjectCommitToNiceDiff is a compatibility function to convert a
90// commit object into a NiceDiff structure.
91func ObjectCommitToNiceDiff(c *object.Commit) types.NiceDiff {
92 var niceDiff types.NiceDiff
93
94 // set commit information
95 niceDiff.Commit.Message = c.Message
96 niceDiff.Commit.Author = c.Author
97 niceDiff.Commit.This = c.Hash.String()
98 niceDiff.Commit.Committer = c.Committer
99 niceDiff.Commit.Tree = c.TreeHash.String()
100 niceDiff.Commit.PGPSignature = c.PGPSignature
101
102 changeId, ok := c.ExtraHeaders["change-id"]
103 if ok {
104 niceDiff.Commit.ChangedId = string(changeId)
105 }
106
107 // set parent hash if available
108 if len(c.ParentHashes) > 0 {
109 niceDiff.Commit.Parent = c.ParentHashes[0].String()
110 }
111
112 // XXX: Stats and Diff fields are typically populated
113 // after fetching the actual diff information, which isn't
114 // directly available in the commit object itself.
115
116 return niceDiff
117}