summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/pk11_gtest/json_reader.h
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/gtests/pk11_gtest/json_reader.h')
-rw-r--r--security/nss/gtests/pk11_gtest/json_reader.h138
1 files changed, 138 insertions, 0 deletions
diff --git a/security/nss/gtests/pk11_gtest/json_reader.h b/security/nss/gtests/pk11_gtest/json_reader.h
new file mode 100644
index 0000000000..dd520a8969
--- /dev/null
+++ b/security/nss/gtests/pk11_gtest/json_reader.h
@@ -0,0 +1,138 @@
+/* -*- 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();
+
+ 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_