-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprotocol.h
254 lines (212 loc) · 11.5 KB
/
protocol.h
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/* Copyright 2016 The Roughtime Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */
#ifndef SECURITY_ROUGHTIME_PROTOCOL_H_
#define SECURITY_ROUGHTIME_PROTOCOL_H_
#include <stdint.h>
#include <string.h>
namespace roughtime {
// Minimum size of a time request. Requests must be padded to larger than their
// contents in order to reduce the value of a time server as a DDOS amplifier.
constexpr size_t kMinRequestSize = 1024;
constexpr size_t kNonceLength = 64; // Size of the client's nonce.
constexpr size_t kTimestampSize = 8; // Size of the server's time.
constexpr size_t kRadiusSize = 4; // Size of the server's uncertainty.
constexpr size_t kPrivateKeyLength = 64; // Size of the server's private key.
constexpr size_t kPublicKeyLength = 32; // Size of the server's public key.
constexpr size_t kSignatureLength = 64; // Size of server signatures.
typedef uint32_t tag_t;
// rough_time_t is the type of a time stamp. Time is UTC and is given as
// milliseconds since the UNIX epoch (00:00:00 UTC on 1 January 1970). Leap
// seconds are linearly smeared over a 24-hour period. That is, the smear
// extends from UTC noon to noon over 86,401 or 86,399 SI seconds, and all the
// smeared seconds are the same length.
typedef uint64_t rough_time_t;
// MakeTag creates an integer value representing a tag. Requests and responses
// in the time protocol are made up of tagged data, QUIC-style. Tags are 4
// bytes long and meant to be readable-ish, but they cannot be chosen with
// complete freedom, because they must be strictly increasing within a message
// to permit binary searches.
constexpr tag_t MakeTag(char a, char b, char c, char d) {
return static_cast<uint32_t>(a) | static_cast<uint32_t>(b) << 8 |
static_cast<uint32_t>(c) << 16 | static_cast<uint32_t>(d) << 24;
}
// kTagNONC ("nonce") is used in client requests. It tags the request's nonce.
constexpr tag_t kTagNONC = MakeTag('N', 'O', 'N', 'C');
// kTagPAD ("padding") is used in client requests. It tags the padding that
// fills out the request to at least |kMinRequestSize|.
constexpr tag_t kTagPAD = MakeTag('P', 'A', 'D', '\xff');
// kTagMIDP is used in the signed portion of server responses. It tags the
// midpoint of the server's time in Unix epoch-microseconds.
constexpr tag_t kTagMIDP = MakeTag('M', 'I', 'D', 'P');
// kTagRADI contains the radius of uncertainty (in microseconds) of the
// server's time.
constexpr tag_t kTagRADI = MakeTag('R', 'A', 'D', 'I');
// kTagROOT is used in the signed portion of server responses. It tags the root
// of a Merkle tree that contains the nonces from a batch of client requests.
constexpr tag_t kTagROOT = MakeTag('R', 'O', 'O', 'T');
// kTagSIG ("signature") is used in in the unsigned portion of server responses.
// It tags a signature made using the key from the server's certificate.
constexpr tag_t kTagSIG = MakeTag('S', 'I', 'G', '\0');
// kTagPATH is used in the unsigned portion of server responses. It tags the
// path from the client's nonce, a leaf node, to the root of the Merkle tree, so
// that the client can verify the inclusion of its nonce in the tree.
constexpr tag_t kTagPATH = MakeTag('P', 'A', 'T', 'H');
// kTagSREP ("signed response") is used in the unsigned portion
// of server responses. It tags the signed portion of the response.
constexpr tag_t kTagSREP = MakeTag('S', 'R', 'E', 'P');
// kTagCERT ("certificate") is used in the unsigned portion of server responses.
// It tags a (not X.509) certificate. The tagged value is the public key whose
// private key the server uses to sign its response. The public key is signed
// offline by another keypair, whose public key is baked into the client.
constexpr tag_t kTagCERT = MakeTag('C', 'E', 'R', 'T');
// kTagINDX ("index") is used in the unsigned portion of server responses. It
// tells the client the index that was assigned to its nonce when generating the
// Merkle tree.
constexpr tag_t kTagINDX = MakeTag('I', 'N', 'D', 'X');
// kTagPUBK ("public key") is used in server certificates. It tags the public
// key whose private key was used to sign the server's response.
constexpr tag_t kTagPUBK = MakeTag('P', 'U', 'B', 'K');
// kTagMINT ("minimum validity timestamp") is used in server certificates. It
// tags the beginning of the certificate's validity period.
constexpr tag_t kTagMINT = MakeTag('M', 'I', 'N', 'T');
// kTagMAXT ("maximum validity timestamp") is used in server certificates. It
// tags the end of the certificate's validity period.
constexpr tag_t kTagMAXT = MakeTag('M', 'A', 'X', 'T');
// kTagDELE ("delegation") is used in server certificates. It tags the data
// signed by the server's offline key.
constexpr tag_t kTagDELE = MakeTag('D', 'E', 'L', 'E');
// kContextString is prefixed to the server's response before generating or
// verifying the server's signature.
static const char kContextString[] = "RoughTime v1 response signature";
static_assert(sizeof(kContextString) % 4 == 0,
"Context strings must be a multiple of four bytes long");
// kCertContextString is added as a prefix to the server's certificate before
// generating or verifying the certificate's signature.
static const char kCertContextString[] = "RoughTime v1 delegation signature--";
static_assert(sizeof(kCertContextString) % 4 == 0,
"Context strings must be a multiple of four bytes long");
// NumMessageOffsets gives the size in entries of the table of offsets for a
// time protocol message having |num_tags| tags. (Since the length of messages
// in the time protocol is known, a message with only one tag does not need a
// table of offsets.)
constexpr size_t NumMessageOffsets(size_t num_tags) {
return num_tags == 0 ? 0 : num_tags - 1;
}
// MessageHeaderLen gives the size in bytes of a message header, which consists
// of a tag count, a table of offsets (if there are least two tags), and a list
// of 0 or more tags.
constexpr size_t MessageHeaderLen(size_t num_tags) {
return sizeof(uint32_t) /* tag count */ +
sizeof(uint32_t) * NumMessageOffsets(num_tags) /* offsets */ +
sizeof(tag_t) * num_tags /* tag values */;
}
// kPaddingLen is the number of padding bytes necessary to make a client request
// sufficiently long.
constexpr size_t kPaddingLen =
kMinRequestSize - (MessageHeaderLen(2) + kNonceLength);
// Parser decodes requests from a time server client.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Number of tags |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Offset, Tag 1 Data |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Offset, Tag N Data |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Tag 0 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Tag N |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Data... |
// | (indexed by offsets) |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
class Parser {
public:
// Parses the supplied data. No copies are made, so the data pointed to by
// |req| must live as long as the |Parser| object. Callers should check
// |is_valid| before calling any other method.
Parser(const uint8_t* req, size_t len);
Parser() = delete;
Parser(const Parser&) = delete;
Parser& operator=(const Parser&) = delete;
bool is_valid() const { return is_valid_; }
// GetTag sets |*out_data| and |*out_len| to reflect the location (within the
// data supplied to the constructor) of the tag |tag|, if found. Returns true
// if |tag| is found.
bool GetTag(const uint8_t** out_data, size_t* out_len, tag_t tag) const;
// GetFixedLen is a specialization of |GetTag| that returns true only if the
// data tagged by |tag| is |expected_len| bytes long.
bool GetFixedLen(const uint8_t** out_data, tag_t tag,
size_t expected_len) const;
// Get is a specialization of |GetFixedLen| that expects |tag| to tag a value
// of type |T|. The tagged value is assigned to |*out_value|.
template <typename T>
bool Get(T* out_value, tag_t tag) const;
private:
bool is_valid_ = false;
size_t num_tags_;
const uint8_t* offsets_; // |NumMessageOffsets| offsets into |data_|.
const uint8_t* tags_; // |num_tags_| 32-bit tags.
const uint8_t* data_; // |len_| bytes.
size_t len_;
};
// Builder creates a time server response.
class Builder {
public:
Builder() = delete;
Builder(const Builder&) = delete;
Builder& operator=(const Builder&) = delete;
// Prepares to write tags and data to a buffer |out| of |out_len| bytes.
Builder(uint8_t* out, size_t out_len, size_t num_tags);
// AddTag adds the tag |tag| to the response, which must be greater than any
// previously added tag, and sets |*out_data| to an address where |len| bytes
// may be written. Returns true iff the tag may be written.
bool AddTag(uint8_t** out_data, tag_t tag, size_t len);
// AddTagData is a specialization of |AddTag| that copies the |len| bytes
// pointed to by |data| into the response. Returns true iff the tag was
// written.
bool AddTagData(tag_t tag, const uint8_t* data, size_t len);
// Finish returns true if the response is valid, and sets |out_len| to the
// size of the response. After calling |Finish| all subsequent calls will
// fail.
bool Finish(size_t* out_len);
private:
const size_t num_tags_;
const size_t header_len_;
uint8_t* const offsets_;
uint8_t* const tags_;
uint8_t* data_; // Offset of next |AddTag|.
size_t len_; // Bytes remaining in the output buffer.
size_t offset_ = 0; // Offset of data for next tag.
size_t tag_i_ = 0; // Index of next tag.
tag_t previous_tag_;
bool have_previous_tag_ = false;
bool valid_ = false;
};
// Computes the SHA-512 hash of a Merkle tree leaf, i.e. a client's nonce, as
// 0||nonce. |in| is assumed to point to |kNonceLength| bytes and |out| is
// assumed to have space for |SHA512_DIGEST_LENGTH| bytes.
void HashLeaf(uint8_t* out, const uint8_t* in);
// Computes the SHA-512 hash of a Merkle tree node as 1||left||right. |left|,
// |right|, and |out| are assumed to point to |SHA512_DIGEST_LENGTH| bytes.
void HashNode(uint8_t* out, const uint8_t* left, const uint8_t* right);
} // namespace roughtime
#endif // SECURITY_ROUGHTIME_PROTOCOL_H_