shared-libraries/Qt-Secret/Qt-RSA/qrsaencryption.cpp

404 lines
12 KiB
C++

//#
//# Copyright (C) 2018-2019 QuasarApp.
//# Distributed under the lgplv3 software license, see the accompanying
//# Everyone is permitted to copy and distribute verbatim copies
//# of this license document, but changing it is not allowed.
//#
#include "qrsaencryption.h"
#include <QString>
#include <iostream>
#include <ctime>
#include <chrono>
#define KEY_GEN_LIMIT 10
const QString SIGN_MARKER = "-SIGN-";
const int signMarkerLength = SIGN_MARKER.length();
QRSAEncryption::INT eulerFunc(const QRSAEncryption::INT &p, const QRSAEncryption::INT &q) {
return (p - 1) * (q - 1);
}
bool QRSAEncryption::isMutuallyPrime(const INT &a, const INT &b) {
if ( (!(a % 2) && !(b % 2))
|| (!(a % 3) && !(b % 3))
|| (!(a % 5) && !(b % 5))
|| (!(a % 7) && !(b % 7))
) return false;
return INT().gcd(a, b) == 1;
}
QRSAEncryption::Rsa QRSAEncryption::getBitsSize(const INT &i) const {
int rsaBits = RSA_64;
int intBits = i.sizeBits();
while (rsaBits < intBits) {
rsaBits *= 2;
}
return static_cast<QRSAEncryption::Rsa>(rsaBits);
}
QRSAEncryption::Rsa QRSAEncryption::getBitsSize(const QByteArray &key) const {
if (isValidRsaKey(key)) {
return static_cast<QRSAEncryption::Rsa>(key.size() * 4);
}
return QRSAEncryption::Rsa::Invalid;
}
QRSAEncryption::INT QRSAEncryption::fromArray(const QByteArray &array) const {
INT res = 0;
res.fromHex(array.toHex().toStdString());
return res;
}
QByteArray QRSAEncryption::toArray(const INT &i, short sizeBlok) {
QByteArray res;
res = QByteArray::fromHex(QByteArray::fromStdString(i.getString(16)));
if (sizeBlok < 0) {
return res;
}
while (res.size() < sizeBlok) {
res.push_front(char(0));
}
return res.left(sizeBlok);
}
QRSAEncryption::INT QRSAEncryption::randomNumber(bool fullFill) const {
srand(std::chrono::duration_cast<std::chrono::nanoseconds>
(std::chrono::system_clock::now().time_since_epoch()).count()
% std::numeric_limits<int>::max());
INT res{1};
if(fullFill) {
while(res.longBits() < _rsa) {
res *= (rand() % (std::numeric_limits<int>::max() - 1)) + 1;
}
} else {
int longDiff = _rsa / (sizeof (int) * 8);
while (longDiff > 0) {
longDiff--;
res *= (rand() % (std::numeric_limits<int>::max() - 1)) + 1;
}
}
return res;
}
QRSAEncryption::INT QRSAEncryption::toPrime(INT n) const {
if (!(n % 2)) {
++n;
}
INT LN = n;
INT RN = n;
while (true) {
if (LN.isPrime(false)) return LN;
RN+=2;
if (RN.isPrime(false)) return RN;
LN-=2;
}
}
QRSAEncryption::INT QRSAEncryption::randomPrimeNumber(INT no) const {
srand(static_cast<unsigned int>(time(nullptr)));
// max INT
INT max('1', _rsa / 2, 2);
auto p = toPrime(randomNumber() % max);
while(p == no) p = toPrime(randomNumber() % max);
return p;
}
QRSAEncryption::INT QRSAEncryption::extEuclid(INT a, INT b) const {
INT x = 0, y = 1, u = 1, v = 0, gcd = b, m, n, q, r;
while (a != 0) {
q = gcd / a;
r = gcd % a;
m = x - u * q;
n = y - v * q;
gcd = a;
a = r;
x = u;
y = v;
u = m;
v = n;
}
return y;
}
short QRSAEncryption::getBlockSize(INT i) const {
return static_cast<short>(i.longBytes()) - 1;
}
QByteArray QRSAEncryption::encodeBlok(const INT &block, const INT &e, const INT &m, short blockSize) {
return toArray(INT::powm(block, e, m), blockSize);
}
QByteArray QRSAEncryption::decodeBlok(const INT &block, const INT &d, const INT &m, short blockSize) {
return toArray(INT::powm(block, d, m), blockSize);
}
QRSAEncryption::QRSAEncryption(Rsa rsa) {
_rsa = rsa;
}
bool QRSAEncryption::generatePairKeyS(QByteArray &pubKey, QByteArray &privKey, QRSAEncryption::Rsa rsa) {
qWarning() << "method " << Q_FUNC_INFO <<
" will be deleted in newxt version. please use generatePairKey method";
return generatePairKey(pubKey, privKey, rsa);
}
QByteArray QRSAEncryption::encodeS(const QByteArray &rawData, const QByteArray &pubKey, QRSAEncryption::Rsa rsa, QRSAEncryption::BlockSize blockSizeMode) {
qWarning() << "method " << Q_FUNC_INFO <<
" will be deleted in newxt version. please use encode method";
return encode(rawData, pubKey, rsa, blockSizeMode);
}
QByteArray QRSAEncryption::decodeS(const QByteArray &rawData, const QByteArray &privKey, QRSAEncryption::Rsa rsa, QRSAEncryption::BlockSize blockSizeMode) {
qWarning() << "method " << Q_FUNC_INFO <<
" will be deleted in newxt version. please use decode method";
return decode(rawData, privKey, rsa, blockSizeMode);
}
QByteArray QRSAEncryption::signMessageS(QByteArray rawData, const QByteArray &privKey, QRSAEncryption::Rsa rsa) {
qWarning() << "method " << Q_FUNC_INFO <<
" will be deleted in newxt version. please use signMessage method";
return signMessage(rawData, privKey, rsa);
}
bool QRSAEncryption::checkSignMessageS(const QByteArray &rawData, const QByteArray &pubKey, QRSAEncryption::Rsa rsa) {
qWarning() << "method " << Q_FUNC_INFO <<
" will be deleted in newxt version. please use signMessage method";
return checkSignMessage(rawData, pubKey, rsa);
}
unsigned int QRSAEncryption::getKeyBytesSize(QRSAEncryption::Rsa rsa) {
return rsa / 4;
}
// --- static methods ---
bool QRSAEncryption::generatePairKey(QByteArray &pubKey, QByteArray &privKey,
QRSAEncryption::Rsa rsa) {
return QRSAEncryption(rsa).generatePairKey(pubKey, privKey);
}
QByteArray QRSAEncryption::encode(const QByteArray &rawData, const QByteArray &pubKey,
Rsa rsa, BlockSize blockSizeMode) {
return QRSAEncryption(rsa).encode(rawData, pubKey, blockSizeMode);
}
QByteArray QRSAEncryption::decode(const QByteArray &rawData, const QByteArray &privKey,
Rsa rsa, BlockSize blockSizeMode) {
return QRSAEncryption(rsa).decode(rawData, privKey, blockSizeMode);
}
QByteArray QRSAEncryption::signMessage(QByteArray rawData, const QByteArray &privKey, Rsa rsa) {
return QRSAEncryption(rsa).signMessage(rawData, privKey);
}
bool QRSAEncryption::checkSignMessage(const QByteArray &rawData, const QByteArray &pubKey, Rsa rsa) {
return QRSAEncryption(rsa).checkSignMessage(rawData, pubKey);
}
// --- end of static methods ---
bool QRSAEncryption::generatePairKey(QByteArray &pubKey, QByteArray &privKey) {
int cnt{0};
bool keyGenRes{false};
INT p, q, modul, eilor, e, d;
do {
pubKey.clear();
privKey.clear();
p = randomPrimeNumber();
q = randomPrimeNumber(p);
modul = 0;
while ((modul = p * q) < 0) {
p = toPrime((p - 1) / 2);
}
eilor = eulerFunc(p, q);
e = randomNumber() % eilor;
if (!(e % 2)) --e;
do {
e -= 2;
} while((!isMutuallyPrime(eilor, e)));
d = extEuclid(eilor , e);
while(d < 0 ) {
d += eilor;
}
pubKey.append(toArray(e, _rsa / 8));
pubKey.append(toArray(modul, _rsa / 8));
privKey.append(toArray(d, _rsa / 8));
privKey.append(toArray(modul, _rsa / 8));
} while (!(keyGenRes = testKeyPair(pubKey, privKey)) && (++cnt < KEY_GEN_LIMIT));
if(cnt >= KEY_GEN_LIMIT) qWarning() << QString("(Warning): Exceeded limit of key generation (%0)!").arg(KEY_GEN_LIMIT);
return (keyGenRes && cnt < KEY_GEN_LIMIT);
}
// --- non-static methods ---
QByteArray QRSAEncryption::encode(const QByteArray &rawData, const QByteArray &pubKey, BlockSize blockSizeMode) {
if (getBitsSize(pubKey) != _rsa) {
return QByteArray();
}
int index = 0;
QByteArray block;
INT e = fromArray(pubKey.mid(0, pubKey.size() / 2));
INT m = fromArray(pubKey.mid(pubKey.size() / 2));
short blockSizeOut = getBlockSize(m) + 1; // BlockSize::OneByte
short blockSizeIn = 1; // BlockSize::OneByte
if (blockSizeMode == BlockSize::Auto) {
blockSizeIn = getBlockSize(m);
}
if (!blockSizeIn) {
qDebug() << "module of key small! size = 1 byte, 2 byte is minimum";
return QByteArray();
}
QByteArray res;
while ((block = rawData.mid(index, blockSizeIn)).size()) {
if (index + blockSizeIn > rawData.size() && block.size() && !block[0]) {
qWarning() << "When trying to encrypt data, problems arose, the last block contains non-significant zeros."
" These zeros will be deleted during the decryption process."
" For encode and decode data with non-significant zeros use BlockSize::OneByte";
}
res.append(encodeBlok(fromArray(block), e, m, blockSizeOut));
index += blockSizeIn;
}
return res;
}
QByteArray QRSAEncryption::decode(const QByteArray &rawData, const QByteArray &privKey, BlockSize blockSizeMode) {
if (getBitsSize(privKey) != _rsa) {
return QByteArray();
}
int index = 0;
QByteArray block;
INT d = fromArray(privKey.mid(0, privKey.size() / 2));
INT m = fromArray(privKey.mid(privKey.size() / 2));
short blockSizeIn = getBlockSize(m) + 1;
short blockSizeOut = 1; // BlockSize::OneByte
if (blockSizeMode == BlockSize::Auto) {
blockSizeOut = getBlockSize(m);
}
QByteArray res;
while ((block = rawData.mid(index, blockSizeIn)).size()) {
bool isLastBlock = (index + blockSizeIn) >= rawData.size();
res.append(decodeBlok(fromArray(block), d, m,
(isLastBlock && blockSizeMode == BlockSize::Auto)? -1 : blockSizeOut));
index += blockSizeIn;
}
return res;
}
QByteArray QRSAEncryption::signMessage(QByteArray rawData, const QByteArray &privKey) {
QByteArray hash = QCryptographicHash::hash(rawData, HashAlgorithm::Sha256);
QByteArray signature = encode(hash, privKey, BlockSize::OneByte);
rawData.append(SIGN_MARKER + signature.toHex() + SIGN_MARKER);
return rawData;
}
bool QRSAEncryption::checkSignMessage(const QByteArray &rawData, const QByteArray &pubKey) {
// start position of SIGN_MARKER in rawData
auto signStartPos = rawData.lastIndexOf(SIGN_MARKER, rawData.length() - signMarkerLength - 1);
// length of signature in rawData
auto signLength = rawData.length() - signStartPos - signMarkerLength * 2;
// message, that was recieved from channel
QByteArray message = rawData.left(signStartPos);
// hash, that was decrypt from recieved signature
QByteArray recievedHash = decode(QByteArray::fromHex(rawData.mid(signStartPos + signMarkerLength, signLength)),
pubKey, BlockSize::OneByte);
// if recievedHash == hashAlgorithm(recived message), then signed message is valid
return recievedHash == QCryptographicHash::hash(message, HashAlgorithm::Sha256);
}
QRSAEncryption::Rsa QRSAEncryption::getRsa() const {
return _rsa;
}
bool QRSAEncryption::testKeyPair(const QByteArray &pubKey, const QByteArray &privKey) {
QByteArray tesVal = "Test message of encrypkey";
bool result = tesVal == decode(encode(tesVal, pubKey), privKey);
if (!result) qWarning() << "(Warning): Testkey Fail, try generate new key pair!";
return result;
}
// --- end of non-static methods ---
bool QRSAEncryption::isValidRsaKey(const QByteArray &key) {
return key.size() && ((static_cast<unsigned int>(key.size()) % getKeyBytesSize(RSA_64)) == 0);
}