forked from github/smimesign
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathverify.go
174 lines (147 loc) · 5.41 KB
/
verify.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
package cms
import (
"bytes"
"crypto/x509"
"errors"
"github.com/github/smimesign/ietf-cms/protocol"
)
// Verify verifies the SingerInfos' signatures. Each signature's associated
// certificate is verified using the provided roots. UnsafeNoVerify may be
// specified to skip this verification. Nil may be provided to use system roots.
// The full chains for the certificates whose keys made the signatures are
// returned.
//
// WARNING: this function doesn't do any revocation checking.
func (sd *SignedData) Verify(opts x509.VerifyOptions) ([][][]*x509.Certificate, error) {
econtent, err := sd.psd.EncapContentInfo.EContentValue()
if err != nil {
return nil, err
}
if econtent == nil {
return nil, errors.New("detached signature")
}
return sd.verify(econtent, opts)
}
// VerifyDetached verifies the SingerInfos' detached signatures over the
// provided data message. Each signature's associated certificate is verified
// using the provided roots. UnsafeNoVerify may be specified to skip this
// verification. Nil may be provided to use system roots. The full chains for
// the certificates whose keys made the signatures are returned.
//
// WARNING: this function doesn't do any revocation checking.
func (sd *SignedData) VerifyDetached(message []byte, opts x509.VerifyOptions) ([][][]*x509.Certificate, error) {
if sd.psd.EncapContentInfo.EContent.Bytes != nil {
return nil, errors.New("signature not detached")
}
return sd.verify(message, opts)
}
func (sd *SignedData) verify(econtent []byte, opts x509.VerifyOptions) ([][][]*x509.Certificate, error) {
if len(sd.psd.SignerInfos) == 0 {
return nil, protocol.ASN1Error{Message: "no signatures found"}
}
certs, err := sd.psd.X509Certificates()
if err != nil {
return nil, err
}
if opts.Intermediates == nil {
opts.Intermediates = x509.NewCertPool()
}
for _, cert := range certs {
opts.Intermediates.AddCert(cert)
}
// Use provided verification options for timestamp verification also, but
// explicitly ask for key-usage=timestamping.
tsOpts := opts
tsOpts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}
chains := make([][][]*x509.Certificate, 0, len(sd.psd.SignerInfos))
for _, si := range sd.psd.SignerInfos {
var signedMessage []byte
// SignedAttrs is optional if EncapContentInfo eContentType isn't id-data.
if si.SignedAttrs == nil {
// SignedAttrs may only be absent if EncapContentInfo eContentType is
// id-data.
if !sd.psd.EncapContentInfo.IsTypeData() {
return nil, protocol.ASN1Error{Message: "missing SignedAttrs"}
}
// If SignedAttrs is absent, the signature is over the original
// encapsulated content itself.
signedMessage = econtent
} else {
// If SignedAttrs is present, we validate the mandatory ContentType and
// MessageDigest attributes.
siContentType, err := si.GetContentTypeAttribute()
if err != nil {
return nil, err
}
if !siContentType.Equal(sd.psd.EncapContentInfo.EContentType) {
return nil, protocol.ASN1Error{Message: "invalid SignerInfo ContentType attribute"}
}
// Calculate the digest over the actual message.
hash, err := si.Hash()
if err != nil {
return nil, err
}
actualMessageDigest := hash.New()
if _, err = actualMessageDigest.Write(econtent); err != nil {
return nil, err
}
// Get the digest from the SignerInfo.
messageDigestAttr, err := si.GetMessageDigestAttribute()
if err != nil {
return nil, err
}
// Make sure message digests match.
if !bytes.Equal(messageDigestAttr, actualMessageDigest.Sum(nil)) {
return nil, errors.New("invalid message digest")
}
// The signature is over the DER encoded signed attributes, minus the
// leading class/tag/length bytes. This includes the digest of the
// original message, so it is implicitly signed too.
if signedMessage, err = si.SignedAttrs.MarshaledForVerification(); err != nil {
return nil, err
}
}
cert, err := si.FindCertificate(certs)
if err != nil {
return nil, err
}
algo := si.X509SignatureAlgorithm()
if algo == x509.UnknownSignatureAlgorithm {
return nil, protocol.ErrUnsupported
}
if err := cert.CheckSignature(algo, signedMessage, si.Signature); err != nil {
return nil, err
}
// If the caller didn't specify the signature time, we'll use the verified
// timestamp. If there's no timestamp we use the current time when checking
// the cert validity window. This isn't perfect because the signature may
// have been created before the cert's not-before date, but this is the best
// we can do. We update a copy of opts because we are verifying multiple
// signatures in a loop and only want the timestamp to affect this one.
optsCopy := opts
if hasTS, err := hasTimestamp(si); err != nil {
return nil, err
} else if hasTS {
tsti, err := getTimestamp(si, tsOpts)
if err != nil {
return nil, err
}
// This check is slightly redundant, given that the cert validity times
// are checked by cert.Verify. We take the timestamp accuracy into account
// here though, whereas cert.Verify will not.
if !tsti.Before(cert.NotAfter) || !tsti.After(cert.NotBefore) {
return nil, x509.CertificateInvalidError{Cert: cert, Reason: x509.Expired, Detail: ""}
}
if optsCopy.CurrentTime.IsZero() {
optsCopy.CurrentTime = tsti.GenTime
}
}
if chain, err := cert.Verify(optsCopy); err != nil {
return nil, err
} else {
chains = append(chains, chain)
}
}
// OK
return chains, nil
}