| /* 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. */ | |
| #include "protocol.h" | |
| #if defined(__APPLE__) | |
| #include <machine/endian.h> | |
| #else | |
| #include <endian.h> | |
| #endif | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <openssl/sha.h> | |
| #include <openssl/curve25519.h> | |
| #include "logging.h" | |
| namespace roughtime { | |
| static_assert(BYTE_ORDER == LITTLE_ENDIAN, | |
| "This code assumes little-endian processors"); | |
| // The OpenSSL constants are kept out of the headers to allow consumers to | |
| // avoid needing OpenSSL's at build time, but the values should still match. | |
| static_assert(kPrivateKeyLength == ED25519_PRIVATE_KEY_LEN, | |
| "Private key length mismatch"); | |
| static_assert(kPublicKeyLength == ED25519_PUBLIC_KEY_LEN, | |
| "Public key length mismatch"); | |
| static_assert(kSignatureLength == ED25519_SIGNATURE_LEN, | |
| "Signature length mismatch"); | |
| static void advance(const uint8_t **ptr, size_t *len, size_t bytes) { | |
| *ptr += bytes; | |
| *len -= bytes; | |
| } | |
| Parser::Parser(const uint8_t *req, size_t len) { | |
| if (len < sizeof(uint32_t)) { | |
| return; | |
| } | |
| // Read number of tags. | |
| uint32_t num_tags_32; | |
| memcpy(&num_tags_32, req, sizeof(uint32_t)); | |
| num_tags_ = num_tags_32; | |
| advance(&req, &len, sizeof(uint32_t)); | |
| if (0xffff < num_tags_) { // Avoids any subsequent overflows. | |
| return; | |
| } | |
| // Validate table of offsets. | |
| const size_t num_offsets = NumMessageOffsets(num_tags_); | |
| if (len < num_offsets * sizeof(uint32_t)) { | |
| return; | |
| } | |
| offsets_ = req; | |
| advance(&req, &len, num_offsets * sizeof(uint32_t)); | |
| uint32_t previous_offset = 0; | |
| for (size_t i = 0; i < num_offsets; i++) { | |
| // A tag may have no data. Hence, subsequent offsets may be equal. | |
| uint32_t offset; | |
| memcpy(&offset, offsets_ + sizeof(uint32_t)*i, sizeof(uint32_t)); | |
| if (offset < previous_offset || | |
| offset % 4 != 0) { | |
| return; | |
| } | |
| previous_offset = offset; | |
| } | |
| uint32_t last_offset = previous_offset; | |
| // Validate list of tags. Tags must be in increasing order. | |
| if (len < num_tags_ * sizeof(tag_t)) { | |
| return; | |
| } | |
| tags_ = req; | |
| advance(&req, &len, num_tags_ * sizeof(tag_t)); | |
| tag_t previous_tag = 0; | |
| for (size_t i = 0; i < num_tags_; i++) { | |
| tag_t tag; | |
| memcpy(&tag, tags_ + sizeof(tag_t) * i, sizeof(tag_t)); | |
| if (i > 0 && tag <= previous_tag) { | |
| return; | |
| } | |
| previous_tag = tag; | |
| } | |
| // Make sure the offset table doesn't point past the end of the data. | |
| if (len < last_offset) { | |
| return; | |
| } | |
| data_ = req; | |
| len_ = len; | |
| is_valid_ = true; | |
| } | |
| static int tag_cmp(const void *keyp, const void *memberp) { | |
| tag_t key, member; | |
| memcpy(&key, keyp, sizeof(tag_t)); | |
| memcpy(&member, memberp, sizeof(uint32_t)); | |
| if (key == member) { | |
| return 0; | |
| } | |
| return key < member ? -1 : 1; | |
| } | |
| bool Parser::GetTag(const uint8_t **out_data, size_t *out_len, | |
| tag_t tag) const { | |
| uint8_t *tagp = reinterpret_cast<uint8_t *>( | |
| bsearch(&tag, tags_, num_tags_, sizeof(tag_t), tag_cmp)); | |
| if (tagp == nullptr) { | |
| return false; | |
| } | |
| size_t tag_number = (tagp - tags_) / sizeof(uint32_t); | |
| uint32_t offset = 0; | |
| if (tag_number != 0) { | |
| memcpy(&offset, offsets_ + sizeof(uint32_t) * (tag_number - 1), | |
| sizeof(uint32_t)); | |
| } | |
| *out_data = data_ + offset; | |
| if (tag_number == num_tags_ - 1) { | |
| *out_len = len_ - offset; | |
| } else { | |
| uint32_t next_offset; | |
| memcpy(&next_offset, offsets_ + sizeof(uint32_t) * tag_number, | |
| sizeof(uint32_t)); | |
| *out_len = next_offset - offset; | |
| } | |
| return true; | |
| } | |
| bool Parser::GetFixedLen(const uint8_t **out_data, tag_t tag, | |
| size_t expected_len) const { | |
| size_t len; | |
| return GetTag(out_data, &len, tag) && len == expected_len; | |
| } | |
| template <typename T> | |
| bool Parser::Get(T *out_value, tag_t tag) const { | |
| const uint8_t *data; | |
| size_t len; | |
| if (!GetTag(&data, &len, tag) || | |
| len != sizeof(T)) { | |
| return false; | |
| } | |
| memcpy(out_value, data, sizeof(T)); | |
| return true; | |
| } | |
| template bool Parser::Get(uint32_t *, tag_t) const; | |
| template bool Parser::Get(uint64_t *, tag_t) const; | |
| Builder::Builder(uint8_t *out, size_t out_len, size_t num_tags) | |
| : num_tags_(num_tags), | |
| header_len_(MessageHeaderLen(num_tags)), | |
| offsets_(out + sizeof(uint32_t)), | |
| tags_(out + sizeof(uint32_t) * (1 + NumMessageOffsets(num_tags))) { | |
| if (out_len < sizeof(uint32_t) || | |
| out_len < header_len_ || | |
| 0xffff < num_tags) { | |
| return; | |
| } | |
| const uint32_t num_tags_32 = num_tags; | |
| memcpy(out, &num_tags_32, sizeof(uint32_t)); | |
| data_ = out + header_len_; | |
| len_ = out_len - header_len_; | |
| valid_ = true; | |
| } | |
| bool Builder::AddTag(uint8_t **out_data, tag_t tag, size_t len) { | |
| if (!valid_ || | |
| len%4 != 0 || | |
| len_ < len || | |
| tag_i_ >= num_tags_ || | |
| (have_previous_tag_ && tag <= previous_tag_)) { | |
| return false; | |
| } | |
| memcpy(tags_ + sizeof(uint32_t)*tag_i_, &tag, sizeof(tag_t)); | |
| if (tag_i_ > 0) { | |
| const uint32_t offset_32 = offset_; | |
| memcpy(offsets_ + sizeof(uint32_t) * (tag_i_ - 1), &offset_32, | |
| sizeof(uint32_t)); | |
| } | |
| tag_i_++; | |
| previous_tag_ = tag; | |
| have_previous_tag_ = true; | |
| *out_data = data_; | |
| offset_ += len; | |
| len_ -= len; | |
| data_ += len; | |
| return true; | |
| } | |
| bool Builder::AddTagData(tag_t tag, const uint8_t *data, size_t len) { | |
| uint8_t *out; | |
| if (!AddTag(&out, tag, len)) { | |
| return false; | |
| } | |
| memcpy(out, data, len); | |
| return true; | |
| } | |
| bool Builder::Finish(size_t *out_len) { | |
| if (!valid_ || tag_i_ != num_tags_) { | |
| return false; | |
| } | |
| *out_len = header_len_ + offset_; | |
| valid_ = false; | |
| return true; | |
| } | |
| constexpr uint8_t kHashLeafTweak[] = {0x00}; | |
| constexpr uint8_t kHashNodeTweak[] = {0x01}; | |
| void HashLeaf(uint8_t *out, const uint8_t *in) { | |
| SHA512_CTX ctx; | |
| SHA512_Init(&ctx); | |
| SHA512_Update(&ctx, kHashLeafTweak, 1); | |
| SHA512_Update(&ctx, in, kNonceLength); | |
| SHA512_Final(out, &ctx); | |
| } | |
| void HashNode(uint8_t *out, const uint8_t *left, const uint8_t *right) { | |
| SHA512_CTX ctx; | |
| SHA512_Init(&ctx); | |
| SHA512_Update(&ctx, kHashNodeTweak, 1); | |
| SHA512_Update(&ctx, left, SHA512_DIGEST_LENGTH); | |
| SHA512_Update(&ctx, right, SHA512_DIGEST_LENGTH); | |
| SHA512_Final(out, &ctx); | |
| } | |
| } // namespace roughtime |