165 lines
3.9 KiB
C++
165 lines
3.9 KiB
C++
// Copyright 2020-2023 The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#include "CryptographicHash.h"
|
|
|
|
#include <QtCore/QString>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <cassert>
|
|
|
|
class CryptographicHashPrivate {
|
|
public:
|
|
CryptographicHashPrivate(const EVP_MD *type);
|
|
~CryptographicHashPrivate();
|
|
void addData(const QByteArray &buf);
|
|
QByteArray result();
|
|
|
|
private:
|
|
EVP_MD_CTX *m_mdctx;
|
|
|
|
/// The result of the cryptographic hash.
|
|
/// This field is filled out by the result()
|
|
/// method.
|
|
/// Once this field is set, the m_mdctx is finalized
|
|
/// and can't be used anymore, so result() and addData()
|
|
/// must guard against that possibility.
|
|
QByteArray m_result;
|
|
|
|
CryptographicHashPrivate();
|
|
|
|
/// If m_mdctx is set, cleanupMdctx will destroy
|
|
/// the EVP_MD_CTX object and set m_mdctx to nullptr.
|
|
void cleanupMdctx();
|
|
};
|
|
|
|
CryptographicHashPrivate::CryptographicHashPrivate() : m_mdctx(nullptr), m_result() {
|
|
}
|
|
|
|
void CryptographicHashPrivate::cleanupMdctx() {
|
|
if (m_mdctx) {
|
|
EVP_MD_CTX_destroy(m_mdctx);
|
|
m_mdctx = nullptr;
|
|
}
|
|
}
|
|
|
|
CryptographicHashPrivate::CryptographicHashPrivate(const EVP_MD *type) : CryptographicHashPrivate() {
|
|
m_mdctx = EVP_MD_CTX_create();
|
|
if (!m_mdctx) {
|
|
return;
|
|
}
|
|
|
|
int err = EVP_DigestInit_ex(m_mdctx, type, nullptr);
|
|
if (err != 1) {
|
|
cleanupMdctx();
|
|
return;
|
|
}
|
|
}
|
|
|
|
CryptographicHashPrivate::~CryptographicHashPrivate() {
|
|
cleanupMdctx();
|
|
}
|
|
|
|
void CryptographicHashPrivate::addData(const QByteArray &buf) {
|
|
if (!m_mdctx) {
|
|
return;
|
|
}
|
|
|
|
// If we have a result already, that means our m_mdctx
|
|
// object has been finalized, and can't be used anymore.
|
|
// In that case, transition into an error state by cleaning
|
|
// up the mdctx -- that way, subsequent calls to result()
|
|
// will return an empty QByteArray.
|
|
if (!m_result.isEmpty()) {
|
|
cleanupMdctx();
|
|
return;
|
|
}
|
|
|
|
int err = EVP_DigestUpdate(m_mdctx, buf.constData(), static_cast< std::size_t >(buf.size()));
|
|
if (err != 1) {
|
|
cleanupMdctx();
|
|
}
|
|
}
|
|
|
|
QByteArray CryptographicHashPrivate::result() {
|
|
if (!m_mdctx) {
|
|
return QByteArray();
|
|
}
|
|
|
|
// If we have a result already, that means our m_mdctx
|
|
// object has been finalized, and can't be used anymore.
|
|
// In that case, just return the already computed result.
|
|
if (!m_result.isEmpty()) {
|
|
return m_result;
|
|
}
|
|
|
|
QByteArray digest(EVP_MD_CTX_size(m_mdctx), '\0');
|
|
int err = EVP_DigestFinal_ex(m_mdctx, reinterpret_cast< unsigned char * >(digest.data()), nullptr);
|
|
if (err != 1) {
|
|
cleanupMdctx();
|
|
return QByteArray();
|
|
}
|
|
|
|
m_result = digest;
|
|
|
|
assert(!m_result.isEmpty());
|
|
|
|
return m_result;
|
|
}
|
|
|
|
QByteArray CryptographicHash::hash(const QByteArray &buf, Algorithm algo) {
|
|
CryptographicHash h(algo);
|
|
h.addData(buf);
|
|
return h.result();
|
|
}
|
|
|
|
QString CryptographicHash::humanReadableAlgorithmName(CryptographicHash::Algorithm algo) {
|
|
switch (algo) {
|
|
case CryptographicHash::Sha1:
|
|
return QLatin1String("SHA-1");
|
|
case CryptographicHash::Sha256:
|
|
return QLatin1String("SHA-256");
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
QString CryptographicHash::shortAlgorithmName(CryptographicHash::Algorithm algo) {
|
|
switch (algo) {
|
|
case CryptographicHash::Sha1:
|
|
return QLatin1String("sha1");
|
|
case CryptographicHash::Sha256:
|
|
return QLatin1String("sha256");
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
CryptographicHash::CryptographicHash() : d(nullptr) {
|
|
}
|
|
|
|
CryptographicHash::CryptographicHash(CryptographicHash::Algorithm algo) : d(nullptr) {
|
|
switch (algo) {
|
|
case CryptographicHash::Sha1:
|
|
d = new CryptographicHashPrivate(EVP_sha1());
|
|
break;
|
|
case CryptographicHash::Sha256:
|
|
d = new CryptographicHashPrivate(EVP_sha256());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CryptographicHash::addData(const QByteArray &buf) {
|
|
if (d) {
|
|
d->addData(buf);
|
|
}
|
|
}
|
|
|
|
QByteArray CryptographicHash::result() const {
|
|
if (d) {
|
|
return d->result();
|
|
}
|
|
return QByteArray();
|
|
}
|