blob: aad38c5aad08c76cfd35d87b667c30fbcbbb7720 [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 <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include <openssl/curve25519.h>
#include <openssl/rand.h>
#include "open_source_fillins.h"
#include "server.h"
namespace roughtime {
TEST(CreateCertificate, Create) {
uint8_t delegated_private_key[ED25519_PRIVATE_KEY_LEN]; // Not used.
uint8_t delegated_public_key[ED25519_PUBLIC_KEY_LEN];
uint8_t root_private_key[ED25519_PRIVATE_KEY_LEN];
uint8_t root_public_key[ED25519_PUBLIC_KEY_LEN];
ED25519_keypair(delegated_public_key, delegated_private_key);
ED25519_keypair(root_public_key, root_private_key);
uint8_t cert[kCertSize];
EXPECT_TRUE(
CreateCertificate(cert, root_private_key, 0, 1, delegated_public_key));
Parser cert_parser(cert, sizeof(cert));
const uint8_t* delegation;
const uint8_t* signature;
EXPECT_TRUE(
cert_parser.GetFixedLen(&delegation, kTagDELE, kToBeSignedCertSize));
EXPECT_TRUE(
cert_parser.GetFixedLen(&signature, kTagSIG, ED25519_SIGNATURE_LEN));
Parser delegation_parser(delegation, kToBeSignedCertSize);
uint64_t mint, maxt;
const uint8_t* pubk;
EXPECT_TRUE(delegation_parser.Get(&mint, kTagMINT));
EXPECT_TRUE(delegation_parser.Get(&maxt, kTagMAXT));
EXPECT_TRUE(
delegation_parser.GetFixedLen(&pubk, kTagPUBK, ED25519_PUBLIC_KEY_LEN));
EXPECT_EQ(0, mint);
EXPECT_EQ(1, maxt);
EXPECT_EQ(0,
memcmp(pubk, delegated_public_key, sizeof(delegated_public_key)));
uint8_t verify[sizeof(kCertContextString) + kToBeSignedCertSize];
memcpy(verify, kCertContextString, sizeof(kCertContextString));
memcpy(verify + sizeof(kCertContextString), delegation, kToBeSignedCertSize);
EXPECT_TRUE(
ED25519_verify(verify, sizeof(verify), signature, root_public_key));
}
TEST(Tree, OneNode) {
std::unique_ptr<Tree> tree(new Tree);
uint8_t nonce[kNonceLength];
RAND_bytes(nonce, sizeof(nonce));
tree->AddLeaf(0, nonce);
tree->Build(1);
uint8_t hash[kNonceLength];
HashLeaf(hash, nonce);
EXPECT_EQ(0, memcmp(hash, tree->GetRoot(), kNonceLength));
EXPECT_EQ(0, tree->GetPathLength());
}
TEST(Tree, ManyNodes) {
std::unique_ptr<Tree> tree(new Tree);
size_t sizes[] = {2, 3, 4, 5, 6, kBatchSize - 1, kBatchSize};
for (size_t i = 0; i < arraysize(sizes); i++) {
size_t size = sizes[i];
uint8_t nonces[kBatchSize][kNonceLength];
for (size_t j = 0; j < size; ++j) {
RAND_bytes(nonces[j], sizeof(nonces[j]));
tree->AddLeaf(j, nonces[j]);
}
tree->Build(size);
// Verify the inclusion of each nonce.
for (size_t j = 0; j < size; ++j) {
uint8_t hash[kNonceLength];
HashLeaf(hash, nonces[j]);
uint8_t path[kBatchSize][kNonceLength];
tree->GetPath(reinterpret_cast<uint8_t*>(path), j);
size_t index = j;
for (size_t elem = 0; elem < tree->GetPathLength(); elem++) {
if (index % 2 == 0) {
HashNode(hash, hash, path[elem]);
} else {
HashNode(hash, path[elem], hash);
}
index /= 2;
}
ASSERT_EQ(0, index);
EXPECT_EQ(0, memcmp(hash, tree->GetRoot(), kNonceLength));
}
}
}
class DummyTimeSource : public TimeSource {
public:
~DummyTimeSource() override {}
std::pair<uint64_t, uint32_t> Now() override {
return std::make_pair(1000000000, 0);
}
};
// MakeServer is a helper function to create a new server. The server's public
// key is written to |*root_public_key|, but its certificate remains hidden.
static std::unique_ptr<Server> MakeServer(uint8_t* root_public_key) {
auto identity = std::unique_ptr<Identity>(new Identity());
uint8_t delegated_private_key[ED25519_PRIVATE_KEY_LEN]; // Not used.
uint8_t delegated_public_key[ED25519_PUBLIC_KEY_LEN];
uint8_t root_private_key[ED25519_PRIVATE_KEY_LEN];
ED25519_keypair(delegated_public_key, delegated_private_key);
ED25519_keypair(root_public_key, root_private_key);
CreateCertificate(identity->certificate, root_private_key, 1000000000,
2000000000 /* 2033-05-17 */, delegated_public_key);
memcpy(identity->private_key, delegated_private_key,
sizeof(delegated_private_key));
std::unique_ptr<TimeSource> time_source(new DummyTimeSource);
Server* server = new Server(std::move(identity), std::move(time_source));
return std::unique_ptr<Server>(server);
}
// VerifyResponse is a helper function to verify a that the supplied |response|
// is valid and includes the supplied |nonce|.
static void VerifyResponse(const uint8_t* nonce, const uint8_t* response,
size_t len) {
// Parse the top-level response.
Parser parser(response, len);
ASSERT_TRUE(parser.is_valid());
uint8_t to_be_verified_with_context[kToBeSignedSize + sizeof(kContextString)];
const uint8_t* to_be_verified;
const uint8_t* signature;
const uint8_t* cert;
const uint8_t* path;
uint32_t index;
size_t path_len;
size_t path_elems;
ASSERT_TRUE(parser.GetFixedLen(&to_be_verified, kTagSREP, kToBeSignedSize));
ASSERT_TRUE(parser.GetFixedLen(&signature, kTagSIG, ED25519_SIGNATURE_LEN));
ASSERT_TRUE(parser.GetFixedLen(&cert, kTagCERT, kCertSize));
ASSERT_TRUE(parser.Get(&index, kTagINDX));
ASSERT_TRUE(parser.GetTag(&path, &path_len, kTagPATH));
// Parse the signed portion.
Parser srep_parser(to_be_verified, kToBeSignedSize);
ASSERT_TRUE(srep_parser.is_valid());
const uint8_t* root;
uint64_t time;
ASSERT_TRUE(srep_parser.GetFixedLen(&root, kTagROOT, kNonceLength));
ASSERT_TRUE(srep_parser.Get(&time, kTagMIDP));
// Parse the certificate.
Parser cert_parser(cert, kCertSize);
ASSERT_TRUE(cert_parser.is_valid());
const uint8_t* delegation;
ASSERT_TRUE(
cert_parser.GetFixedLen(&delegation, kTagDELE, kToBeSignedCertSize));
// Parse the delegation.
Parser delegation_parser(delegation, kToBeSignedCertSize);
ASSERT_TRUE(delegation_parser.is_valid());
const uint8_t* public_key;
uint64_t maxt, mint;
ASSERT_TRUE(delegation_parser.GetFixedLen(&public_key, kTagPUBK,
ED25519_PUBLIC_KEY_LEN));
ASSERT_TRUE(delegation_parser.Get(&mint, kTagMINT));
ASSERT_TRUE(delegation_parser.Get(&maxt, kTagMAXT));
// Verify that delegation is valid for the supplied time.
EXPECT_GE(time, mint);
EXPECT_LE(time, maxt);
// Verify the signature.
memcpy(to_be_verified_with_context, kContextString, sizeof(kContextString));
memcpy(to_be_verified_with_context + sizeof(kContextString), to_be_verified,
kToBeSignedSize);
EXPECT_TRUE(ED25519_verify(to_be_verified_with_context,
sizeof(to_be_verified_with_context), signature,
public_key));
// Verify the inclusion of |nonce|.
ASSERT_EQ(0, path_len % kNonceLength);
path_elems = path_len / kNonceLength;
uint8_t hash[kNonceLength];
HashLeaf(hash, nonce);
for (size_t i = 0; i < path_elems; i++) {
if (index % 2 == 1) {
HashNode(hash, path, hash);
} else {
HashNode(hash, hash, path);
}
path += kNonceLength;
index /= 2;
}
EXPECT_EQ(0, memcmp(hash, root, kNonceLength));
}
// MakeRequest, a helper function, writes a new client request with the given
// |nonce| to |*request|.
void MakeRequest(uint8_t* request, uint8_t* nonce) {
Builder builder(request, kMinRequestSize, 2);
RAND_bytes(nonce, sizeof(nonce));
ASSERT_TRUE(builder.AddTagData(kTagNONC, nonce, kNonceLength));
uint8_t* padding;
ASSERT_TRUE(builder.AddTag(&padding, kTagPAD, kPaddingLen));
size_t len;
ASSERT_TRUE(builder.Finish(&len));
ASSERT_EQ(kMinRequestSize, len);
}
TEST(Server, BadRequest) {
uint8_t root_public_key[ED25519_PUBLIC_KEY_LEN];
auto server = MakeServer(root_public_key);
uint8_t garbage[kMinRequestSize];
memset(garbage, 'a', sizeof(garbage));
EXPECT_FALSE(server->AddRequest(garbage, sizeof(garbage)));
}
TEST(Server, GoodRequests) {
uint8_t root_public_key[ED25519_PUBLIC_KEY_LEN];
auto server = MakeServer(root_public_key);
size_t batch_sizes[] = {1, 2, 3, 4, 5, 6, kBatchSize - 1, kBatchSize};
for (size_t i = 0; i < arraysize(batch_sizes); ++i) {
std::unique_ptr<uint8_t[]> requests(
new uint8_t[kBatchSize * kMinRequestSize]);
std::unique_ptr<uint8_t[]> nonces(new uint8_t[kBatchSize * kNonceLength]);
memset(nonces.get(), 0, kBatchSize * kNonceLength);
for (size_t j = 0; j < batch_sizes[i]; ++j) {
MakeRequest(&requests[j * kMinRequestSize], &nonces[j * kNonceLength]);
EXPECT_TRUE(
server->AddRequest(&requests[j * kMinRequestSize], kMinRequestSize));
}
EXPECT_TRUE(server->Sign());
for (size_t j = 0; j < batch_sizes[i]; ++j) {
uint8_t response[kMaxResponseSize];
size_t response_len;
EXPECT_TRUE(server->MakeResponse(response, &response_len, j));
VerifyResponse(&nonces[j * kNonceLength], response, response_len);
}
server->Reset();
}
}
TEST(Server, MixedGoodAndBadRequests) {
uint8_t root_public_key[ED25519_PUBLIC_KEY_LEN];
auto server = MakeServer(root_public_key);
std::unique_ptr<uint8_t[]> requests(
new uint8_t[kBatchSize * kMinRequestSize]);
std::unique_ptr<uint8_t[]> nonces(new uint8_t[kBatchSize * kNonceLength]);
memset(nonces.get(), 0, kBatchSize * kNonceLength);
MakeRequest(&requests[0 * kMinRequestSize], &nonces[0 * kNonceLength]);
MakeRequest(&requests[1 * kMinRequestSize], &nonces[1 * kNonceLength]);
MakeRequest(&requests[2 * kMinRequestSize], &nonces[2 * kNonceLength]);
memset(&requests[1 * kMinRequestSize], 'a', kMinRequestSize);
EXPECT_TRUE(
server->AddRequest(&requests[0 * kMinRequestSize], kMinRequestSize));
EXPECT_FALSE(
server->AddRequest(&requests[1 * kMinRequestSize], kMinRequestSize));
EXPECT_TRUE(
server->AddRequest(&requests[2 * kMinRequestSize], kMinRequestSize));
server->Sign();
uint8_t response[kMaxResponseSize];
size_t response_len;
EXPECT_TRUE(server->MakeResponse(response, &response_len, 0));
VerifyResponse(&nonces[0 * kNonceLength], response, response_len);
EXPECT_TRUE(server->MakeResponse(response, &response_len, 1));
// index #1 -> nonce #2
VerifyResponse(&nonces[2 * kNonceLength], response, response_len);
}
} // namespace roughtime