140 lines
4.3 KiB
C++
140 lines
4.3 KiB
C++
/* -*- 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_
|