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 <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()), 62 fileContent.size(), ret.data()); 63 64 return ret; 65 } 66 67 Hash::Hash(const std::string &path) 68 : mPath(path), 69 mHash(sha256File(path)) {} 70 71 std::string Hash::hexString(const std::vector<uint8_t> &hash) { 72 std::ostringstream s; 73 s << std::hex << std::setfill('0'); 74 for (uint8_t i : hash) { 75 s << std::setw(2) << static_cast<int>(i); 76 } 77 return s.str(); 78 } 79 80 std::string Hash::hexString() const { 81 return hexString(mHash); 82 } 83 84 const std::vector<uint8_t> &Hash::raw() const { 85 return mHash; 86 } 87 88 const std::string &Hash::getPath() const { 89 return mPath; 90 } 91 92 #define HASH "([0-9a-f]+)" 93 #define FQNAME "([^\\s]+)" 94 #define SPACES " +" 95 #define MAYBE_SPACES " *" 96 #define OPTIONAL_COMMENT "(?:#.*)?" 97 static const std::regex kHashLine( 98 "(?:" 99 MAYBE_SPACES HASH SPACES FQNAME MAYBE_SPACES 100 ")?" 101 OPTIONAL_COMMENT); 102 103 struct HashFile { 104 static const HashFile *parse(const std::string &path, std::string *err) { 105 static std::map<std::string, HashFile*> hashfiles; 106 auto it = hashfiles.find(path); 107 108 if (it == hashfiles.end()) { 109 it = hashfiles.insert(it, {path, readHashFile(path, err)}); 110 } 111 112 return it->second; 113 } 114 115 std::vector<std::string> lookup(const std::string &fqName) const { 116 auto it = hashes.find(fqName); 117 118 if (it == hashes.end()) { 119 return {}; 120 } 121 122 return it->second; 123 } 124 125 private: 126 static HashFile *readHashFile(const std::string &path, std::string *err) { 127 std::ifstream stream(path); 128 if (!stream) { 129 return nullptr; 130 } 131 132 HashFile *file = new HashFile(); 133 file->path = path; 134 135 std::string line; 136 while(std::getline(stream, line)) { 137 std::smatch match; 138 bool valid = std::regex_match(line, match, kHashLine); 139 140 if (!valid) { 141 *err = "Error reading line from " + path + ": " + line; 142 delete file; 143 return nullptr; 144 } 145 146 CHECK_EQ(match.size(), 3u); 147 148 std::string hash = match.str(1); 149 std::string fqName = match.str(2); 150 151 if (hash.size() == 0 && fqName.size() == 0) { 152 continue; 153 } 154 155 if (hash.size() == 0 || fqName.size() == 0) { 156 *err = "Hash or fqName empty on " + path + ": " + line; 157 delete file; 158 return nullptr; 159 } 160 161 file->hashes[fqName].push_back(hash); 162 } 163 return file; 164 } 165 166 std::string path; 167 std::map<std::string,std::vector<std::string>> hashes; 168 }; 169 170 std::vector<std::string> Hash::lookupHash(const std::string& path, const std::string& interfaceName, 171 std::string* err, bool* fileExists) { 172 *err = ""; 173 const HashFile *file = HashFile::parse(path, err); 174 175 if (file == nullptr || err->size() > 0) { 176 if (fileExists != nullptr) *fileExists = false; 177 return {}; 178 } 179 180 if (fileExists != nullptr) *fileExists = true; 181 182 return file->lookup(interfaceName); 183 } 184 185 } // android 186