如何将 Github API 哈希与本地文件哈希进行比较?

How to compare Github API hash with a local file hash?

提问人:Jabu 提问时间:8/29/2023 最后编辑:Jabu 更新时间:8/30/2023 访问量:108

问:

我正在尝试将本地文件的哈希值与 API 中的哈希值进行比较;GitHub

我已经创建并将其上传到我的存储库,当我调用:test.txtcompute_sha1_hashtest.txt

2aae6c35c94fcfb415dbe95f408b9ce91ee846ed

API 响应:GitHub

      "path": "test.txt",
      "mode": "100644",
      "type": "blob",
      "sha": "95d09f2b10159347eece71399a7e2e907ea3df4f",

从我搜索的内容来看,GitHub 使用,为什么值不匹配?SHA1

我是否需要在此哈希中执行任何类型的计算来检索“本地文件哈希”?

我写了一个工作示例

#include <curl/curl.h>
#include <openssl/evp.h>
#include <fstream>
#include <vector>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <regex>

std::string compute_sha1_hash(const std::string& file_path)
{
    std::ifstream file(file_path, std::ios::binary);
    if (!file) {
        throw std::runtime_error("Could not open file");
    }

    EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
    if (mdctx == NULL) {
        throw std::runtime_error("Failed to create EVP_MD_CTX");
    }

    if (EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL) != 1) {
        throw std::runtime_error("Failed to initialize SHA-1 context");
    }

    std::vector<char> buffer(4096);
    while (file.read(buffer.data(), buffer.size())) {
        if (EVP_DigestUpdate(mdctx, buffer.data(), file.gcount()) != 1) {
            throw std::runtime_error("Failed to update SHA-1 context");
        }
    }

    if (file.gcount() > 0) {
        if (EVP_DigestUpdate(mdctx, buffer.data(), file.gcount()) != 1) {
            throw std::runtime_error("Failed to update SHA-1 context");
        }
    }

    std::vector<unsigned char> hash_value(EVP_MD_size(EVP_sha1()));
    unsigned int digest_len;
    if (EVP_DigestFinal_ex(mdctx, hash_value.data(), &digest_len) != 1) {
        throw std::runtime_error("Failed to finalize SHA-1 context");
    }

    EVP_MD_CTX_free(mdctx);

    // Convert to hex
    std::ostringstream oss;
    for (const auto& byte : hash_value) {
        oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
    }

    return oss.str();
}

// Brian613542705's answer
std::string compute_sha1_hash_2(const std::string& file_path)
{
   std::ifstream file(file_path, std::ios::binary);
   if (!file) {
      throw std::runtime_error("Could not open file");
   }

   // Read the content of the file
   std::ostringstream oss;
   oss << file.rdbuf();
   std::string content = oss.str();

   // Prepend "blob " and the size of the content
   std::string preparedContent = "blob " + std::to_string(content.size()) + "\0" + content;

   EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
   if (mdctx == NULL) {
      throw std::runtime_error("Failed to create EVP_MD_CTX");
   }

   if (EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL) != 1) {
      throw std::runtime_error("Failed to initialize SHA-1 context");
   }

   if (EVP_DigestUpdate(mdctx, preparedContent.c_str(), preparedContent.size()) != 1) {
      throw std::runtime_error("Failed to update SHA-1 context");
   }

   std::vector<unsigned char> hash_value(EVP_MD_size(EVP_sha1()));
   unsigned int digest_len;
   if (EVP_DigestFinal_ex(mdctx, hash_value.data(), &digest_len) != 1) {
      throw std::runtime_error("Failed to finalize SHA-1 context");
   }

   EVP_MD_CTX_free(mdctx);

   // Convert to hex
   std::ostringstream sha1oss;
   for (const auto& byte : hash_value) {
      sha1oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
   }

   return sha1oss.str();
}

 static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) 
 {
     size_t realsize = size * nmemb;
     auto& mem = *static_cast<std::string*>(userp);
     mem.append(static_cast<char*>(contents), realsize);
     return realsize;
 }

