| /* 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 <errno.h> | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #include <openssl/curve25519.h> | |
| #include "logging.h" | |
| #include "protocol.h" | |
| #include "server.h" | |
| namespace roughtime { | |
| static_assert(kMaxRecvPacketSize % 4 == 0, | |
| "kMaxRecvPacketSize must be a multiple of four"); | |
| static_assert(kBatchSize != 0 && (kBatchSize & (kBatchSize - 1)) == 0, | |
| "kBatchSize must be a power of two"); | |
| static_assert(1u << kBatchSizeLog2 == kBatchSize, | |
| "kBatchSizeLog2 is incorrect"); | |
| static_assert(kMinRequestSize <= kMaxRecvPacketSize, | |
| "Miniumum request size must be <= to the maximum packet size"); | |
| static_assert(kMaxResponseSize <= kMinRequestSize, | |
| "This design could be a DDoS amplifier"); | |
| static_assert(ED25519_SIGNATURE_LEN == 64, "crypto constant mismatch"); | |
| static_assert(ED25519_PUBLIC_KEY_LEN == 32, "crypto constant mismatch"); | |
| static_assert(ED25519_PRIVATE_KEY_LEN == 64, "crypto constant mismatch"); | |
| Server::Server(std::unique_ptr<Identity> identity, | |
| std::unique_ptr<TimeSource> time_source) | |
| : time_source_(std::move(time_source)), | |
| identity_(std::move(identity)), | |
| num_leaves_(0), | |
| to_be_signed_(to_be_signed_with_context_ + sizeof(kContextString)) { | |
| memcpy(to_be_signed_with_context_, kContextString, sizeof(kContextString)); | |
| } | |
| bool Server::AddRequest(const uint8_t *packet, size_t len) { | |
| ROUGHTIME_DCHECK_LE(num_leaves_, kBatchSize); | |
| if (len < kMinRequestSize) { | |
| return false; | |
| } | |
| Parser request(packet, len); | |
| if (!request.is_valid()) { | |
| return false; | |
| } | |
| const uint8_t *nonce; | |
| if (!request.GetFixedLen(&nonce, kTagNONC, kNonceLength)) { | |
| return false; | |
| } | |
| // kTagPAD lives here too, but we don't bother to check for it. The check | |
| // above against |kMinRequestSize| is sufficient. | |
| tree_.AddLeaf(num_leaves_++, nonce); | |
| return true; | |
| } | |
| bool Server::Sign() { | |
| ROUGHTIME_DCHECK_GT(num_leaves_, 0ul); | |
| // The signature is over the root hash and the timestamp---that's it! | |
| tree_.Build(num_leaves_); | |
| const auto interval = time_source_->Now(); | |
| const rough_time_t now = interval.first; | |
| const uint32_t radius = interval.second; | |
| Builder to_be_signed(to_be_signed_, kToBeSignedSize, 3); | |
| size_t to_be_signed_len; | |
| static_assert(kTagRADI < kTagMIDP, "Tags must be written in order"); | |
| static_assert(kTagMIDP < kTagROOT, "Tags must be written in order"); | |
| if (!to_be_signed.AddTagData(kTagRADI, | |
| reinterpret_cast<const uint8_t *>(&radius), | |
| sizeof(radius)) || | |
| !to_be_signed.AddTagData( | |
| kTagMIDP, reinterpret_cast<const uint8_t *>(&now), sizeof(now)) || | |
| !to_be_signed.AddTagData(kTagROOT, tree_.GetRoot(), kNonceLength) || | |
| !to_be_signed.Finish(&to_be_signed_len)) { | |
| ROUGHTIME_LOG(ERROR) << "failed to construct to_be_signed"; | |
| return false; | |
| } | |
| ROUGHTIME_CHECK_EQ(to_be_signed_len, kToBeSignedSize); | |
| if (!ED25519_sign(signature_, to_be_signed_with_context_, | |
| sizeof(to_be_signed_with_context_), | |
| identity_->private_key)) { | |
| ROUGHTIME_LOG(ERROR) << "signature failure"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool Server::MakeResponse(uint8_t *out_response, size_t *out_len, | |
| uint32_t index) { | |
| ROUGHTIME_DCHECK_LT(index, num_leaves_); | |
| static_assert(kMaxResponseSize <= kMaxRecvPacketSize, | |
| "Receive buffers are too small to use as send buffers"); | |
| Builder response(out_response, kMaxResponseSize, 5); | |
| static_assert(kTagSIG < kTagPATH, "Tags must be written in order"); | |
| static_assert(kTagPATH < kTagSREP, "Tags must be written in order"); | |
| static_assert(kTagSREP < kTagCERT, "Tags must be written in order"); | |
| static_assert(kTagCERT < kTagINDX, "Tags must be written in order"); | |
| uint8_t *path; | |
| uint8_t *pindex = reinterpret_cast<uint8_t *>(&index); | |
| if (!response.AddTagData(kTagSIG, signature_, sizeof(signature_)) || | |
| !response.AddTag(&path, kTagPATH, kNonceLength * tree_.GetPathLength()) || | |
| !response.AddTagData(kTagSREP, to_be_signed_, kToBeSignedSize) || | |
| !response.AddTagData(kTagCERT, identity_->certificate, kCertSize) || | |
| !response.AddTagData(kTagINDX, pindex, sizeof(index)) || | |
| !response.Finish(out_len)) { | |
| ROUGHTIME_LOG(ERROR) << "failed to construct response"; | |
| return false; | |
| } | |
| tree_.GetPath(path, index); | |
| return true; | |
| } | |
| // static | |
| bool CreateCertificate(uint8_t out_cert[kCertSize], | |
| const uint8_t root_private_key[ED25519_PRIVATE_KEY_LEN], | |
| rough_time_t start_time, rough_time_t end_time, | |
| const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]) { | |
| ROUGHTIME_CHECK_LT(start_time, end_time); | |
| uint8_t to_be_signed_bytes[sizeof(kCertContextString) + kToBeSignedCertSize]; | |
| size_t to_be_signed_len; | |
| memcpy(to_be_signed_bytes, kCertContextString, sizeof(kCertContextString)); | |
| Builder to_be_signed(to_be_signed_bytes + sizeof(kCertContextString), | |
| kToBeSignedCertSize, 3); | |
| static_assert(kTagPUBK < kTagMINT, "Tags must be written in order"); | |
| static_assert(kTagMINT < kTagMAXT, "Tags must be written in order"); | |
| if (!to_be_signed.AddTagData(kTagPUBK, public_key, ED25519_PUBLIC_KEY_LEN) || | |
| !to_be_signed.AddTagData(kTagMINT, | |
| reinterpret_cast<uint8_t *>(&start_time), | |
| sizeof(start_time)) || | |
| !to_be_signed.AddTagData(kTagMAXT, reinterpret_cast<uint8_t *>(&end_time), | |
| sizeof(end_time)) || | |
| !to_be_signed.Finish(&to_be_signed_len)) { | |
| ROUGHTIME_LOG(ERROR) << "failed to construct signed portion of certificate"; | |
| return false; | |
| } | |
| ROUGHTIME_CHECK_EQ(to_be_signed_len, kToBeSignedCertSize); | |
| uint8_t signature[ED25519_SIGNATURE_LEN]; | |
| if (!ED25519_sign(signature, to_be_signed_bytes, sizeof(to_be_signed_bytes), | |
| root_private_key)) { | |
| ROUGHTIME_LOG(ERROR) << "failed to sign certificate"; | |
| return false; | |
| } | |
| size_t cert_len; | |
| Builder cert(out_cert, kCertSize, 2); | |
| static_assert(kTagSIG < kTagDELE, "Tags must be written in order"); | |
| if (!cert.AddTagData(kTagSIG, signature, sizeof(signature)) || | |
| !cert.AddTagData(kTagDELE, | |
| to_be_signed_bytes + sizeof(kCertContextString), | |
| to_be_signed_len) || | |
| !cert.Finish(&cert_len)) { | |
| ROUGHTIME_LOG(ERROR) << "failed to construct certificate"; | |
| return false; | |
| } | |
| ROUGHTIME_CHECK_EQ(cert_len, kCertSize); | |
| return true; | |
| } | |
| void Tree::Build(size_t num_nodes) { | |
| ROUGHTIME_DCHECK_GT(num_nodes, 0ul); | |
| size_t level; | |
| for (level = 0; num_nodes > 1; level++, num_nodes /= 2) { | |
| // Even out the level with a dummy node, if need be. Use an existing node | |
| // in |tree_|, to simplify analysis that we are not inadvertently signing | |
| // other messages. | |
| if (num_nodes % 2 == 1) { | |
| memcpy(tree_[level][num_nodes], tree_[level][0], kNonceLength); | |
| num_nodes++; | |
| } | |
| for (size_t i = 0; i < num_nodes; i += 2) { | |
| HashNode(tree_[level + 1][i / 2], tree_[level][i], tree_[level][i + 1]); | |
| } | |
| } | |
| ROUGHTIME_DCHECK_EQ(1ul, num_nodes); // Root node. | |
| levels_ = level + 1; | |
| } | |
| void Tree::GetPath(uint8_t *out_path, size_t index) { | |
| // At the lowest level, the client knows its own leaf hash, so send it only | |
| // that leaf's sibling, and so on up the tree. | |
| for (size_t level = 0; level < levels_ - 1; level++) { | |
| if (index % 2 == 1) { | |
| memcpy(out_path, tree_[level][index - 1], kNonceLength); | |
| } else { | |
| memcpy(out_path, tree_[level][index + 1], kNonceLength); | |
| } | |
| out_path += kNonceLength; | |
| index /= 2; | |
| } | |
| ROUGHTIME_DCHECK_EQ(0ul, index); | |
| } | |
| BrokenReplyGenerator::~BrokenReplyGenerator() {} | |
| uint16_t BrokenReplyGenerator::probability_1024() const { | |
| return probability_1024_; | |
| } | |
| void BrokenReplyGenerator::set_probability_1024(uint16_t probability) { | |
| probability_1024_ = probability; | |
| } | |
| } // namespace roughtime |