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