/* -*- 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/. */
#include "json_reader.h"
#include "pk11pub.h"
JsonReader::JsonReader(
const std::string& n) : buf_(), available_(0), i_(0) {
f_.reset(PR_Open(n.c_str(), PR_RDONLY, 00600));
EXPECT_TRUE(f_) <<
"error opening vectors from: " << n;
buf_[0] = 0;
}
uint64_t JsonReader::ReadInt() {
SkipWhitespace();
uint8_t c = peek();
uint64_t v = 0;
while (c >=
'0' && c <=
'9') {
v = v * 10 + c -
'0';
next();
c = peek();
}
return v;
}
// No input checking, no unicode, no escaping (not even \"), just read ASCII.
std::string JsonReader::ReadString() {
SkipWhitespace();
if (peek() !=
'"') {
return "";
}
next();
std::string s;
uint8_t c = take();
while (c !=
'"') {
s.push_back(c);
c = take();
}
return s;
}
std::string JsonReader::ReadLabel() {
std::string s = ReadString();
SkipWhitespace();
EXPECT_EQ(take(),
':');
return s;
}
std::vector<uint8_t> JsonReader::ReadHex() {
SkipWhitespace();
uint8_t c = take();
EXPECT_EQ(c,
'"');
std::vector<uint8_t> v;
c = take();
while (c !=
'"') {
v.push_back(JsonReader::Hex(c) << 4 | JsonReader::Hex(take()));
c = take();
}
return v;
}
SECOidTag JsonReader::ReadHash() {
std::string s = ReadString();
if (s ==
"SHA-1") {
return SEC_OID_SHA1;
}
if (s ==
"SHA-224") {
return SEC_OID_SHA224;
}
if (s ==
"SHA-256") {
return SEC_OID_SHA256;
}
if (s ==
"SHA-384") {
return SEC_OID_SHA384;
}
if (s ==
"SHA-512") {
return SEC_OID_SHA512;
}
ADD_FAILURE() <<
"unsupported hash";
return SEC_OID_UNKNOWN;
}
SECStatus JsonReader::ReadSECStatus() {
std::string s = ReadString();
if (s ==
"SECSuccess") {
return SECSuccess;
}
else if (s ==
"SECFailure") {
return SECFailure;
}
else if (s ==
"SECWouldBlock") {
return SECWouldBlock;
}
ADD_FAILURE() <<
"unknown SECStatus";
return SECFailure;
}
bool JsonReader::ReadBool() {
std::string s = ReadString();
if (s ==
"true") {
return true;
}
else if (s ==
"false") {
return false;
}
ADD_FAILURE() <<
"not a bool";
return false;
}
bool JsonReader::NextItem(uint8_t h, uint8_t t) {
SkipWhitespace();
switch (uint8_t c = take()) {
case ',':
return true;
case '{':
case '[':
EXPECT_EQ(c, h);
SkipWhitespace();
if (peek() == t) {
next();
return false;
}
return true;
case '}':
case ']':
EXPECT_EQ(c, t);
return false;
default:
ADD_FAILURE() <<
"Unexpected '" << c <<
"'";
}
return false;
}
void JsonReader::SkipValue() {
SkipWhitespace();
uint8_t c = take();
if (c ==
'"') {
do {
c = take();
}
while (c !=
'"');
}
else if (c >=
'0' && c <=
'9') {
c = peek();
while (c >=
'0' && c <=
'9') {
next();
c = peek();
}
}
else if (c ==
'[') {
do {
SkipWhitespace();
if (peek() !=
']') {
SkipValue();
}
}
while (NextItemArray());
}
else if (c ==
'{') {
do {
SkipWhitespace();
if (peek() ==
'}') {
continue;
}
std::string n = ReadLabel();
if (n ==
"") {
break;
}
SkipValue();
}
while (NextItem());
}
else {
ADD_FAILURE() <<
"No idea how to skip '" << c <<
"'";
}
}
void JsonReader::TopUp() {
if (available_ > i_) {
return;
}
i_ = 0;
if (!f_) {
return;
}
PRInt32 res = PR_Read(f_.get(), buf_,
sizeof(buf_));
if (res > 0) {
available_ =
static_cast<size_t>(res);
}
else {
available_ = 1;
f_.reset(nullptr);
buf_[0] = 0;
}
}
void JsonReader::SkipWhitespace() {
uint8_t c = peek();
while (c && (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')) {
next();
c = peek();
}
}
// This only handles lowercase.
uint8_t JsonReader::Hex(uint8_t c) {
if (c >=
'0' && c <=
'9') {
return c -
'0';
}
EXPECT_TRUE(c >=
'a' && c <=
'f');
return c -
'a' + 10;
}
extern std::string g_source_dir;
void WycheproofHeader(
const std::string& name,
const std::string& algorithm,
const std::string& schema,
std::function<
void(JsonReader& r)> group_handler) {
std::string basename = name +
"_test.json";
std::string dir = ::g_source_dir +
"/../common/wycheproof/source_vectors/";
std::cout <<
"Reading tests from: " << basename << std::endl;
JsonReader r(dir + basename);
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n ==
"") {
break;
}
if (n ==
"algorithm") {
ASSERT_EQ(algorithm, r.ReadString());
}
else if (n ==
"generatorVersion" || n ==
"numberOfTests") {
r.SkipValue();
}
else if (n ==
"header") {
while (r.NextItemArray()) {
std::cout <<
" " << r.ReadString() << std::endl;
}
}
else if (n ==
"notes") {
while (r.NextItem()) {
std::string note = r.ReadLabel();
if (note ==
"") {
break;
}
std::cout <<
" " << note <<
": " << r.ReadString() << std::endl;
}
}
else if (n ==
"schema") {
ASSERT_EQ(schema, r.ReadString());
}
else if (n ==
"testGroups") {
while (r.NextItemArray()) {
group_handler(r);
}
}
else {
FAIL() <<
"unknown value in header";
}
}
}