-
Notifications
You must be signed in to change notification settings - Fork 140
/
Copy pathtimestamp.go
129 lines (106 loc) · 3.19 KB
/
timestamp.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
package cms
import (
"bytes"
"crypto/x509"
"errors"
"github.com/github/smimesign/ietf-cms/oid"
"github.com/github/smimesign/ietf-cms/protocol"
"github.com/github/smimesign/ietf-cms/timestamp"
)
// AddTimestamps adds a timestamp to the SignedData using the RFC3161
// timestamping service at the given URL. This timestamp proves that the signed
// message existed the time of generation, allowing verifiers to have more trust
// in old messages signed with revoked keys.
func (sd *SignedData) AddTimestamps(url string) error {
var (
attrs = make([]protocol.Attribute, len(sd.psd.SignerInfos))
err error
)
// Fetch all timestamp tokens before adding any to sd. This avoids a partial
// failure.
for i := range attrs {
if attrs[i], err = fetchTS(url, sd.psd.SignerInfos[i]); err != nil {
return err
}
}
for i := range attrs {
sd.psd.SignerInfos[i].UnsignedAttrs = append(sd.psd.SignerInfos[i].UnsignedAttrs, attrs[i])
}
return nil
}
func fetchTS(url string, si protocol.SignerInfo) (protocol.Attribute, error) {
nilAttr := protocol.Attribute{}
req, err := tsRequest(si)
if err != nil {
return nilAttr, err
}
resp, err := req.Do(url)
if err != nil {
return nilAttr, err
}
if tsti, err := resp.Info(); err != nil {
return nilAttr, err
} else if !req.Matches(tsti) {
return nilAttr, errors.New("invalid message imprint")
}
return protocol.NewAttribute(oid.AttributeTimeStampToken, resp.TimeStampToken)
}
func tsRequest(si protocol.SignerInfo) (timestamp.Request, error) {
hash, err := si.Hash()
if err != nil {
return timestamp.Request{}, err
}
mi, err := timestamp.NewMessageImprint(hash, bytes.NewReader(si.Signature))
if err != nil {
return timestamp.Request{}, err
}
return timestamp.Request{
Version: 1,
CertReq: true,
Nonce: timestamp.GenerateNonce(),
MessageImprint: mi,
}, nil
}
// getTimestamp verifies and returns the timestamp.Info from the SignerInfo.
func getTimestamp(si protocol.SignerInfo, opts x509.VerifyOptions) (timestamp.Info, error) {
rawValue, err := si.UnsignedAttrs.GetOnlyAttributeValueBytes(oid.AttributeTimeStampToken)
if err != nil {
return timestamp.Info{}, err
}
tst, err := ParseSignedData(rawValue.FullBytes)
if err != nil {
return timestamp.Info{}, err
}
tsti, err := timestamp.ParseInfo(tst.psd.EncapContentInfo)
if err != nil {
return timestamp.Info{}, err
}
if tsti.Version != 1 {
return timestamp.Info{}, protocol.ErrUnsupported
}
// verify timestamp signature and certificate chain..
if _, err = tst.Verify(opts); err != nil {
return timestamp.Info{}, err
}
// verify timestamp token matches SignerInfo.
hash, err := tsti.MessageImprint.Hash()
if err != nil {
return timestamp.Info{}, err
}
mi, err := timestamp.NewMessageImprint(hash, bytes.NewReader(si.Signature))
if err != nil {
return timestamp.Info{}, err
}
if !mi.Equal(tsti.MessageImprint) {
return timestamp.Info{}, errors.New("invalid message imprint")
}
return tsti, nil
}
// hasTimestamp checks if si has a timestamp.
func hasTimestamp(si protocol.SignerInfo) (bool, error) {
vals, err := si.UnsignedAttrs.GetValues(oid.AttributeTimeStampToken)
if err != nil {
return false, err
}
return len(vals) > 0, nil
}