...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package signdeb
18
19 import (
20 "bufio"
21 "bytes"
22 "crypto"
23 "errors"
24 "fmt"
25 "io"
26 "io/ioutil"
27 "strings"
28
29 "github.com/qur/ar"
30 "github.com/sassoftware/relic/lib/pgptools"
31 "golang.org/x/crypto/openpgp"
32 )
33
34
35
36
37 func Verify(r io.Reader, keyring openpgp.EntityList, skipDigest bool) (map[string]*pgptools.PgpSignature, error) {
38 reader := ar.NewReader(r)
39 digests := make(map[string]string)
40 sigs := make(map[string][]byte)
41 for {
42 hdr, err := reader.Next()
43 if err == io.EOF {
44 break
45 } else if err != nil {
46 return nil, err
47 }
48 if strings.HasPrefix(hdr.Name, "_gpg") {
49 role := hdr.Name[4:]
50 sigs[role], err = ioutil.ReadAll(reader)
51 if err != nil {
52 return nil, err
53 }
54 } else if !skipDigest {
55 md5 := crypto.MD5.New()
56 sha1 := crypto.SHA1.New()
57 if _, err := io.Copy(io.MultiWriter(md5, sha1), reader); err != nil {
58 return nil, err
59 }
60 digests[hdr.Name] = fmt.Sprintf("%x %x", md5.Sum(nil), sha1.Sum(nil))
61 }
62 }
63 ret := make(map[string]*pgptools.PgpSignature, len(sigs))
64 for role, sig := range sigs {
65 var body bytes.Buffer
66 info, err := pgptools.VerifyClearSign(bytes.NewReader(sig), &body, keyring)
67 if err != nil {
68 return nil, err
69 }
70 if !skipDigest {
71 if err := checkSig(role, &body, digests); err != nil {
72 return nil, err
73 }
74 }
75 ret[role] = info
76 }
77 return ret, nil
78 }
79
80 func checkSig(role string, body io.Reader, digests map[string]string) error {
81 sawFiles := false
82 scanner := bufio.NewScanner(body)
83
84 for scanner.Scan() {
85 line := scanner.Text()
86 if line == "Files:" {
87 sawFiles = true
88 break
89 }
90 }
91 if !sawFiles {
92 return errors.New("malformed signature")
93 }
94
95 checked := make(map[string]bool, len(digests))
96 for scanner.Scan() {
97 line := scanner.Text()
98 if line == "" {
99 break
100 } else if line[0] != '\t' || len(line) < 76 {
101 return errors.New("malformed signature")
102 }
103 parts := strings.SplitN(line[1:], " ", 4)
104 sums := parts[0] + " " + parts[1]
105 name := parts[3]
106 calculated := digests[name]
107 if calculated == "" {
108 return fmt.Errorf("signature references unknown file %s", name)
109 } else if calculated != sums {
110 return fmt.Errorf("signature mismatch on file %s: (%s) != (%s)", name, calculated, sums)
111 }
112 checked[name] = true
113 }
114
115 for name := range digests {
116 if checked[name] {
117 continue
118 }
119 return fmt.Errorf("signature does not cover file: %s", name)
120 }
121 return nil
122 }
123
View as plain text