|  | /* | 
|  | * Copyright (C) 2017 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "Hash.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <fstream> | 
|  | #include <iomanip> | 
|  | #include <map> | 
|  | #include <regex> | 
|  | #include <sstream> | 
|  |  | 
|  | #include <android-base/logging.h> | 
|  | #include <openssl/sha.h> | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | const std::vector<uint8_t> Hash::kEmptyHash = std::vector<uint8_t>(SHA256_DIGEST_LENGTH, 0); | 
|  |  | 
|  | Hash& Hash::getMutableHash(const std::string& path) { | 
|  | static std::map<std::string, Hash> hashes; | 
|  |  | 
|  | auto it = hashes.find(path); | 
|  |  | 
|  | if (hashes.find(path) == hashes.end()) { | 
|  | it = hashes.insert(it, {path, Hash(path)}); | 
|  | } | 
|  |  | 
|  | return it->second; | 
|  | } | 
|  |  | 
|  | const Hash& Hash::getHash(const std::string& path) { | 
|  | return getMutableHash(path); | 
|  | } | 
|  |  | 
|  | void Hash::clearHash(const std::string& path) { | 
|  | getMutableHash(path).mHash = kEmptyHash; | 
|  | } | 
|  |  | 
|  | static std::vector<uint8_t> sha256File(const std::string &path) { | 
|  | std::ifstream stream(path); | 
|  | std::stringstream fileStream; | 
|  | fileStream << stream.rdbuf(); | 
|  | std::string fileContent = fileStream.str(); | 
|  |  | 
|  | std::vector<uint8_t> ret = std::vector<uint8_t>(SHA256_DIGEST_LENGTH); | 
|  |  | 
|  | SHA256(reinterpret_cast<const uint8_t *>(fileContent.c_str()), | 
|  | fileContent.size(), ret.data()); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | Hash::Hash(const std::string &path) | 
|  | : mPath(path), | 
|  | mHash(sha256File(path)) {} | 
|  |  | 
|  | std::string Hash::hexString(const std::vector<uint8_t> &hash) { | 
|  | std::ostringstream s; | 
|  | s << std::hex << std::setfill('0'); | 
|  | for (uint8_t i : hash) { | 
|  | s << std::setw(2) << static_cast<int>(i); | 
|  | } | 
|  | return s.str(); | 
|  | } | 
|  |  | 
|  | std::string Hash::hexString() const { | 
|  | return hexString(mHash); | 
|  | } | 
|  |  | 
|  | const std::vector<uint8_t> &Hash::raw() const { | 
|  | return mHash; | 
|  | } | 
|  |  | 
|  | const std::string &Hash::getPath() const { | 
|  | return mPath; | 
|  | } | 
|  |  | 
|  | #define HASH "([0-9a-f]+)" | 
|  | #define FQNAME "([^\\s]+)" | 
|  | #define SPACES " +" | 
|  | #define MAYBE_SPACES " *" | 
|  | #define OPTIONAL_COMMENT "(?:#.*)?" | 
|  | static const std::regex kHashLine( | 
|  | "(?:" | 
|  | MAYBE_SPACES HASH SPACES FQNAME MAYBE_SPACES | 
|  | ")?" | 
|  | OPTIONAL_COMMENT); | 
|  |  | 
|  | struct HashFile { | 
|  | static const HashFile *parse(const std::string &path, std::string *err) { | 
|  | static std::map<std::string, HashFile*> hashfiles; | 
|  | auto it = hashfiles.find(path); | 
|  |  | 
|  | if (it == hashfiles.end()) { | 
|  | it = hashfiles.insert(it, {path, readHashFile(path, err)}); | 
|  | } | 
|  |  | 
|  | return it->second; | 
|  | } | 
|  |  | 
|  | std::vector<std::string> lookup(const std::string &fqName) const { | 
|  | auto it = hashes.find(fqName); | 
|  |  | 
|  | if (it == hashes.end()) { | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | return it->second; | 
|  | } | 
|  |  | 
|  | private: | 
|  | static HashFile *readHashFile(const std::string &path, std::string *err) { | 
|  | std::ifstream stream(path); | 
|  | if (!stream) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | HashFile *file = new HashFile(); | 
|  | file->path = path; | 
|  |  | 
|  | std::string line; | 
|  | while(std::getline(stream, line)) { | 
|  | std::smatch match; | 
|  | bool valid = std::regex_match(line, match, kHashLine); | 
|  |  | 
|  | if (!valid) { | 
|  | *err = "Error reading line from " + path + ": " + line; | 
|  | delete file; | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | CHECK_EQ(match.size(), 3u); | 
|  |  | 
|  | std::string hash = match.str(1); | 
|  | std::string fqName = match.str(2); | 
|  |  | 
|  | if (hash.size() == 0 && fqName.size() == 0) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (hash.size() == 0 || fqName.size() == 0) { | 
|  | *err = "Hash or fqName empty on " + path + ": " + line; | 
|  | delete file; | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | file->hashes[fqName].push_back(hash); | 
|  | } | 
|  | return file; | 
|  | } | 
|  |  | 
|  | std::string path; | 
|  | std::map<std::string,std::vector<std::string>> hashes; | 
|  | }; | 
|  |  | 
|  | std::vector<std::string> Hash::lookupHash(const std::string& path, const std::string& interfaceName, | 
|  | std::string* err, bool* fileExists) { | 
|  | *err = ""; | 
|  | const HashFile *file = HashFile::parse(path, err); | 
|  |  | 
|  | if (file == nullptr || err->size() > 0) { | 
|  | if (fileExists != nullptr) *fileExists = false; | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | if (fileExists != nullptr) *fileExists = true; | 
|  |  | 
|  | return file->lookup(interfaceName); | 
|  | } | 
|  |  | 
|  | }  // android |