json_reader.h (4394B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef PK11GTEST_JSON_H_ 8 #define PK11GTEST_JSON_H_ 9 10 #include <functional> 11 #include <iostream> 12 #include <vector> 13 #include "gtest/gtest.h" 14 #include "nss_scoped_ptrs.h" 15 #include "secoidt.h" 16 17 // If we make a few assumptions about the file, parsing JSON can be easy. 18 // This is not a full parser, it only works on a narrow set of inputs. 19 class JsonReader { 20 public: 21 JsonReader(const std::string& n); 22 23 void next() { i_++; } 24 uint8_t peek() { 25 TopUp(); 26 return buf_[i_]; 27 } 28 uint8_t take() { 29 uint8_t v = peek(); 30 next(); 31 return v; 32 } 33 34 // No input checking, overflow protection, or any safety. 35 // Returns 0 if there isn't a number here rather than aborting. 36 uint64_t ReadInt(); 37 // No input checking, no unicode, no escaping (not even \"), just read ASCII. 38 std::string ReadString(); 39 std::string ReadLabel(); 40 std::vector<uint8_t> ReadHex(); 41 SECOidTag ReadHash(); 42 SECStatus ReadSECStatus(); 43 bool ReadBool(); 44 45 bool NextItem(uint8_t h = '{', uint8_t t = '}'); 46 bool NextItemArray() { return NextItem('[', ']'); } 47 void SkipValue(); 48 49 private: 50 void TopUp(); 51 void SkipWhitespace(); 52 // This only handles lowercase. 53 uint8_t Hex(uint8_t c); 54 55 ScopedPRFileDesc f_; 56 uint8_t buf_[4096]; 57 size_t available_; 58 size_t i_; 59 }; 60 61 // The way this is expected to work is that this reads the header, then 62 // passes off the content of each "testGroups" member to `group_handler`. 63 // That function processes any attributes in that structure, calls 64 // `WycheproofReadTests` to load individual cases and runs those tests. 65 void WycheproofHeader(const std::string& name, const std::string& algorithm, 66 const std::string& schema, 67 std::function<void(JsonReader& r)> group_handler); 68 69 template <typename T> 70 struct id { 71 typedef T type; 72 }; 73 74 template <typename T> 75 using nondeduced = typename id<T>::type; 76 77 // Read into a block of test cases, handling standard attributes on Wycheproof 78 // tests. 79 // 80 // `T` needs `uint64_t id` and `bool valid` fields. 81 // `attr_reader` is responsible for reading values into the test case struct. 82 // `acceptable` determines whether a test marked "acceptable" is valid by 83 // default. `process_flags` allows for processing the flags on an entry. 84 // 85 // Note that this gathers all tests into a vector rather than running tests as 86 // they arrive. This is necessary because the testGroup JSON struct might have 87 // fields that haven't been read when this list is constructed (it doesn't in 88 // the current files, but this is not guaranteed). Tests can only run after all 89 // of the group attributes have been read and processed. 90 template <typename T> 91 void WycheproofReadTests( 92 JsonReader& r, std::vector<T>* tests, 93 const std::function<nondeduced<void(T&, const std::string&, JsonReader&)>>& 94 attr_reader, 95 bool acceptable = true, 96 const std::function<nondeduced<void(T&, const std::string&, 97 const std::vector<std::string>&)>>& 98 process_flags = nullptr) { 99 while (r.NextItemArray()) { 100 T tc; 101 std::string comment; 102 std::string result; 103 std::vector<std::string> flags; 104 105 while (r.NextItem()) { 106 std::string n = r.ReadLabel(); 107 if (n == "") { 108 break; 109 } 110 if (n == "tcId") { 111 tc.id = r.ReadInt(); 112 } else if (n == "result") { 113 result = r.ReadString(); 114 } else if (n == "comment") { 115 comment = r.ReadString(); 116 } else if (n == "flags") { 117 while (r.NextItemArray()) { 118 flags.push_back(r.ReadString()); 119 } 120 } else { 121 ASSERT_NO_FATAL_FAILURE(attr_reader(tc, n, r)); 122 } 123 } 124 125 tc.valid = (result == "valid") || (acceptable && result == "acceptable"); 126 if (process_flags) { 127 process_flags(tc, result, flags); 128 } 129 130 std::cout << " tc " << tc.id << ": " << comment << " [" << result; 131 for (auto& f : flags) { 132 std::cout << ", " << f; 133 } 134 std::cout << "] expect " << (tc.valid ? "success" : "failure") << std::endl; 135 136 tests->push_back(tc); 137 } 138 } 139 140 #endif // PK11GTEST_JSON_H_