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