roughtime.googlesource.com/roughtime.git@v0.0.0-20201210012726-dd529367052d/protocol.h (about) 1 /* Copyright 2016 The Roughtime Authors. 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. */ 14 15 #ifndef SECURITY_ROUGHTIME_PROTOCOL_H_ 16 #define SECURITY_ROUGHTIME_PROTOCOL_H_ 17 18 #include <stdint.h> 19 #include <string.h> 20 21 namespace roughtime { 22 23 // Minimum size of a time request. Requests must be padded to larger than their 24 // contents in order to reduce the value of a time server as a DDOS amplifier. 25 constexpr size_t kMinRequestSize = 1024; 26 27 constexpr size_t kNonceLength = 64; // Size of the client's nonce. 28 29 constexpr size_t kTimestampSize = 8; // Size of the server's time. 30 31 constexpr size_t kRadiusSize = 4; // Size of the server's uncertainty. 32 33 constexpr size_t kPrivateKeyLength = 64; // Size of the server's private key. 34 35 constexpr size_t kPublicKeyLength = 32; // Size of the server's public key. 36 37 constexpr size_t kSignatureLength = 64; // Size of server signatures. 38 39 typedef uint32_t tag_t; 40 41 // rough_time_t is the type of a time stamp. Time is UTC and is given as 42 // milliseconds since the UNIX epoch (00:00:00 UTC on 1 January 1970). Leap 43 // seconds are linearly smeared over a 24-hour period. That is, the smear 44 // extends from UTC noon to noon over 86,401 or 86,399 SI seconds, and all the 45 // smeared seconds are the same length. 46 typedef uint64_t rough_time_t; 47 48 // MakeTag creates an integer value representing a tag. Requests and responses 49 // in the time protocol are made up of tagged data, QUIC-style. Tags are 4 50 // bytes long and meant to be readable-ish, but they cannot be chosen with 51 // complete freedom, because they must be strictly increasing within a message 52 // to permit binary searches. 53 constexpr tag_t MakeTag(char a, char b, char c, char d) { 54 return static_cast<uint32_t>(a) | static_cast<uint32_t>(b) << 8 | 55 static_cast<uint32_t>(c) << 16 | static_cast<uint32_t>(d) << 24; 56 } 57 58 // kTagNONC ("nonce") is used in client requests. It tags the request's nonce. 59 constexpr tag_t kTagNONC = MakeTag('N', 'O', 'N', 'C'); 60 // kTagPAD ("padding") is used in client requests. It tags the padding that 61 // fills out the request to at least |kMinRequestSize|. 62 constexpr tag_t kTagPAD = MakeTag('P', 'A', 'D', '\xff'); 63 64 // kTagMIDP is used in the signed portion of server responses. It tags the 65 // midpoint of the server's time in Unix epoch-microseconds. 66 constexpr tag_t kTagMIDP = MakeTag('M', 'I', 'D', 'P'); 67 // kTagRADI contains the radius of uncertainty (in microseconds) of the 68 // server's time. 69 constexpr tag_t kTagRADI = MakeTag('R', 'A', 'D', 'I'); 70 // kTagROOT is used in the signed portion of server responses. It tags the root 71 // of a Merkle tree that contains the nonces from a batch of client requests. 72 constexpr tag_t kTagROOT = MakeTag('R', 'O', 'O', 'T'); 73 74 // kTagSIG ("signature") is used in in the unsigned portion of server responses. 75 // It tags a signature made using the key from the server's certificate. 76 constexpr tag_t kTagSIG = MakeTag('S', 'I', 'G', '\0'); 77 // kTagPATH is used in the unsigned portion of server responses. It tags the 78 // path from the client's nonce, a leaf node, to the root of the Merkle tree, so 79 // that the client can verify the inclusion of its nonce in the tree. 80 constexpr tag_t kTagPATH = MakeTag('P', 'A', 'T', 'H'); 81 // kTagSREP ("signed response") is used in the unsigned portion 82 // of server responses. It tags the signed portion of the response. 83 constexpr tag_t kTagSREP = MakeTag('S', 'R', 'E', 'P'); 84 // kTagCERT ("certificate") is used in the unsigned portion of server responses. 85 // It tags a (not X.509) certificate. The tagged value is the public key whose 86 // private key the server uses to sign its response. The public key is signed 87 // offline by another keypair, whose public key is baked into the client. 88 constexpr tag_t kTagCERT = MakeTag('C', 'E', 'R', 'T'); 89 // kTagINDX ("index") is used in the unsigned portion of server responses. It 90 // tells the client the index that was assigned to its nonce when generating the 91 // Merkle tree. 92 constexpr tag_t kTagINDX = MakeTag('I', 'N', 'D', 'X'); 93 94 // kTagPUBK ("public key") is used in server certificates. It tags the public 95 // key whose private key was used to sign the server's response. 96 constexpr tag_t kTagPUBK = MakeTag('P', 'U', 'B', 'K'); 97 // kTagMINT ("minimum validity timestamp") is used in server certificates. It 98 // tags the beginning of the certificate's validity period. 99 constexpr tag_t kTagMINT = MakeTag('M', 'I', 'N', 'T'); 100 // kTagMAXT ("maximum validity timestamp") is used in server certificates. It 101 // tags the end of the certificate's validity period. 102 constexpr tag_t kTagMAXT = MakeTag('M', 'A', 'X', 'T'); 103 // kTagDELE ("delegation") is used in server certificates. It tags the data 104 // signed by the server's offline key. 105 constexpr tag_t kTagDELE = MakeTag('D', 'E', 'L', 'E'); 106 107 // kContextString is prefixed to the server's response before generating or 108 // verifying the server's signature. 109 static const char kContextString[] = "RoughTime v1 response signature"; 110 static_assert(sizeof(kContextString) % 4 == 0, 111 "Context strings must be a multiple of four bytes long"); 112 113 // kCertContextString is added as a prefix to the server's certificate before 114 // generating or verifying the certificate's signature. 115 static const char kCertContextString[] = "RoughTime v1 delegation signature--"; 116 static_assert(sizeof(kCertContextString) % 4 == 0, 117 "Context strings must be a multiple of four bytes long"); 118 119 // NumMessageOffsets gives the size in entries of the table of offsets for a 120 // time protocol message having |num_tags| tags. (Since the length of messages 121 // in the time protocol is known, a message with only one tag does not need a 122 // table of offsets.) 123 constexpr size_t NumMessageOffsets(size_t num_tags) { 124 return num_tags == 0 ? 0 : num_tags - 1; 125 } 126 127 // MessageHeaderLen gives the size in bytes of a message header, which consists 128 // of a tag count, a table of offsets (if there are least two tags), and a list 129 // of 0 or more tags. 130 constexpr size_t MessageHeaderLen(size_t num_tags) { 131 return sizeof(uint32_t) /* tag count */ + 132 sizeof(uint32_t) * NumMessageOffsets(num_tags) /* offsets */ + 133 sizeof(tag_t) * num_tags /* tag values */; 134 } 135 136 // kPaddingLen is the number of padding bytes necessary to make a client request 137 // sufficiently long. 138 constexpr size_t kPaddingLen = 139 kMinRequestSize - (MessageHeaderLen(2) + kNonceLength); 140 141 // Parser decodes requests from a time server client. 142 // 143 // 0 1 2 3 144 // 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 145 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 146 // | Number of tags | 147 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 148 // | Offset, Tag 1 Data | 149 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 150 // | ... | 151 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 152 // | Offset, Tag N Data | 153 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 154 // | Tag 0 | 155 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 156 // | ... | 157 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 158 // | Tag N | 159 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 160 // | Data... | 161 // | (indexed by offsets) | 162 // | | 163 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 164 class Parser { 165 public: 166 // Parses the supplied data. No copies are made, so the data pointed to by 167 // |req| must live as long as the |Parser| object. Callers should check 168 // |is_valid| before calling any other method. 169 Parser(const uint8_t* req, size_t len); 170 171 Parser() = delete; 172 Parser(const Parser&) = delete; 173 Parser& operator=(const Parser&) = delete; 174 175 bool is_valid() const { return is_valid_; } 176 177 // GetTag sets |*out_data| and |*out_len| to reflect the location (within the 178 // data supplied to the constructor) of the tag |tag|, if found. Returns true 179 // if |tag| is found. 180 bool GetTag(const uint8_t** out_data, size_t* out_len, tag_t tag) const; 181 182 // GetFixedLen is a specialization of |GetTag| that returns true only if the 183 // data tagged by |tag| is |expected_len| bytes long. 184 bool GetFixedLen(const uint8_t** out_data, tag_t tag, 185 size_t expected_len) const; 186 187 // Get is a specialization of |GetFixedLen| that expects |tag| to tag a value 188 // of type |T|. The tagged value is assigned to |*out_value|. 189 template <typename T> 190 bool Get(T* out_value, tag_t tag) const; 191 192 private: 193 bool is_valid_ = false; 194 size_t num_tags_; 195 const uint8_t* offsets_; // |NumMessageOffsets| offsets into |data_|. 196 const uint8_t* tags_; // |num_tags_| 32-bit tags. 197 const uint8_t* data_; // |len_| bytes. 198 size_t len_; 199 }; 200 201 // Builder creates a time server response. 202 class Builder { 203 public: 204 Builder() = delete; 205 Builder(const Builder&) = delete; 206 Builder& operator=(const Builder&) = delete; 207 208 // Prepares to write tags and data to a buffer |out| of |out_len| bytes. 209 Builder(uint8_t* out, size_t out_len, size_t num_tags); 210 211 // AddTag adds the tag |tag| to the response, which must be greater than any 212 // previously added tag, and sets |*out_data| to an address where |len| bytes 213 // may be written. Returns true iff the tag may be written. 214 bool AddTag(uint8_t** out_data, tag_t tag, size_t len); 215 216 // AddTagData is a specialization of |AddTag| that copies the |len| bytes 217 // pointed to by |data| into the response. Returns true iff the tag was 218 // written. 219 bool AddTagData(tag_t tag, const uint8_t* data, size_t len); 220 221 // Finish returns true if the response is valid, and sets |out_len| to the 222 // size of the response. After calling |Finish| all subsequent calls will 223 // fail. 224 bool Finish(size_t* out_len); 225 226 private: 227 const size_t num_tags_; 228 const size_t header_len_; 229 uint8_t* const offsets_; 230 uint8_t* const tags_; 231 232 uint8_t* data_; // Offset of next |AddTag|. 233 size_t len_; // Bytes remaining in the output buffer. 234 size_t offset_ = 0; // Offset of data for next tag. 235 236 size_t tag_i_ = 0; // Index of next tag. 237 tag_t previous_tag_; 238 bool have_previous_tag_ = false; 239 240 bool valid_ = false; 241 }; 242 243 // Computes the SHA-512 hash of a Merkle tree leaf, i.e. a client's nonce, as 244 // 0||nonce. |in| is assumed to point to |kNonceLength| bytes and |out| is 245 // assumed to have space for |SHA512_DIGEST_LENGTH| bytes. 246 void HashLeaf(uint8_t* out, const uint8_t* in); 247 248 // Computes the SHA-512 hash of a Merkle tree node as 1||left||right. |left|, 249 // |right|, and |out| are assumed to point to |SHA512_DIGEST_LENGTH| bytes. 250 void HashNode(uint8_t* out, const uint8_t* left, const uint8_t* right); 251 252 } // namespace roughtime 253 254 #endif // SECURITY_ROUGHTIME_PROTOCOL_H_