Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef PK11GTEST_JSON_H_
#define PK11GTEST_JSON_H_
#include <functional>
#include <iostream>
#include <vector>
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "secoidt.h"
// If we make a few assumptions about the file, parsing JSON can be easy.
// This is not a full parser, it only works on a narrow set of inputs.
class JsonReader {
public:
JsonReader(const std::string& n);
void next() { i_++; }
uint8_t peek() {
TopUp();
return buf_[i_];
}
uint8_t take() {
uint8_t v = peek();
next();
return v;
}
// No input checking, overflow protection, or any safety.
// Returns 0 if there isn't a number here rather than aborting.
uint64_t ReadInt();
// No input checking, no unicode, no escaping (not even \"), just read ASCII.
std::string ReadString();
std::string ReadLabel();
std::vector<uint8_t> ReadHex();
SECOidTag ReadHash();
SECStatus ReadSECStatus();
bool ReadBool();
bool NextItem(uint8_t h = '{', uint8_t t = '}');
bool NextItemArray() { return NextItem('[', ']'); }
void SkipValue();
private:
void TopUp();
void SkipWhitespace();
// This only handles lowercase.
uint8_t Hex(uint8_t c);
ScopedPRFileDesc f_;
uint8_t buf_[4096];
size_t available_;
size_t i_;
};
// The way this is expected to work is that this reads the header, then
// passes off the content of each "testGroups" member to `group_handler`.
// That function processes any attributes in that structure, calls
// `WycheproofReadTests` to load individual cases and runs those tests.
void WycheproofHeader(const std::string& name, const std::string& algorithm,
const std::string& schema,
std::function<void(JsonReader& r)> group_handler);
template <typename T>
struct id {
typedef T type;
};
template <typename T>
using nondeduced = typename id<T>::type;
// Read into a block of test cases, handling standard attributes on Wycheproof
// tests.
//
// `T` needs `uint64_t id` and `bool valid` fields.
// `attr_reader` is responsible for reading values into the test case struct.
// `acceptable` determines whether a test marked "acceptable" is valid by
// default. `process_flags` allows for processing the flags on an entry.
//
// Note that this gathers all tests into a vector rather than running tests as
// they arrive. This is necessary because the testGroup JSON struct might have
// fields that haven't been read when this list is constructed (it doesn't in
// the current files, but this is not guaranteed). Tests can only run after all
// of the group attributes have been read and processed.
template <typename T>
void WycheproofReadTests(
JsonReader& r, std::vector<T>* tests,
const std::function<nondeduced<void(T&, const std::string&, JsonReader&)>>&
attr_reader,
bool acceptable = true,
const std::function<nondeduced<void(T&, const std::string&,
const std::vector<std::string>&)>>&
process_flags = nullptr) {
while (r.NextItemArray()) {
T tc;
std::string comment;
std::string result;
std::vector<std::string> flags;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "tcId") {
tc.id = r.ReadInt();
} else if (n == "result") {
result = r.ReadString();
} else if (n == "comment") {
comment = r.ReadString();
} else if (n == "flags") {
while (r.NextItemArray()) {
flags.push_back(r.ReadString());
}
} else {
ASSERT_NO_FATAL_FAILURE(attr_reader(tc, n, r));
}
}
tc.valid = (result == "valid") || (acceptable && result == "acceptable");
if (process_flags) {
process_flags(tc, result, flags);
}
std::cout << " tc " << tc.id << ": " << comment << " [" << result;
for (auto& f : flags) {
std::cout << ", " << f;
}
std::cout << "] expect " << (tc.valid ? "success" : "failure") << std::endl;
tests->push_back(tc);
}
}
#endif // PK11GTEST_JSON_H_