forked from github/smimesign
-
Notifications
You must be signed in to change notification settings - Fork 0
/
status.go
225 lines (193 loc) · 6.8 KB
/
status.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
package main
import (
"crypto"
"crypto/x509"
"fmt"
"os"
"sync"
"time"
"golang.org/x/crypto/openpgp/packet"
"golang.org/x/crypto/openpgp/s2k"
)
// This file implements gnupg's "status protocol". When the --status-fd argument
// is passed, gpg will output machine-readable status updates to that fd.
// Details on the "protocol" can be found at https://git.io/vFFKC
type status string
const (
// BEGIN_SIGNING
// Mark the start of the actual signing process. This may be used as an
// indication that all requested secret keys are ready for use.
sBeginSigning status = "BEGIN_SIGNING"
// SIG_CREATED <type> <pk_algo> <hash_algo> <class> <timestamp> <keyfpr>
// A signature has been created using these parameters.
// Values for type <type> are:
// - D :: detached
// - C :: cleartext
// - S :: standard
// (only the first character should be checked)
//
// <class> are 2 hex digits with the OpenPGP signature class.
//
// Note, that TIMESTAMP may either be a number of seconds since Epoch
// or an ISO 8601 string which can be detected by the presence of the
// letter 'T'.
sSigCreated status = "SIG_CREATED"
// NEWSIG [<signers_uid>]
// Is issued right before a signature verification starts. This is
// useful to define a context for parsing ERROR status messages.
// arguments are currently defined. If SIGNERS_UID is given and is
// not "-" this is the percent escape value of the OpenPGP Signer's
// User ID signature sub-packet.
sNewSig status = "NEWSIG"
// GOODSIG <long_keyid_or_fpr> <username>
// The signature with the keyid is good. For each signature only one
// of the codes GOODSIG, BADSIG, EXPSIG, EXPKEYSIG, REVKEYSIG or
// ERRSIG will be emitted. In the past they were used as a marker
// for a new signature; new code should use the NEWSIG status
// instead. The username is the primary one encoded in UTF-8 and %XX
// escaped. The fingerprint may be used instead of the long keyid if
// it is available. This is the case with CMS and might eventually
// also be available for OpenPGP.
sGoodSig status = "GOODSIG"
// BADSIG <long_keyid_or_fpr> <username>
// The signature with the keyid has not been verified okay. The username is
// the primary one encoded in UTF-8 and %XX escaped. The fingerprint may be
// used instead of the long keyid if it is available. This is the case with
// CMS and might eventually also be available for OpenPGP.
sBadSig status = "BADSIG"
// ERRSIG <keyid> <pkalgo> <hashalgo> <sig_class> <time> <rc>
//
// It was not possible to check the signature. This may be caused by a
// missing public key or an unsupported algorithm. A RC of 4 indicates
// unknown algorithm, a 9 indicates a missing public key. The other fields
// give more information about this signature. sig_class is a 2 byte hex-
// value. The fingerprint may be used instead of the keyid if it is
// available. This is the case with gpgsm and might eventually also be
// available for OpenPGP.
//
// Note, that TIME may either be the number of seconds since Epoch or an ISO
// 8601 string. The latter can be detected by the presence of the letter
// ‘T’.
sErrSig status = "ERRSIG"
// TRUST_
// These are several similar status codes:
//
// - TRUST_UNDEFINED <error_token>
// - TRUST_NEVER <error_token>
// - TRUST_MARGINAL [0 [<validation_model>]]
// - TRUST_FULLY [0 [<validation_model>]]
// - TRUST_ULTIMATE [0 [<validation_model>]]
//
// For good signatures one of these status lines are emitted to
// indicate the validity of the key used to create the signature.
// The error token values are currently only emitted by gpgsm.
//
// VALIDATION_MODEL describes the algorithm used to check the
// validity of the key. The defaults are the standard Web of Trust
// model for gpg and the standard X.509 model for gpgsm. The
// defined values are
//
// - pgp :: The standard PGP WoT.
// - shell :: The standard X.509 model.
// - chain :: The chain model.
// - steed :: The STEED model.
// - tofu :: The TOFU model
//
// Note that the term =TRUST_= in the status names is used for
// historic reasons; we now speak of validity.
sTrustFully status = "TRUST_FULLY"
)
var (
_setupStatus sync.Once
statusFile *os.File
)
func setupStatus() {
_setupStatus.Do(func() {
if *statusFdOpt <= 0 {
return
}
const (
unixStdout = 1
unixStderr = 2
)
// Even though Windows uses different numbers, we always equate 1/2 with
// stdout/stderr because Git always passes `--status-fd=1`.
switch *statusFdOpt {
case unixStdout:
statusFile = os.Stdout
case unixStderr:
statusFile = os.Stderr
default:
// TODO: debugging output if this fails
statusFile = os.NewFile(uintptr(*statusFdOpt), "status")
}
})
}
func (s status) emitf(format string, args ...interface{}) {
setupStatus()
if statusFile == nil {
return
}
const prefix = "[GNUPG:] "
statusFile.WriteString(prefix)
statusFile.WriteString(string(s))
fmt.Fprintf(statusFile, " "+format+"\n", args...)
}
func (s status) emit() {
setupStatus()
if statusFile == nil {
return
}
const prefix = "[GNUPG:] "
statusFile.WriteString(prefix + string(s) + "\n")
}
func emitSigCreated(cert *x509.Certificate, isDetached bool) {
// SIG_CREATED arguments
var (
sigType string
pkAlgo, hashAlgo, sigClass byte
now int64
fpr string
)
if isDetached {
sigType = "D"
} else {
sigType = "S"
}
switch cert.SignatureAlgorithm {
case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
pkAlgo = byte(packet.PubKeyAlgoRSA)
case x509.ECDSAWithSHA1, x509.ECDSAWithSHA256, x509.ECDSAWithSHA384, x509.ECDSAWithSHA512:
pkAlgo = byte(packet.PubKeyAlgoECDSA)
}
switch cert.SignatureAlgorithm {
case x509.SHA1WithRSA, x509.ECDSAWithSHA1:
hashAlgo, _ = s2k.HashToHashId(crypto.SHA1)
case x509.SHA256WithRSA, x509.ECDSAWithSHA256:
hashAlgo, _ = s2k.HashToHashId(crypto.SHA256)
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
hashAlgo, _ = s2k.HashToHashId(crypto.SHA384)
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
hashAlgo, _ = s2k.HashToHashId(crypto.SHA512)
}
// gpgsm seems to always use 0x00
sigClass = 0
now = time.Now().Unix()
fpr = certHexFingerprint(cert)
sSigCreated.emitf("%s %d %d %02x %d %s", sigType, pkAlgo, hashAlgo, sigClass, now, fpr)
}
func emitGoodSig(chains [][][]*x509.Certificate) {
cert := chains[0][0][0]
subj := cert.Subject.String()
fpr := certHexFingerprint(cert)
sGoodSig.emitf("%s %s", fpr, subj)
}
func emitBadSig(chains [][][]*x509.Certificate) {
cert := chains[0][0][0]
subj := cert.Subject.String
fpr := certHexFingerprint(cert)
sBadSig.emitf("%s %s", fpr, subj)
}
func emitTrustFully() {
sTrustFully.emitf("0 shell")
}