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 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 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 56 Hash::Hash(const std::string &path) 57 : mPath(path), 58 mHash(sha256File(path)) {} 59 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 69 std::string Hash::hexString() const { 70 return hexString(mHash); 71 } 72 73 const std::vector<uint8_t> &Hash::raw() const { 74 return mHash; 75 } 76 77 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 { 93 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 104 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: 115 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 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