1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "test_library.h"
6
7 #define BORINGSSL_NO_CXX
8 #include <openssl/sha.h>
9
10 #include <regex>
11
12 #include <unittest/unittest.h>
13
14 namespace {
15
16 // Some of the tests below required generating strings offline until their
17 // SHA-256 sums had particular properties. The code used to calculate a
18 // collision in the first 32 bits is included below, in case it proves useful in
19 // the future.
20
21 // #include <climits>
22 // #include <iostream>
23 // #include <openssl/sha.h>
24 // #include <stdio.h>
25 // #include <string.h>
26 // #include <string>
27
28 // std::string next_name(std::string& curr) {
29 // std::string next = curr;
30 // int i = next.length() - 1;
31 // for (; i >= 0; i--) {
32 // if (next[i] < 'z') {
33 // next[i]++;
34 // break;
35 // } else {
36 // next[i] = 'a';
37 // }
38 // }
39 // if (i == -1) {
40 // next = 'a' + next;
41 // }
42 // return next;
43 // }
44
45 // int main(int argc, char** argv) {
46 // uint8_t* bitvec = new uint8_t[UINT_MAX];
47 // std::string base("a.b/");
48 // memset(bitvec, 0, UINT_MAX);
49 // bool keep_going = true;
50 // std::string curr_name = "a";
51 // uint32_t ordinal = 0;
52 // uint64_t iterations = 0;
53 // do {
54 // uint8_t digest[SHA256_DIGEST_LENGTH];
55 // curr_name = next_name(curr_name);
56 // std::string full_name = base + curr_name;
57 // SHA256(reinterpret_cast<const uint8_t*>(full_name.data()), full_name.size(), digest);
58 // ordinal = *(reinterpret_cast<uint32_t*>(digest)) & 0x7fffffff;
59 // keep_going = bitvec[ordinal] == 0;
60 // bitvec[ordinal] = 1;
61 // } while (keep_going);
62 // fprintf(stderr, "ordinal = %d name = %s\n", ordinal, curr_name.c_str());
63 // }
64
ordinal_cannot_be_zero()65 bool ordinal_cannot_be_zero() {
66 BEGIN_TEST;
67
68 TestLibrary library(R"FIDL(
69 library a;
70
71 // The first 32 bits of the SHA256 hash of a.b/fcuvhse are 0.
72 interface b {
73 fcuvhse() -> (int64 i);
74 };
75
76 )FIDL");
77 ASSERT_FALSE(library.Compile());
78 const auto& errors = library.errors();
79 ASSERT_EQ(1, errors.size(), "Ordinal value 0 should be disallowed");
80
81 END_TEST;
82 }
83
test_clashing_ordinal_values()84 bool test_clashing_ordinal_values() {
85 BEGIN_TEST;
86
87 TestLibrary library(R"FIDL(
88 library a;
89
90 // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are
91 // the same. This will trigger an error when ordinals are generated.
92 interface b {
93 ljz(string s, bool b) -> (int32 i);
94 clgn(string s) -> (handle<channel> r);
95 };
96
97 )FIDL");
98 ASSERT_FALSE(library.Compile());
99 const auto& errors = library.errors();
100 ASSERT_EQ(1, errors.size());
101
102 // The FTP requires the error message as follows
103 const std::regex pattern(R"REGEX(\[\s*Selector\s*=\s*"(ljz|clgn)_"\s*\])REGEX");
104 std::smatch sm;
105 ASSERT_TRUE(std::regex_search(errors[0], sm, pattern),
106 ("Selector pattern not found in error: " + errors[0]).c_str());
107
108 END_TEST;
109 }
110
test_clashing_ordinal_values_with_attribute()111 bool test_clashing_ordinal_values_with_attribute() {
112 BEGIN_TEST;
113
114 TestLibrary library(R"FIDL(
115 library a;
116
117 // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are
118 // the same. This will trigger an error when ordinals are generated.
119 interface b {
120 [Selector = "ljz"]
121 foo(string s, bool b) -> (int32 i);
122 [Selector = "clgn"]
123 bar(string s) -> (handle<channel> r);
124 };
125
126 )FIDL");
127 ASSERT_FALSE(library.Compile());
128 const auto& errors = library.errors();
129 ASSERT_EQ(1, errors.size());
130
131 // The FTP requires the error message as follows
132 const std::regex pattern(R"REGEX(\[\s*Selector\s*=\s*"(ljz|clgn)_"\s*\])REGEX");
133 std::smatch sm;
134 ASSERT_TRUE(std::regex_search(errors[0], sm, pattern),
135 ("Selector pattern not found in error: " + errors[0]).c_str());
136
137 END_TEST;
138 }
139
attribute_resolves_clashes()140 bool attribute_resolves_clashes() {
141 BEGIN_TEST;
142
143 TestLibrary library(R"FIDL(
144 library a;
145
146 // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are
147 // the same. This will trigger an error when ordinals are generated.
148 interface b {
149 [Selector = "ljz_"]
150 ljz(string s, bool b) -> (int32 i);
151 clgn(string s) -> (handle<channel> r);
152 };
153
154 )FIDL");
155 ASSERT_TRUE(library.Compile());
156
157 END_TEST;
158 }
159
test_ordinal_value_is_sha256()160 bool test_ordinal_value_is_sha256() {
161 BEGIN_TEST;
162 TestLibrary library(R"FIDL(
163 library a;
164
165 interface b {
166 potato(string s, bool b) -> (int32 i);
167 };
168 )FIDL");
169 ASSERT_TRUE(library.Compile());
170
171 const char hash_name[] = "a.b/potato";
172 uint8_t digest[SHA256_DIGEST_LENGTH];
173 SHA256(reinterpret_cast<const uint8_t*>(hash_name), strlen(hash_name), digest);
174 uint32_t expected_hash = *(reinterpret_cast<uint32_t*>(digest)) & 0x7fffffff;
175
176 const fidl::flat::Interface* iface = library.LookupInterface("b");
177 uint32_t actual_hash = iface->methods[0].ordinal->value;
178 ASSERT_EQ(actual_hash, expected_hash, "Expected hash is not correct");
179 END_TEST;
180 }
181
182 } // namespace
183
184 BEGIN_TEST_CASE(ordinals_test);
185 RUN_TEST(ordinal_cannot_be_zero);
186 RUN_TEST(test_clashing_ordinal_values);
187 RUN_TEST(test_clashing_ordinal_values_with_attribute);
188 RUN_TEST(attribute_resolves_clashes);
189 RUN_TEST(test_ordinal_value_is_sha256);
190 END_TEST_CASE(ordinals_test);
191