mirror of
https://github.com/chris2511/xca.git
synced 2026-09-13 18:46:25 +05:00
315 lines
8.8 KiB
C++
315 lines
8.8 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2024 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <QTest>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "lib/pki_multi.h"
|
|
#include "lib/db_x509.h"
|
|
#include "lib/pki_x509.h"
|
|
#include "lib/xfile.h"
|
|
#include "lib/database_model.h"
|
|
|
|
#include <widgets/MainWindow.h>
|
|
#include "main.h"
|
|
|
|
void check_pems(const QString &name, int n, QStringList matches = QStringList())
|
|
{
|
|
int begin = 0, end = 0;
|
|
qWarning() << "Expecting" << n << "PEMs in" << name;
|
|
|
|
#if 0
|
|
// This is an endless loop: open_read() succeeds,
|
|
// but isOpen returns false. Stop investigating, use POSIX open()
|
|
XFile F(name);
|
|
while (!F.isOpen()) {
|
|
qWarning() << "OPEN" << name;
|
|
F.close();
|
|
Q_ASSERT(F.open_read());
|
|
}
|
|
QByteArray all = F.readAll();
|
|
#else
|
|
int fd = open(qPrintable(name), O_RDONLY);
|
|
Q_ASSERT(fd != -1);
|
|
char buf[65536];
|
|
ssize_t ret = read(fd, buf, sizeof buf);
|
|
Q_ASSERT(ret != -1);
|
|
QByteArray all(buf, ret);
|
|
close(fd);
|
|
#endif
|
|
qWarning() << "ALL" << name << all.size();
|
|
|
|
foreach(QByteArray b, all.split('\n')) {
|
|
if (b.indexOf("-----BEGIN ") == 0)
|
|
begin++;
|
|
if (b.indexOf("-----END ") == 0)
|
|
end++;
|
|
|
|
QMutableStringListIterator i(matches);
|
|
while (i.hasNext()) {
|
|
QByteArray match = i.next().toUtf8();
|
|
if (b.indexOf(match) != -1)
|
|
i.remove();
|
|
}
|
|
}
|
|
QCOMPARE(begin, n);
|
|
QCOMPARE(end, n);
|
|
foreach(QString m, matches) {
|
|
qWarning() << QString("Pattern %1 not found in %2").arg(m).arg(name);
|
|
}
|
|
QCOMPARE(matches.size(), 0);
|
|
}
|
|
|
|
void verify_key(const QString &name, QList<unsigned> hashes, bool priv)
|
|
{
|
|
pki_multi *pems = new pki_multi();
|
|
QVERIFY(pems != nullptr);
|
|
pems->probeAnything(name);
|
|
QCOMPARE(pems->get().size(), hashes.size());
|
|
foreach (pki_base *pki, pems->get()) {
|
|
unsigned hash = pki->hash();
|
|
qWarning() << pki->getIntName() << hash;
|
|
QVERIFY2(hashes.contains(hash),
|
|
qPrintable(QString("%1 not expected in %2")
|
|
.arg(pki->getIntName())
|
|
.arg(name)
|
|
)
|
|
);
|
|
pki_key *key = dynamic_cast<pki_key*>(pki);
|
|
if (key) {
|
|
QCOMPARE(key->isPrivKey(), priv);
|
|
}
|
|
}
|
|
}
|
|
|
|
void verify_file(const QString &name, QList<unsigned> hashes)
|
|
{
|
|
verify_key(name, hashes, false);
|
|
}
|
|
|
|
void export_by_id(int id, const QString &name,
|
|
QModelIndexList &list, db_base *db)
|
|
{
|
|
const pki_export *xport = pki_export::by_id(id);
|
|
QVERIFY(xport != nullptr);
|
|
XFile F(name);
|
|
F.open_write();
|
|
if (xport->match_all(F_PEM)) {
|
|
QString prefix = QString("%1\n").arg(xport->help);
|
|
foreach (QModelIndex idx, list) {
|
|
pki_base *pki = db_base::fromIndex(idx);
|
|
QVERIFY(pki != nullptr);
|
|
prefix += QString(" - %1[%2]\n")
|
|
.arg(pki->getIntName())
|
|
.arg(pki->getTypeString());
|
|
}
|
|
F.write(prefix.toUtf8());
|
|
}
|
|
db->exportItems(list, xport, F);
|
|
F.close();
|
|
}
|
|
|
|
void test_main::exportFormat()
|
|
{
|
|
int l=0;
|
|
QModelIndex idx;
|
|
QModelIndexList list;
|
|
|
|
try {
|
|
|
|
ign_openssl_error();
|
|
openDB();
|
|
dbstatus();
|
|
pki_multi *pem = new pki_multi();
|
|
QString all = pemdata["Inter CA 1"] +
|
|
pemdata["Inter CA 1 Key"] +
|
|
pemdata["Root CA"] +
|
|
pemdata["Endentity"];
|
|
|
|
pem->fromPEMbyteArray(all.toUtf8(), QString());
|
|
QCOMPARE(pem->failed_files.count(), 0);
|
|
Database.insert(pem);
|
|
dbstatus();
|
|
|
|
db_base *certs = Database.model<db_x509>();
|
|
QVERIFY(certs != nullptr);
|
|
|
|
// Root CA as only item: No chain, no private key
|
|
idx = certs->index(certs->getByName("Root CA"));
|
|
list << idx;
|
|
QCOMPARE(certs->exportFlags(idx) , F_CHAIN | F_PRIVATE);
|
|
QCOMPARE(certs->exportFlags(list) , F_CHAIN | F_PRIVATE | F_MULTI);
|
|
|
|
// Inter CA 1: All export options permitted
|
|
// Together with "Root CA" in "list": No chain, private or single
|
|
idx = certs->index(certs->getByName("Inter CA 1"));
|
|
list << idx;
|
|
QCOMPARE(certs->exportFlags(idx) , 0);
|
|
QCOMPARE(certs->exportFlags(list) , F_CHAIN | F_PRIVATE | F_SINGLE);
|
|
|
|
// Endentity has no private key and id no CA
|
|
idx = certs->index(certs->getByName("Endentity"));
|
|
list << idx;
|
|
QVERIFY(idx.isValid());
|
|
QCOMPARE(certs->exportFlags(idx) , F_PRIVATE | F_CA);
|
|
|
|
pki_key *key = new pki_evp();
|
|
key->fromPEMbyteArray(pemdata["Endentity Key"].toUtf8(), QString());
|
|
openssl_error();
|
|
Database.insert(key);
|
|
dbstatus();
|
|
|
|
// Endentity now has a private key, but is still no CA
|
|
QCOMPARE(certs->exportFlags(idx) , F_CA);
|
|
|
|
#define ROOT_HASH 531145749
|
|
#define INTER_HASH 376625776
|
|
#define END_HASH 94304590
|
|
#define ENDKEY_HASH 1121702347
|
|
|
|
#define xstr(s) str(s)
|
|
#define str(s) #s
|
|
#define AUTOFILE(type) "_" # type "_Line" xstr(__LINE__) ".data" ;
|
|
|
|
const char *file = AUTOFILE(ALLCERT)
|
|
// Export All certs in one PEM File
|
|
export_by_id(3, file, list, certs);
|
|
verify_file(file, QList<unsigned> { ROOT_HASH, INTER_HASH, END_HASH });
|
|
check_pems(file, 3);
|
|
// Export 2 cert Chain from Inter CA1
|
|
file = AUTOFILE(CERTCHAIN)
|
|
list.clear();
|
|
list << certs->index(certs->getByName("Inter CA 1"));
|
|
export_by_id(2, file, list, certs);
|
|
verify_file(file, QList<unsigned> { ROOT_HASH, INTER_HASH });
|
|
check_pems(file, 2);
|
|
|
|
// Export 3 cert Chain from Endentity
|
|
file = AUTOFILE(CERTCHAIN)
|
|
list.clear();
|
|
list << certs->index(certs->getByName("Endentity"));
|
|
export_by_id(2, file, list, certs);
|
|
verify_file(file, QList<unsigned> { ROOT_HASH, INTER_HASH, END_HASH });
|
|
check_pems(file, 3);
|
|
|
|
// Export Endentity + corresponding key
|
|
file = AUTOFILE(CERTKEY)
|
|
export_by_id(6, file, list, certs);
|
|
verify_key(file, QList<unsigned> { END_HASH, ENDKEY_HASH }, true);
|
|
check_pems(file, 2, QStringList { " RSA PRIVATE KEY-", " CERTIFICATE-" });
|
|
|
|
// Export Endentity + corresponding PKCS#8 key
|
|
file = AUTOFILE(CERTPK8)
|
|
pwdialog->setExpectations(QList<pw_expect*>{
|
|
new pw_expect("pass", pw_ok),
|
|
new pw_expect("pass", pw_ok),
|
|
});
|
|
export_by_id(7, file, list, certs);
|
|
verify_key(file, QList<unsigned> { END_HASH, ENDKEY_HASH }, true);
|
|
check_pems(file, 2, QStringList { " ENCRYPTED PRIVATE KEY-", " CERTIFICATE-" });
|
|
// Export OpenVPN format
|
|
file = AUTOFILE(OPENVPN)
|
|
export_by_id(4, file, list, certs);
|
|
verify_key(file, QList<unsigned> {
|
|
ROOT_HASH, INTER_HASH, END_HASH, ENDKEY_HASH }, true);
|
|
check_pems(file, 4, QStringList { " RSA PRIVATE KEY-",
|
|
" CERTIFICATE-", " CERTIFICATE-"," CERTIFICATE-",
|
|
"<ca>", "</ca>", "<extra-certs>", "</extra-certs>",
|
|
"<cert>", "</cert>", "<key>", "</key>" });
|
|
// Export Endentity as PKCS#7
|
|
file = AUTOFILE(CERTP7)
|
|
export_by_id(8, file, list, certs);
|
|
verify_file(file, QList<unsigned> { END_HASH });
|
|
check_pems(file, 0);
|
|
// Export Endentity as PKCS#7 chain
|
|
file = AUTOFILE(CERTP7)
|
|
export_by_id(12, file, list, certs);
|
|
verify_file(file, QList<unsigned> { ROOT_HASH, INTER_HASH, END_HASH });
|
|
check_pems(file, 0);
|
|
// Export Endentity as DER certificate
|
|
export_by_id(13, file, list, certs);
|
|
verify_file(file, QList<unsigned> { END_HASH });
|
|
check_pems(file, 0);
|
|
|
|
// Export Endentity key
|
|
list.clear();
|
|
key = dynamic_cast<pki_x509*>(certs->getByName("Endentity"))->getRefKey();
|
|
db_base *keys = Database.model<db_key>();
|
|
list << keys->index(key);
|
|
|
|
// Public Key
|
|
file = AUTOFILE(PUBKEY)
|
|
export_by_id(19, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, false);
|
|
check_pems(file, 1, QStringList{ "PUBLIC KEY" });
|
|
|
|
// Private Key
|
|
file = AUTOFILE(PRIVKEY)
|
|
export_by_id(20, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
|
|
check_pems(file, 1, QStringList{ "RSA PRIVATE KEY" });
|
|
|
|
// Private Key Openssl Encrypted
|
|
file = AUTOFILE(PRIVKEY)
|
|
pwdialog->setExpectations(QList<pw_expect*>{
|
|
new pw_expect("pass", pw_ok),
|
|
new pw_expect("pass", pw_ok),
|
|
});
|
|
export_by_id(21, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
|
|
check_pems(file, 1, QStringList { "DEK-Info: ", "Proc-Type: 4,ENCRYPTED", "BEGIN RSA PRIVATE KEY" });
|
|
|
|
// Private SSH Key
|
|
file = AUTOFILE(PRIVSSH)
|
|
export_by_id(22, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
|
|
check_pems(file, 1, QStringList{ "RSA PRIVATE KEY" });
|
|
|
|
// Public SSH Key
|
|
file = AUTOFILE(PUBSSH)
|
|
export_by_id(23, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, false);
|
|
check_pems(file, 0, QStringList{ "ssh-rsa " });
|
|
|
|
// Public DER Key
|
|
file = AUTOFILE(PUBDER)
|
|
export_by_id(24, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, false);
|
|
check_pems(file, 0);
|
|
|
|
// Private DER Key
|
|
file = AUTOFILE(PRIVDER)
|
|
export_by_id(25, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
|
|
check_pems(file, 0);
|
|
|
|
// Private PVK Key
|
|
file = AUTOFILE(PVK)
|
|
export_by_id(26, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
|
|
check_pems(file, 0);
|
|
|
|
// Private PVK Key encrypted
|
|
file = AUTOFILE(PVK)
|
|
pwdialog->setExpectations(QList<pw_expect*>{
|
|
new pw_expect("pass", pw_ok),
|
|
new pw_expect("pass", pw_ok),
|
|
});
|
|
export_by_id(27, file, list, keys);
|
|
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
|
|
check_pems(file, 0);
|
|
|
|
} catch (...) {
|
|
QString m = QString("Exception thrown L %1").arg(l);
|
|
QVERIFY2(false, m.toUtf8().constData());
|
|
}
|
|
}
|