blob: 2b16debbb4c2b2a0770bb22908053a02d5bfc8bf [file] [log] [blame]
/* 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