roughtime.googlesource.com/roughtime.git@v0.0.0-20201210012726-dd529367052d/protocol.cc (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 #include "protocol.h" 16 17 #if defined(__APPLE__) 18 #include <machine/endian.h> 19 #else 20 #include <endian.h> 21 #endif 22 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include <openssl/sha.h> 27 #include <openssl/curve25519.h> 28 29 #include "logging.h" 30 31 namespace roughtime { 32 33 static_assert(BYTE_ORDER == LITTLE_ENDIAN, 34 "This code assumes little-endian processors"); 35 36 // The OpenSSL constants are kept out of the headers to allow consumers to 37 // avoid needing OpenSSL's at build time, but the values should still match. 38 static_assert(kPrivateKeyLength == ED25519_PRIVATE_KEY_LEN, 39 "Private key length mismatch"); 40 static_assert(kPublicKeyLength == ED25519_PUBLIC_KEY_LEN, 41 "Public key length mismatch"); 42 static_assert(kSignatureLength == ED25519_SIGNATURE_LEN, 43 "Signature length mismatch"); 44 45 static void advance(const uint8_t **ptr, size_t *len, size_t bytes) { 46 *ptr += bytes; 47 *len -= bytes; 48 } 49 50 Parser::Parser(const uint8_t *req, size_t len) { 51 if (len < sizeof(uint32_t)) { 52 return; 53 } 54 55 // Read number of tags. 56 uint32_t num_tags_32; 57 memcpy(&num_tags_32, req, sizeof(uint32_t)); 58 num_tags_ = num_tags_32; 59 advance(&req, &len, sizeof(uint32_t)); 60 61 if (0xffff < num_tags_) { // Avoids any subsequent overflows. 62 return; 63 } 64 65 // Validate table of offsets. 66 const size_t num_offsets = NumMessageOffsets(num_tags_); 67 if (len < num_offsets * sizeof(uint32_t)) { 68 return; 69 } 70 offsets_ = req; 71 advance(&req, &len, num_offsets * sizeof(uint32_t)); 72 73 uint32_t previous_offset = 0; 74 for (size_t i = 0; i < num_offsets; i++) { 75 // A tag may have no data. Hence, subsequent offsets may be equal. 76 uint32_t offset; 77 memcpy(&offset, offsets_ + sizeof(uint32_t)*i, sizeof(uint32_t)); 78 79 if (offset < previous_offset || 80 offset % 4 != 0) { 81 return; 82 } 83 previous_offset = offset; 84 } 85 uint32_t last_offset = previous_offset; 86 87 // Validate list of tags. Tags must be in increasing order. 88 if (len < num_tags_ * sizeof(tag_t)) { 89 return; 90 } 91 tags_ = req; 92 advance(&req, &len, num_tags_ * sizeof(tag_t)); 93 94 tag_t previous_tag = 0; 95 for (size_t i = 0; i < num_tags_; i++) { 96 tag_t tag; 97 memcpy(&tag, tags_ + sizeof(tag_t) * i, sizeof(tag_t)); 98 if (i > 0 && tag <= previous_tag) { 99 return; 100 } 101 previous_tag = tag; 102 } 103 104 // Make sure the offset table doesn't point past the end of the data. 105 if (len < last_offset) { 106 return; 107 } 108 109 data_ = req; 110 len_ = len; 111 is_valid_ = true; 112 } 113 114 static int tag_cmp(const void *keyp, const void *memberp) { 115 tag_t key, member; 116 memcpy(&key, keyp, sizeof(tag_t)); 117 memcpy(&member, memberp, sizeof(uint32_t)); 118 119 if (key == member) { 120 return 0; 121 } 122 return key < member ? -1 : 1; 123 } 124 125 bool Parser::GetTag(const uint8_t **out_data, size_t *out_len, 126 tag_t tag) const { 127 uint8_t *tagp = reinterpret_cast<uint8_t *>( 128 bsearch(&tag, tags_, num_tags_, sizeof(tag_t), tag_cmp)); 129 if (tagp == nullptr) { 130 return false; 131 } 132 size_t tag_number = (tagp - tags_) / sizeof(uint32_t); 133 134 uint32_t offset = 0; 135 if (tag_number != 0) { 136 memcpy(&offset, offsets_ + sizeof(uint32_t) * (tag_number - 1), 137 sizeof(uint32_t)); 138 } 139 140 *out_data = data_ + offset; 141 if (tag_number == num_tags_ - 1) { 142 *out_len = len_ - offset; 143 } else { 144 uint32_t next_offset; 145 memcpy(&next_offset, offsets_ + sizeof(uint32_t) * tag_number, 146 sizeof(uint32_t)); 147 *out_len = next_offset - offset; 148 } 149 return true; 150 } 151 152 bool Parser::GetFixedLen(const uint8_t **out_data, tag_t tag, 153 size_t expected_len) const { 154 size_t len; 155 return GetTag(out_data, &len, tag) && len == expected_len; 156 } 157 158 template <typename T> 159 bool Parser::Get(T *out_value, tag_t tag) const { 160 const uint8_t *data; 161 size_t len; 162 if (!GetTag(&data, &len, tag) || 163 len != sizeof(T)) { 164 return false; 165 } 166 memcpy(out_value, data, sizeof(T)); 167 return true; 168 } 169 170 template bool Parser::Get(uint32_t *, tag_t) const; 171 template bool Parser::Get(uint64_t *, tag_t) const; 172 173 Builder::Builder(uint8_t *out, size_t out_len, size_t num_tags) 174 : num_tags_(num_tags), 175 header_len_(MessageHeaderLen(num_tags)), 176 offsets_(out + sizeof(uint32_t)), 177 tags_(out + sizeof(uint32_t) * (1 + NumMessageOffsets(num_tags))) { 178 if (out_len < sizeof(uint32_t) || 179 out_len < header_len_ || 180 0xffff < num_tags) { 181 return; 182 } 183 184 const uint32_t num_tags_32 = num_tags; 185 memcpy(out, &num_tags_32, sizeof(uint32_t)); 186 187 data_ = out + header_len_; 188 len_ = out_len - header_len_; 189 valid_ = true; 190 } 191 192 bool Builder::AddTag(uint8_t **out_data, tag_t tag, size_t len) { 193 if (!valid_ || 194 len%4 != 0 || 195 len_ < len || 196 tag_i_ >= num_tags_ || 197 (have_previous_tag_ && tag <= previous_tag_)) { 198 return false; 199 } 200 201 memcpy(tags_ + sizeof(uint32_t)*tag_i_, &tag, sizeof(tag_t)); 202 if (tag_i_ > 0) { 203 const uint32_t offset_32 = offset_; 204 memcpy(offsets_ + sizeof(uint32_t) * (tag_i_ - 1), &offset_32, 205 sizeof(uint32_t)); 206 } 207 tag_i_++; 208 previous_tag_ = tag; 209 have_previous_tag_ = true; 210 211 *out_data = data_; 212 213 offset_ += len; 214 len_ -= len; 215 data_ += len; 216 217 return true; 218 } 219 220 bool Builder::AddTagData(tag_t tag, const uint8_t *data, size_t len) { 221 uint8_t *out; 222 if (!AddTag(&out, tag, len)) { 223 return false; 224 } 225 memcpy(out, data, len); 226 return true; 227 } 228 229 bool Builder::Finish(size_t *out_len) { 230 if (!valid_ || tag_i_ != num_tags_) { 231 return false; 232 } 233 *out_len = header_len_ + offset_; 234 valid_ = false; 235 return true; 236 } 237 238 constexpr uint8_t kHashLeafTweak[] = {0x00}; 239 constexpr uint8_t kHashNodeTweak[] = {0x01}; 240 241 void HashLeaf(uint8_t *out, const uint8_t *in) { 242 SHA512_CTX ctx; 243 SHA512_Init(&ctx); 244 SHA512_Update(&ctx, kHashLeafTweak, 1); 245 SHA512_Update(&ctx, in, kNonceLength); 246 SHA512_Final(out, &ctx); 247 } 248 249 void HashNode(uint8_t *out, const uint8_t *left, const uint8_t *right) { 250 SHA512_CTX ctx; 251 SHA512_Init(&ctx); 252 SHA512_Update(&ctx, kHashNodeTweak, 1); 253 SHA512_Update(&ctx, left, SHA512_DIGEST_LENGTH); 254 SHA512_Update(&ctx, right, SHA512_DIGEST_LENGTH); 255 SHA512_Final(out, &ctx); 256 } 257 258 } // namespace roughtime