Skip to content

Commit

Permalink
Test export functionality and indirectly the import
Browse files Browse the repository at this point in the history
  • Loading branch information
chris2511 committed Jan 22, 2024
1 parent 2f482fc commit 469d24e
Show file tree
Hide file tree
Showing 7 changed files with 554 additions and 72 deletions.
3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

list(APPEND srcs PwDialogMock.h importPEM.cpp main.cpp main.h newKey.cpp pem.cpp)
list(APPEND srcs PwDialogMock.h importPEM.cpp main.cpp main.h
newKey.cpp pem.cpp export.cpp)

list(TRANSFORM srcs PREPEND ${PROJECT_SOURCE_DIR}/test/)

Expand Down
30 changes: 21 additions & 9 deletions test/PwDialogMock.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* All rights reserved.
*/

#ifndef __PWDIALOGMOCK_H
#define __PWDIALOGMOCK_H
#include <QDebug>

#include "lib/debug_info.h"
Expand Down Expand Up @@ -42,17 +44,27 @@ class PwDialogMock: public PwDialogUI_i
pwe->pi.setTitle(p->getTitle());
pwe->pi.setDescription(p->getDescription());

qWarning() << "PwDialogMock" << p->getDescription() << expect_idx;
qWarning() << "PwDialogMock" << p->getDescription() << expect_idx
<< "Password:" << pwe->pass_return;
*passwd = pwe->pass_return;
return pwe->result;
}

~PwDialogMock()
{
qDeleteAll(pw_expectations);
}

public:
int expect_idx{};
QList<pw_expect*> pw_expectations{};
void setExpectations(const QList<pw_expect*> pwe)
{
qDeleteAll(pw_expectations);
pw_expectations = pwe;
expect_idx = 0;
}

int expect_idx{};
QList<pw_expect*> pw_expectations{};

void setExpectations(const QList<pw_expect*> pwe)
{
qDeleteAll(pw_expectations);
pw_expectations = pwe;
expect_idx = 0;
}
};
#endif
305 changes: 305 additions & 0 deletions test/export.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
/* vi: set sw=4 ts=4:
*
* Copyright (C) 2023 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) {
QWARN(qPrintable(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 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());
}
}
Loading

0 comments on commit 469d24e

Please sign in to comment.