Task: SHA-256 Merkle tree


C++: #include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include <openssl/sha.h>

class sha256_exception : public std::exception {
public:
    const char* what() const noexcept override {
        return "SHA-256 error";
    }
};

class sha256 {
public:
    sha256() { reset(); }
    sha256(const sha256&) = delete;
    sha256& operator=(const sha256&) = delete;
    void reset() {
        if (SHA256_Init(&context_) == 0)
            throw sha256_exception();
    }
    void update(const void* data, size_t length) {
        if (SHA256_Update(&context_, data, length) == 0)
            throw sha256_exception();
    }
    std::vector<unsigned char> digest() {
        std::vector<unsigned char> digest(SHA256_DIGEST_LENGTH);
        if (SHA256_Final(digest.data(), &context_) == 0)
            throw sha256_exception();
        return digest;
    }
private:
    SHA256_CTX context_;
};

std::string digest_to_string(const std::vector<unsigned char>& digest) {
    std::ostringstream out;
    out << std::hex << std::setfill('0');
    for (size_t i = 0; i < digest.size(); ++i)
        out << std::setw(2) << static_cast<int>(digest[i]);
    return out.str();
}

std::vector<unsigned char> sha256_merkle_tree(std::istream& in, size_t block_size) {
    std::vector<std::vector<unsigned char>> hashes;
    std::vector<char> buffer(block_size);
    sha256 md;
    while (in) {
        in.read(buffer.data(), block_size);
        size_t bytes = in.gcount();
        if (bytes == 0)
            break;
        md.reset();
        md.update(buffer.data(), bytes);
        hashes.push_back(md.digest());
    }
    if (hashes.empty())
        return {};
    size_t length = hashes.size();
    while (length > 1) {
        size_t j = 0;
        for (size_t i = 0; i < length; i += 2, ++j) {
            auto& digest1 = hashes[i];
            auto& digest_out = hashes[j];
            if (i + 1 < length) {
                auto& digest2 = hashes[i + 1];
                md.reset();
                md.update(digest1.data(), digest1.size());
                md.update(digest2.data(), digest2.size());
                digest_out = md.digest();
            } else {
                digest_out = digest1;
            }
        }
        length = j;
    }
    return hashes[0];
}

int main(int argc, char** argv) {
    if (argc != 2) {
        std::cerr << "usage: " << argv[0] << " filename\n";
        return EXIT_FAILURE;
    }
    std::ifstream in(argv[1], std::ios::binary);
    if (!in) {
        std::cerr << "Cannot open file " << argv[1] << ".\n";
        return EXIT_FAILURE;
    }
    try {
        std::cout << digest_to_string(sha256_merkle_tree(in, 1024)) << '\n';
    } catch (const std::exception& ex) {
        std::cerr << ex.what() << "\n";
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}




Java: import java.io.*;
import java.security.*;
import java.util.*;

public class SHA256MerkleTree {
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("missing file argument");
            System.exit(1);
        }
        try (InputStream in = new BufferedInputStream(new FileInputStream(args[0]))) {
            byte[] digest = sha256MerkleTree(in, 1024);
            if (digest != null)
                System.out.println(digestToString(digest));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String digestToString(byte[] digest) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < digest.length; ++i)
            result.append(String.format("%02x", digest[i]));
        return result.toString();
    }

    private static byte[] sha256MerkleTree(InputStream in, int blockSize) throws Exception {
        byte[] buffer = new byte[blockSize];
        int bytes;
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        List<byte[]> digests = new ArrayList<>();
        while ((bytes = in.read(buffer)) > 0) {
            md.reset();
            md.update(buffer, 0, bytes);
            digests.add(md.digest());
        }
        int length = digests.size();
        if (length == 0)
            return null;
        while (length > 1) {
            int j = 0;
            for (int i = 0; i < length; i += 2, ++j) {
                byte[] digest1 = digests.get(i);
                if (i + 1 < length) {
                    byte[] digest2 = digests.get(i + 1);
                    md.reset();
                    md.update(digest1);
                    md.update(digest2);
                    digests.set(j, md.digest());
                } else {
                    digests.set(j, digest1);
                }
            }
            length = j;
        }
        return digests.get(0);
    }
}