Biblioteca de função Hash C ++ simples

Oct 03 2020

I criara biblioteca função hash ( MD5, MD4, SHA256, SHA384, SHA512, RipeMD128, RipeMD160, CRC16, CRC32, CRC64), escrito em C ++.

Tudo está funcionando bem e Minha biblioteca produz exatamente a mesma saída em comparação com a saída do PHP. (Exceto para a série CRC)

As camadas de abstração algorítmica individuais consistem na interface chash :: IAlgorithm e chash :: IDigest. Mas eu gostaria de refinar o IDigest com mais elegância. Como eu posso fazer isso?

https://github.com/whoamiho1006/chash

IAlgorithm.hpp

#pragma once
#include "Macros.hpp"

namespace chash {
    enum class EAlgorithm {
        Unknown     = 0x0000,
        CRC16       = 0x1000,       // --> IBM Poly-Nomial.
        CRC32       = 0x1001,       // --> IEEE 802.3
        CRC64       = 0x1002,       // --> ISO Poly-Nomial.
        SHA256      = 0x2000,
        SHA384      = 0x2001,
        SHA512      = 0x2002,
        MD5         = 0x3000,
        MD4         = 0x3001,
        RipeMD128   = 0x4000,
        RipeMD160   = 0x4001,
    };

    enum class EAlgorithmErrno {
        Succeed = 0,
        InvalidState,
        InvalidDigest
    };

    class IDigest;
    class IAlgorithm {
    public:
        IAlgorithm(EAlgorithm type)
            : _type(type), _errno(EAlgorithmErrno::Succeed)
        { 
        }

        virtual ~IAlgorithm() { }

    private:
        EAlgorithm _type;
        EAlgorithmErrno _errno;

    protected:
        inline void setError(EAlgorithmErrno _errno) {
            this->_errno = _errno;
        }

    public:
        /* get algorithm type. */
        inline EAlgorithm type() const { return _type; }

        /* get algorithm state. */
        inline EAlgorithmErrno error() const { return _errno; }

        /* create a new digest. */
        virtual IDigest* create() const = 0;

        /* initiate the algorithm. */
        virtual bool init() = 0;

        /* update the algorithm state by given bytes. */
        virtual bool update(const uint8_t* inBytes, size_t inSize) = 0;

        /* finalize the algorithm. */
        virtual bool finalize(IDigest* outDigest) = 0;

        /* compute hash with digest. */
        virtual EAlgorithmErrno compute(IDigest* outDigest, const uint8_t* inBytes, size_t inSize) {
            if (init()) {
                update(inBytes, inSize);
                finalize(outDigest);
                return error();
            }

            return error();
        }
    };

}

IDigest.hpp

#pragma once
#include "Macros.hpp"
#include <string>

namespace chash {
    class IDigest {
    public:
        virtual ~IDigest() { }

    public:
        /* get bytes pointer. */
        virtual uint8_t* bytes() const = 0;

        /* get size in bytes. */
        virtual size_t size() const = 0;

    public:
        inline std::string toHex() {
            std::string outHex;

            uint8_t* bytes = this->bytes();
            size_t size = this->size();

            outHex.reserve(size << 1);
            for (size_t i = 0; i < size; ++i) {
                int32_t b = bytes[i];

                int32_t fr = b / 16;
                int32_t bk = b % 16;

                if (fr < 10) outHex.push_back('0' + fr);
                else outHex.push_back('a' + (fr - 10));

                if (bk < 10) outHex.push_back('0' + bk);
                else outHex.push_back('a' + (bk - 10));
            }

            return outHex;
        }
    };

    /* Digest in template. */
    template<size_t Size>
    class TDigest : public IDigest {
    public:
        TDigest() {
            for (size_t i = 0; i < Size; ++i)
                _bytes[i] = 0;
        }

    private:
        mutable uint8_t _bytes[Size];

    public:
        /* get bytes pointer. */
        virtual uint8_t* bytes() const { return _bytes; }

        /* get size in bytes. */
        virtual size_t size() const { return Size;  }
    };
}

Tenho perguntas de acompanhamento com base na resposta de G. Sliepen.

Respostas

2 G.Sliepen Oct 03 2020 at 22:35

Use a biblioteca padrão

Mas eu gostaria de refinar o IDigest com mais elegância. Como eu posso fazer isso?

Seus IDigestbasicamente reimplementos std::vector<uint8_t>. Percebendo isso, eu iria removê-lo totalmente e substituí-lo por std::vector<uint8_t>. Você pode adicionar isso ao namespace chashse quiser:

using Digest = std::vector<uint8_t>;

O seu IDigesttem limitações: você não pode comparar dois resumos entre si. Mas você pode com std::vector<uint8_t>. O único recurso que sua classe possui é a função de convertê-la em uma string contendo caracteres hexadecimais. Eu deixaria isso totalmente de fora e deixaria para o aplicativo implementar tudo o que for necessário para converter um resumo em alguma outra forma, se for necessário. Mas se você quiser fornecer isso como um utilitário, basta criar uma função fora da classe para fazer isso:

std::string toHex(const Digest &digest) {
    std::string outHex;
    outHex.reserve(digest.size() * 2);

    for (auto b: digest) {
        ...
    }

    return outHex;
}

Considere se você precisa de classes básicas virtuais

Você realmente precisa de classes básicas virtuais? Isso vem com um custo: as classes derivadas agora terão uma vtable e as chamadas de função precisam passar por um nível extra de indireção. Considere usar uma classe base não virtual e faça o construtor da classe base protected, de forma que você não possa mais criar uma instância de uma classe base.