forked from github/ietf-cms
/
timestamp.go
189 lines (154 loc) · 4.31 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
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
package cms
import (
"bytes"
"crypto/x509"
"errors"
"io"
"github.com/andr3whur5t/cms/protocol"
"github.com/andr3whur5t/cms/timestamp"
"github.com/mastahyeti/cms/oid"
)
type (
StamperResponse struct {
RawTimeStampToken []byte
Error error
}
)
// 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 (sd *SignedData) AddRawTimestamps(stamper func([]byte) *StamperResponse) error {
var (
attrs = make([]protocol.Attribute, len(sd.psd.SignerInfos))
)
// Fetch all timestamp tokens before adding any to sd. This avoids a partial failure.
for i := range attrs {
siDigest, err := siTimeStampDigest(sd.psd.SignerInfos[i])
if err != nil {
return err
}
sr := stamper(siDigest)
if sr.Error != nil {
err = sr.Error
return err
}
ciToken, err := protocol.ParseContentInfo(sr.RawTimeStampToken)
if err != nil {
return err
}
attrs[i], err = protocol.NewAttribute(oid.AttributeTimeStampToken, ciToken)
if err != nil {
return err
}
}
for i := range attrs {
sd.psd.SignerInfos[i].UnsignedAttrs = append(sd.psd.SignerInfos[i].UnsignedAttrs, attrs[i])
}
return nil
}
func siTimeStampDigest(si protocol.SignerInfo) (digest []byte, err error) {
hash, err := si.Hash()
if err != nil {
return nil, err
}
h := hash.New()
_, err = io.Copy(h, bytes.NewReader(si.Signature))
if err != nil {
return nil, err
}
return h.Sum(nil), 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
}