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