| /* 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 |