Welcome to mirror list, hosted at ThFree Co, Russian Federation.

OpData01.cpp « format « src - github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: d373e58142bb46dd49de43ed4390eceade483968 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 *  Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 or (at your option)
 *  version 3 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "OpData01.h"

#include "crypto/CryptoHash.h"
#include "crypto/SymmetricCipher.h"

#include <QDataStream>
#include <QDebug>

OpData01::OpData01(QObject* parent)
    : QObject(parent)
{
}

OpData01::~OpData01()
{
}

bool OpData01::decodeBase64(QString const& b64String, const QByteArray& key, const QByteArray& hmacKey)
{
    const QByteArray b64Bytes = QByteArray::fromBase64(b64String.toUtf8());
    return decode(b64Bytes, key, hmacKey);
}

bool OpData01::decode(const QByteArray& data, const QByteArray& key, const QByteArray& hmacKey)
{
    /*!
     * The first 8 bytes of the data are the string “opdata01”.
     */
    const QByteArray header("opdata01");
    if (!data.startsWith(header)) {
        m_errorStr = tr("Invalid OpData01, does not contain header");
        return false;
    }

    QDataStream in(data);
    in.setByteOrder(QDataStream::LittleEndian);
    in.skipRawData(header.size());

    /*!
     * The next 8 bytes contain the length in bytes of the plaintext as a little endian unsigned 64 bit integer.
     */
    qlonglong len;
    in >> len;

    /*!
     * The next 16 bytes are the randomly chosen initialization vector.
     */
    QByteArray iv(16, '\0');
    int read = in.readRawData(iv.data(), 16);
    if (read != 16) {
        m_errorStr = tr("Unable to read all IV bytes, wanted 16 but got %1").arg(iv.size());
        return false;
    }

    SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt);
    if (!cipher.init(key, iv)) {
        m_errorStr = tr("Unable to init cipher for opdata01: %1").arg(cipher.errorString());
        return false;
    }

    /*!
     * The plaintext is padded using the following scheme.
     *
     * If the size of the plaintext is an even multiple of the block size then 1 block of random data is prepended
     * to the plaintext. Otherwise, between 1 and 15 (inclusive) bytes of random data are prepended to the plaintext
     * to achieve an even multiple of blocks.
     */
    const int blockSize = cipher.blockSize();
    int randomBytes = blockSize - (len % blockSize);
    if (randomBytes == 0) {
        // add random block
        randomBytes = blockSize;
    }
    qlonglong clear_len = len + randomBytes;
    QByteArray qbaCT(clear_len, '\0');
    in.readRawData(qbaCT.data(), clear_len);

    /*!
     * The HMAC-SHA256 is computed over the entirety of the opdata including header, length, IV and ciphertext
     * using a 256-bit MAC key. The 256-bit MAC is not truncated. It is appended to the ciphertext.
     */
    const int hmacLen = 256 / 8;
    QByteArray hmacSig(hmacLen, '\0'); // 256 / 8, '\0');
    in.readRawData(hmacSig.data(), hmacLen);
    if (hmacSig.size() != hmacLen) {
        m_errorStr = tr("Unable to read all HMAC signature bytes");
        return false;
    }

    const QByteArray hmacData = data.mid(0, data.size() - hmacSig.size());
    const QByteArray actualHmac = CryptoHash::hmac(hmacData, hmacKey, CryptoHash::Algorithm::Sha256);
    if (actualHmac != hmacSig) {
        m_errorStr = tr("Malformed OpData01 due to a failed HMAC");
        return false;
    }

    if (!cipher.processInPlace(qbaCT)) {
        m_errorStr = tr("Unable to process clearText in place");
        return false;
    }

    // Remove random bytes
    const QByteArray& clearText = qbaCT.mid(randomBytes);
    if (clearText.size() != len) {
        m_errorStr = tr("Expected %1 bytes of clear-text, found %2").arg(len, clearText.size());
        return false;
    }
    m_clearText = clearText;
    return true;
}

QByteArray OpData01::getClearText()
{
    return m_clearText;
}

QString OpData01::errorString()
{
    return m_errorStr;
}