1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package pgptools
18
19 import (
20 "bytes"
21 "crypto"
22 "errors"
23 "fmt"
24 "io"
25 "io/ioutil"
26 "time"
27
28 "golang.org/x/crypto/openpgp"
29 "golang.org/x/crypto/openpgp/clearsign"
30 "golang.org/x/crypto/openpgp/packet"
31 )
32
33 type PgpSignature struct {
34 Key *openpgp.Key
35 CreationTime time.Time
36 Hash crypto.Hash
37 }
38
39
40
41
42 func VerifyDetached(signature, signed io.Reader, keyring openpgp.EntityList) (*PgpSignature, error) {
43 packetReader := packet.NewReader(signature)
44 genpkt, err := packetReader.Next()
45 if err == io.EOF {
46 return nil, errors.New("no PGP signature found")
47 } else if err != nil {
48 return nil, err
49 }
50
51 var hash crypto.Hash
52 var keyID uint64
53 var creationTime time.Time
54 switch pkt := genpkt.(type) {
55 case *packet.SignatureV3:
56 hash = pkt.Hash
57 keyID = pkt.IssuerKeyId
58 creationTime = pkt.CreationTime
59 case *packet.Signature:
60 if pkt.IssuerKeyId == nil {
61 return nil, errors.New("Missing keyId in signature")
62 }
63 hash = pkt.Hash
64 keyID = *pkt.IssuerKeyId
65 creationTime = pkt.CreationTime
66 default:
67 return nil, errors.New("not a PGP signature")
68 }
69
70 keys := keyring.KeysById(keyID)
71 if len(keys) == 0 {
72 return nil, ErrNoKey(keyID)
73 }
74
75 if !hash.Available() {
76 return nil, errors.New("signature uses unknown digest")
77 }
78 d := hash.New()
79 if _, err := io.Copy(d, signed); err != nil {
80 return nil, err
81 }
82
83 switch pkt := genpkt.(type) {
84 case *packet.SignatureV3:
85 err = keys[0].PublicKey.VerifySignatureV3(d, pkt)
86 case *packet.Signature:
87 err = keys[0].PublicKey.VerifySignature(d, pkt)
88 }
89 return &PgpSignature{&keys[0], creationTime, hash}, err
90 }
91
92
93
94
95 func VerifyClearSign(signature io.Reader, cleartext io.Writer, keyring openpgp.EntityList) (*PgpSignature, error) {
96 blob, err := ioutil.ReadAll(signature)
97 if err != nil {
98 return nil, err
99 }
100 csblock, rest := clearsign.Decode(blob)
101 if csblock == nil {
102 return nil, errors.New("malformed clearsign signature")
103 } else if bytes.Contains(rest, []byte("-----BEGIN")) {
104 return nil, errors.New("clearsign contains multiple documents")
105 }
106 if cleartext != nil {
107 if _, err := cleartext.Write(csblock.Bytes); err != nil {
108 return nil, err
109 }
110 }
111 sig, err := VerifyDetached(csblock.ArmoredSignature.Body, bytes.NewReader(csblock.Bytes), keyring)
112 return sig, err
113 }
114
115
116
117
118 func VerifyInline(signature io.Reader, cleartext io.Writer, keyring openpgp.EntityList) (*PgpSignature, error) {
119 md, err := openpgp.ReadMessage(signature, keyring, nil, nil)
120 if err == io.EOF {
121 return nil, ErrNoContent{}
122 } else if err != nil {
123 return nil, err
124 } else if md.SignedBy == nil {
125 return nil, ErrNoKey(md.SignedByKeyId)
126 }
127 if cleartext == nil {
128 cleartext = ioutil.Discard
129 }
130 if _, err := io.Copy(cleartext, md.UnverifiedBody); err != nil {
131 return nil, err
132 }
133
134 sig := &PgpSignature{Key: md.SignedBy}
135 if md.Signature != nil {
136 sig.CreationTime = md.Signature.CreationTime
137 sig.Hash = md.Signature.Hash
138 } else if md.SignatureV3 != nil {
139 sig.CreationTime = md.SignatureV3.CreationTime
140 sig.Hash = md.Signature.Hash
141 }
142 return sig, md.SignatureError
143 }
144
145
146
147 type ErrNoKey uint64
148
149 func (e ErrNoKey) Error() string {
150 return fmt.Sprintf("keyId %x not found", uint64(e))
151 }
152
153
154 type ErrNoContent struct{}
155
156 func (ErrNoContent) Error() string {
157 return "missing content for detached signature"
158 }
159
View as plain text