void curl(std::string& data, const char* url)
{
    CURL* curl_handle = curl_easy_init();
    curl_easy_setopt(curl_handle, CURLOPT_URL, url);
    curl_easy_setopt(curl_handle, CURLOPT_TCP_KEEPALIVE, 0);
    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &data);
    curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);  // redirects
    CURLcode res = curl_easy_perform(curl_handle);
    if(res != CURLE_OK)
        std::cerr << "curl_easy_perform failed: " << curl_easy_strerror(res) << '\n';
    curl_easy_cleanup(curl_handle);
    curl_global_cleanup();
}

int main() 
{
    std::string fileHash = compute_sha1_hash("C:/Users/jabu/test.txt");
    std::string fileHash_2 = compute_sha1_hash_2("C:/Users/jabu/test.txt");
    std::string jsonString;
    curl(jsonString, "https://api.github.com/repos/jajabu33/test/git/trees/main?recursive=1");

    // just to kick compare the strings
    std::regex shaPattern("\"sha\":\\s*\"([a-fA-F0-9]+)\"");
    std::sregex_iterator shaIterator(jsonString.begin(), jsonString.end(), shaPattern);
    std::sregex_iterator endIterator;

    std::string githubHash;
    for (; shaIterator != endIterator; ++shaIterator)
    {
        std::smatch match = *shaIterator;
        if (match.size() >= 2)
            githubHash = match[1].str();
    }

    if (fileHash == githubHash)
        std::cout << "Hashes are equal\n";
    else
        std::cout << "Hashes are not equal\n";
    return 0;
}

编辑我已经添加了执行已回答的函数,但哈希值仍然不匹配。compute_sha1_hash_2Brian61354270

c++ git github github-api git-plumbing

答:

3赞 Brian61354270 8/29/2023 #1

你正在查看 blob 的哈希值(也称为对象 ID),而不是文件的哈希值。Blob 是 git 用于存储文件内容的对象类型。除了文件内容之外,它们还包含一个标头,这就是 blob ID 与文件的哈希不匹配的原因。

通常,若要根据文件的内容计算文件的 blob ID,可以使用 git hash-object

$ git hash-object test.txt

若要在 C++ 中计算 blob ID,需要手动为文件创建一个 blob 对象,然后计算其 SHA1 哈希。幸运的是,这是一项简单的任务。首先创建 blob 标头,其表单为

blob <size>\0

其中 是文件的大小(以字节为单位),以 ASCII 十进制数的形式写入,并且是 null 字节。标头紧跟文件内容,以形成完整的 blob 对象。<size>\0

可以通过将构造的 blob 对象的 SHA1 哈希值与 的输出进行比较,从自我检查标头是否正确。例如,假设我们有一个长度为 3 的文件,其内容为 .然后,完整的 blob 对象是 ,SHA1 哈希为 。检查一下,我们看到我们得到了一个匹配项:git hash-objectfooblob 3\0foo19102815...git hash-object

$ echo -n "foo" | git hash-object --stdin
19102815663d23f8b75a47e7a01965dcdc96468c
$ echo -n $'blob 3\0foo' | sha1sum 
19102815663d23f8b75a47e7a01965dcdc96468c  -

请参阅 Git Internals - Git Objects from the git book 了解有关如何在 git 中存储对象的更多信息。

评论

0赞 Jabu 8/29/2023
我不明白你做了什么,但你的概念证明中的这些值与“本地哈希”不同,我将如何从 C++ 调用这些命令?2aae6c35c94fcfb415dbe95f408b9ce91ee846ed
0赞 Brian61354270 8/29/2023
@Jabu 概念证明是针对具有 .如果将相同的步骤应用于文件,则应获得与 GitHub 返回的 blob 哈希匹配的哈希值。footest.txt95d09f2...
0赞 Brian61354270 8/29/2023
@Jabu 您无需从C++调用这些命令。在代码中,可以使用任何你喜欢的 SHA1 实现
0赞 Jabu 8/29/2023
我仍然不明白如何比较哈希值,您能否举个 c++ 示例以及我在上面代码中使用的文件?
1赞 Brian61354270 8/29/2023
@Jabu 计算 blob 哈希值与计算本地文件的哈希值没有什么不同。只需按上述方式追加 blob 标头即可。如果您需要有关特定部分的帮助(例如,如何将字符串附加到文件的内存内容中),您可以为此创建一个特定问题。