1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "Hash.h"
18
19 #include <fstream>
20 #include <iomanip>
21 #include <map>
22 #include <regex>
23 #include <sstream>
24
25 #include <android-base/logging.h>
26 #include <openssl/sha.h>
27
28 namespace android {
29
getHash(const std::string & path)30 const Hash &Hash::getHash(const std::string &path) {
31 static std::map<std::string, Hash> hashes;
32
33 auto it = hashes.find(path);
34
35 if (hashes.find(path) == hashes.end()) {
36 it = hashes.insert(it, {path, Hash(path)});
37 }
38
39 return it->second;
40 }
41
sha256File(const std::string & path)42 static std::vector<uint8_t> sha256File(const std::string &path) {
43 std::ifstream stream(path);
44 std::stringstream fileStream;
45 fileStream << stream.rdbuf();
46 std::string fileContent = fileStream.str();
47
48 std::vector<uint8_t> ret = std::vector<uint8_t>(SHA256_DIGEST_LENGTH);
49
50 SHA256(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
51 fileContent.size(), ret.data());
52
53 return ret;
54 }
55
Hash(const std::string & path)56 Hash::Hash(const std::string &path)
57 : mPath(path),
58 mHash(sha256File(path)) {}
59
hexString(const std::vector<uint8_t> & hash)60 std::string Hash::hexString(const std::vector<uint8_t> &hash) {
61 std::ostringstream s;
62 s << std::hex << std::setfill('0');
63 for (uint8_t i : hash) {
64 s << std::setw(2) << static_cast<int>(i);
65 }
66 return s.str();
67 }
68
hexString() const69 std::string Hash::hexString() const {
70 return hexString(mHash);
71 }
72
raw() const73 const std::vector<uint8_t> &Hash::raw() const {
74 return mHash;
75 }
76
getPath() const77 const std::string &Hash::getPath() const {
78 return mPath;
79 }
80
81 #define HASH "([0-9a-f]+)"
82 #define FQNAME "([^\\s]+)"
83 #define SPACES " +"
84 #define MAYBE_SPACES " *"
85 #define OPTIONAL_COMMENT "(?:#.*)?"
86 static const std::regex kHashLine(
87 "(?:"
88 MAYBE_SPACES HASH SPACES FQNAME MAYBE_SPACES
89 ")?"
90 OPTIONAL_COMMENT);
91
92 struct HashFile {
parseandroid::HashFile93 static const HashFile *parse(const std::string &path, std::string *err) {
94 static std::map<std::string, HashFile*> hashfiles;
95 auto it = hashfiles.find(path);
96
97 if (it == hashfiles.end()) {
98 it = hashfiles.insert(it, {path, readHashFile(path, err)});
99 }
100
101 return it->second;
102 }
103
lookupandroid::HashFile104 std::vector<std::string> lookup(const std::string &fqName) const {
105 auto it = hashes.find(fqName);
106
107 if (it == hashes.end()) {
108 return {};
109 }
110
111 return it->second;
112 }
113
114 private:
readHashFileandroid::HashFile115 static HashFile *readHashFile(const std::string &path, std::string *err) {
116 std::ifstream stream(path);
117 if (!stream) {
118 return nullptr;
119 }
120
121 HashFile *file = new HashFile();
122 file->path = path;
123
124 std::string line;
125 while(std::getline(stream, line)) {
126 std::smatch match;
127 bool valid = std::regex_match(line, match, kHashLine);
128
129 if (!valid) {
130 *err = "Error reading line from " + path + ": " + line;
131 delete file;
132 return nullptr;
133 }
134
135 CHECK_EQ(match.size(), 3u);
136
137 std::string hash = match.str(1);
138 std::string fqName = match.str(2);
139
140 if (hash.size() == 0 && fqName.size() == 0) {
141 continue;
142 }
143
144 if (hash.size() == 0 || fqName.size() == 0) {
145 *err = "Hash or fqName empty on " + path + ": " + line;
146 delete file;
147 return nullptr;
148 }
149
150 file->hashes[fqName].push_back(hash);
151 }
152 return file;
153 }
154
155 std::string path;
156 std::map<std::string,std::vector<std::string>> hashes;
157 };
158
lookupHash(const std::string & path,const std::string & interfaceName,std::string * err)159 std::vector<std::string> Hash::lookupHash(const std::string &path,
160 const std::string &interfaceName,
161 std::string *err) {
162 *err = "";
163 const HashFile *file = HashFile::parse(path, err);
164
165 if (file == nullptr || err->size() > 0) {
166 return {};
167 }
168
169 return file->lookup(interfaceName);
170 }
171
172 } // android
173