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 <hidl-hash/Hash.h> 18 19 #include <algorithm> 20 #include <fstream> 21 #include <iomanip> 22 #include <map> 23 #include <regex> 24 #include <sstream> 25 26 #include <android-base/logging.h> 27 #include <openssl/sha.h> 28 29 namespace android { 30 31 const std::vector<uint8_t> Hash::kEmptyHash = std::vector<uint8_t>(SHA256_DIGEST_LENGTH, 0); 32 33 Hash& Hash::getMutableHash(const std::string& path) { 34 static std::map<std::string, Hash> hashes; 35 36 auto it = hashes.find(path); 37 38 if (hashes.find(path) == hashes.end()) { 39 it = hashes.insert(it, {path, Hash(path)}); 40 } 41 42 return it->second; 43 } 44 45 const Hash& Hash::getHash(const std::string& path) { 46 return getMutableHash(path); 47 } 48 49 void Hash::clearHash(const std::string& path) { 50 getMutableHash(path).mHash = kEmptyHash; 51 } 52 53 static std::vector<uint8_t> sha256File(const std::string& path) { 54 std::ifstream stream(path); 55 std::stringstream fileStream; 56 fileStream << stream.rdbuf(); 57 std::string fileContent = fileStream.str(); 58 59 std::vector<uint8_t> ret = std::vector<uint8_t>(SHA256_DIGEST_LENGTH); 60 61 SHA256(reinterpret_cast<const uint8_t*>(fileContent.c_str()), fileContent.size(), ret.data()); 62 63 return ret; 64 } 65 66 Hash::Hash(const std::string& path) : mPath(path), mHash(sha256File(path)) {} 67 68 std::string Hash::hexString(const std::vector<uint8_t>& hash) { 69 std::ostringstream s; 70 s << std::hex << std::setfill('0'); 71 for (uint8_t i : hash) { 72 s << std::setw(2) << static_cast<int>(i); 73 } 74 return s.str(); 75 } 76 77 std::string Hash::hexString() const { 78 return hexString(mHash); 79 } 80 81 const std::vector<uint8_t>& Hash::raw() const { 82 return mHash; 83 } 84 85 const std::string& Hash::getPath() const { 86 return mPath; 87 } 88 89 #define HASH "([0-9a-f]+)" 90 #define FQNAME "([^\\s]+)" 91 #define SPACES " +" 92 #define MAYBE_SPACES " *" 93 #define OPTIONAL_COMMENT "(?:#.*)?" 94 static const std::regex kHashLine("(?:" MAYBE_SPACES HASH SPACES FQNAME MAYBE_SPACES 95 ")?" OPTIONAL_COMMENT); 96 97 struct HashFile { 98 static const HashFile* parse(const std::string& path, std::string* err) { 99 static std::map<std::string, HashFile*> hashfiles; 100 auto it = hashfiles.find(path); 101 102 if (it == hashfiles.end()) { 103 it = hashfiles.insert(it, {path, readHashFile(path, err)}); 104 } 105 106 return it->second; 107 } 108 109 std::vector<std::string> lookup(const std::string& fqName) const { 110 auto it = hashes.find(fqName); 111 112 if (it == hashes.end()) { 113 return {}; 114 } 115 116 return it->second; 117 } 118 119 private: 120 static HashFile* readHashFile(const std::string& path, std::string* err) { 121 std::ifstream stream(path); 122 if (!stream) { 123 return nullptr; 124 } 125 126 HashFile* file = new HashFile(); 127 file->path = path; 128 129 std::string line; 130 while (std::getline(stream, line)) { 131 std::smatch match; 132 bool valid = std::regex_match(line, match, kHashLine); 133 134 if (!valid) { 135 *err = "Error reading line from " + path + ": " + line; 136 delete file; 137 return nullptr; 138 } 139 140 CHECK_EQ(match.size(), 3u); 141 142 std::string hash = match.str(1); 143 std::string fqName = match.str(2); 144 145 if (hash.size() == 0 && fqName.size() == 0) { 146 continue; 147 } 148 149 if (hash.size() == 0 || fqName.size() == 0) { 150 *err = "Hash or fqName empty on " + path + ": " + line; 151 delete file; 152 return nullptr; 153 } 154 155 file->hashes[fqName].push_back(hash); 156 } 157 return file; 158 } 159 160 std::string path; 161 std::map<std::string, std::vector<std::string>> hashes; 162 }; 163 164 std::vector<std::string> Hash::lookupHash(const std::string& path, const std::string& interfaceName, 165 std::string* err, bool* fileExists) { 166 *err = ""; 167 const HashFile* file = HashFile::parse(path, err); 168 169 if (file == nullptr || err->size() > 0) { 170 if (fileExists != nullptr) *fileExists = false; 171 return {}; 172 } 173 174 if (fileExists != nullptr) *fileExists = true; 175 176 return file->lookup(interfaceName); 177 } 178 179 } // namespace android 180