14#include <QJsonDocument>
16#include <QRegularExpression>
24#include <rnp/rnp_err.h>
29auto rnpError(
const QString &what, rnp_result_t result) -> QString {
30 return QStringLiteral(
"%1: %2").arg(
31 what, QString::fromUtf8(rnp_result_to_string(result)));
34auto rnpError(
const char *what, rnp_result_t result) -> QString {
35 return rnpError(QString::fromUtf8(what), result);
38void setError(QString *error,
const QString &message) {
39 if (error !=
nullptr) {
45 void operator()(rnp_input_st *input)
const { rnp_input_destroy(input); }
48 void operator()(rnp_output_st *output)
const { rnp_output_destroy(output); }
51 void operator()(rnp_key_handle_st *key)
const { rnp_key_handle_destroy(key); }
53using Input = std::unique_ptr<rnp_input_st, InputDeleter>;
54using Output = std::unique_ptr<rnp_output_st, OutputDeleter>;
55using Key = std::unique_ptr<rnp_key_handle_st, KeyDeleter>;
58auto memoryInput(
const QByteArray &data, QString *error,
const char *what)
60 rnp_input_t input =
nullptr;
61 const rnp_result_t result = rnp_input_from_memory(
62 &input,
reinterpret_cast<const uint8_t *
>(data.constData()),
63 static_cast<size_t>(data.size()),
false);
64 if (result != RNP_SUCCESS) {
65 setError(error, rnpError(what, result));
73auto memoryOutput(QString *error,
const char *what, qint64 maxBytes = 0)
75 rnp_output_t output =
nullptr;
76 const rnp_result_t result =
77 rnp_output_to_memory(&output,
static_cast<size_t>(maxBytes));
78 if (result != RNP_SUCCESS) {
79 setError(error, rnpError(what, result));
82 return Output(output);
86auto outputBytes(rnp_output_t output) -> std::optional<QByteArray> {
87 uint8_t *buf =
nullptr;
89 if (rnp_output_memory_get_buf(output, &buf, &len,
false) != RNP_SUCCESS) {
92 return QByteArray(
reinterpret_cast<const char *
>(buf),
93 static_cast<qsizetype
>(len));
97auto isValidKey(rnp_key_handle_t key) ->
bool {
99 return rnp_key_is_valid(key, &valid) == RNP_SUCCESS && valid;
102auto allowsEncryption(rnp_key_handle_t key) ->
bool {
103 bool allowed =
false;
104 return rnp_key_allows_usage(key,
"encrypt", &allowed) == RNP_SUCCESS &&
114auto canEncryptTo(rnp_key_handle_t key) ->
bool {
115 if (!isValidKey(key)) {
118 if (allowsEncryption(key)) {
122 rnp_key_get_subkey_count(key, &subkeys);
123 for (
size_t i = 0; i < subkeys; ++i) {
124 rnp_key_handle_t handle =
nullptr;
125 if (rnp_key_get_subkey_at(key, i, &handle) != RNP_SUCCESS ||
129 const Key subkey(handle);
130 if (isValidKey(handle) && allowsEncryption(handle)) {
143auto secretIsHere(rnp_key_handle_t key) ->
bool {
145 if (rnp_key_have_secret(key, &secret) != RNP_SUCCESS || !secret) {
148 char *type =
nullptr;
149 if (rnp_key_get_protection_type(key, &type) != RNP_SUCCESS ||
153 const QString protection = QString::fromLatin1(type);
154 rnp_buffer_destroy(type);
155 return protection != QLatin1String(
"GPG-None") &&
156 protection != QLatin1String(
"GPG-Smartcard") &&
157 protection != QLatin1String(
"Unknown");
160auto isProtected(rnp_key_handle_t key) ->
bool {
162 return rnp_key_is_protected(key, &locked) == RNP_SUCCESS && locked;
171template <
typename Visit>
172auto forEachSecretPart(rnp_key_handle_t key, Visit visit) ->
int {
174 if (secretIsHere(key)) {
181 rnp_key_get_subkey_count(key, &subkeys);
182 for (
size_t i = 0; i < subkeys; ++i) {
183 rnp_key_handle_t handle =
nullptr;
184 if (rnp_key_get_subkey_at(key, i, &handle) != RNP_SUCCESS ||
188 const Key subkey(handle);
189 if (secretIsHere(handle)) {
191 if (!visit(handle)) {
204auto signingKeyOf(rnp_key_handle_t key) -> Key {
205 if (!isValidKey(key)) {
208 rnp_key_handle_t handle =
nullptr;
209 if (rnp_key_get_default_key(key,
"sign", 0, &handle) != RNP_SUCCESS ||
214 if (!isValidKey(handle) || !secretIsHere(handle)) {
221enum class Usage { Encrypt, Sign };
223auto usableFor(rnp_key_handle_t primary, Usage usage) ->
bool {
224 return usage == Usage::Encrypt ? canEncryptTo(primary)
225 : signingKeyOf(primary) != nullptr;
229auto primaryFingerprint(rnp_key_handle_t key) -> QString {
230 bool primary =
false;
231 rnp_key_is_primary(key, &primary);
233 if ((primary ? rnp_key_get_fprint(key, &fpr)
234 : rnp_key_get_primary_fprint(key, &fpr)) != RNP_SUCCESS) {
237 QString result = QString::fromLatin1(fpr);
238 rnp_buffer_destroy(fpr);
248auto liveUserIds(rnp_key_handle_t key) -> QStringList {
251 rnp_key_get_uid_count(key, &uids);
252 for (
size_t i = 0; i < uids; ++i) {
253 rnp_uid_handle_t handle =
nullptr;
254 if (rnp_key_get_uid_handle_at(key, i, &handle) != RNP_SUCCESS ||
261 const bool known = rnp_uid_is_valid(handle, &valid) == RNP_SUCCESS &&
262 rnp_uid_is_revoked(handle, &revoked) == RNP_SUCCESS;
263 rnp_uid_handle_destroy(handle);
265 if (!known || !valid || revoked ||
266 rnp_key_get_uid_at(key, i, &uid) != RNP_SUCCESS) {
269 live << QString::fromUtf8(uid);
270 rnp_buffer_destroy(uid);
276template <
typename Hit>
277auto anyUserIdMatches(rnp_key_handle_t key, Hit hit) ->
bool {
278 const QStringList uids = liveUserIds(key);
279 return std::any_of(uids.cbegin(), uids.cend(), hit);
283auto mailboxOf(
const QString &uid) -> QString {
284 const qsizetype open = uid.lastIndexOf(QLatin1Char(
'<'));
285 const qsizetype close = uid.lastIndexOf(QLatin1Char(
'>'));
286 return open >= 0 && close > open ? uid.mid(open + 1, close - open - 1)
295template <
typename Match>
296auto matchingPrimaries(rnp_ffi_t ffi, Usage usage, Match match) -> QStringList {
297 QStringList primaries;
298 rnp_identifier_iterator_t it =
nullptr;
299 if (rnp_identifier_iterator_create(ffi, &it,
"fingerprint") != RNP_SUCCESS) {
302 const char *fpr =
nullptr;
303 while (rnp_identifier_iterator_next(it, &fpr) == RNP_SUCCESS &&
305 rnp_key_handle_t handle =
nullptr;
306 if (rnp_locate_key(ffi,
"fingerprint", fpr, &handle) != RNP_SUCCESS ||
310 const Key key(handle);
311 if (!match(handle)) {
314 const QString primary = primaryFingerprint(handle);
315 rnp_key_handle_t primaryHandle =
nullptr;
316 if (primary.isEmpty() || primaries.contains(primary) ||
317 rnp_locate_key(ffi,
"fingerprint", primary.toLatin1().constData(),
318 &primaryHandle) != RNP_SUCCESS ||
319 primaryHandle ==
nullptr) {
322 const Key primaryKey(primaryHandle);
323 if (usableFor(primaryHandle, usage)) {
324 primaries << primary;
327 rnp_identifier_iterator_destroy(it);
338auto locateKey(rnp_ffi_t ffi,
const QString &recipient, QString *why,
339 Usage usage = Usage::Encrypt) -> Key {
340 const QString
id = recipient.trimmed();
342 setError(why, QStringLiteral(
"Empty recipient"));
349 static const QRegularExpression blanks(QStringLiteral(
"\\s+"));
350 static const QRegularExpression hex(QStringLiteral(
"^[0-9A-Fa-f]+$"));
351 const auto withoutPrefix = [](
const QString &text) {
352 return text.startsWith(QLatin1String(
"0x"), Qt::CaseInsensitive) &&
353 hex.match(text.mid(2)).hasMatch()
357 QString fingerprint = id;
358 fingerprint.remove(blanks);
359 fingerprint = withoutPrefix(fingerprint);
360 const QString keyId = withoutPrefix(
id);
361 if (keyId.compare(QLatin1String(
"0x"), Qt::CaseInsensitive) == 0) {
362 setError(why, QStringLiteral(
"Empty recipient"));
365 const bool isHexId = hex.match(keyId).hasMatch();
366 const char *type =
nullptr;
369 if (hex.match(fingerprint).hasMatch() &&
370 (fingerprint.size() == 40 || fingerprint.size() == 64)) {
371 type =
"fingerprint";
372 lookup = fingerprint;
373 }
else if (isHexId && keyId.size() == 16) {
377 if (type !=
nullptr) {
378 rnp_key_handle_t handle =
nullptr;
379 rnp_locate_key(ffi, type, lookup.toUpper().toLatin1().constData(), &handle);
380 if (handle ==
nullptr) {
381 setError(why, (usage == Usage::Encrypt
382 ? QStringLiteral(
"No public key for %1")
383 : QStringLiteral(
"No secret key for %1"))
390 const QString primary = primaryFingerprint(handle);
391 rnp_key_handle_t primaryHandle =
nullptr;
392 rnp_locate_key(ffi,
"fingerprint", primary.toLatin1().constData(),
394 const Key primaryKey(primaryHandle);
395 if (primaryHandle ==
nullptr || !usableFor(primaryHandle, usage)) {
397 (usage == Usage::Encrypt
398 ? QStringLiteral(
"The key %1 cannot be encrypted to: it "
399 "is expired or revoked, or has no valid "
401 : QStringLiteral(
"The key %1 cannot sign: it is expired "
402 "or revoked, has no valid signing key, "
403 "or its secret key is not here"))
410 QStringList primaries;
411 if (isHexId && keyId.size() == 8) {
413 const QString shortId = keyId.toUpper();
414 primaries = matchingPrimaries(ffi, usage, [&shortId](rnp_key_handle_t key) {
415 char *keyId =
nullptr;
416 if (rnp_key_get_keyid(key, &keyId) != RNP_SUCCESS) {
419 const bool hit = QString::fromLatin1(keyId).endsWith(shortId);
420 rnp_buffer_destroy(keyId);
423 }
else if (
static const QRegularExpression address(QStringLiteral(
424 R
"(^(?:<[^\s<>@]+@[^\s<>@]+>|[^\s<>@]+@[^\s<>@]+)$)"));
425 address.match(id).hasMatch()) {
431 QString mailbox = id;
432 mailbox.remove(QLatin1Char(
'<')).remove(QLatin1Char(
'>'));
433 primaries = matchingPrimaries(ffi, usage, [&mailbox](rnp_key_handle_t key) {
434 return anyUserIdMatches(key, [&mailbox](
const QString &uid) {
435 return mailboxOf(uid).compare(mailbox, Qt::CaseInsensitive) == 0;
443 primaries = matchingPrimaries(ffi, usage, [&
id](rnp_key_handle_t key) {
444 return anyUserIdMatches(key,
445 [&
id](
const QString &uid) {
return uid == id; });
447 if (primaries.isEmpty()) {
448 primaries = matchingPrimaries(ffi, usage, [&
id](rnp_key_handle_t key) {
449 return anyUserIdMatches(key, [&
id](
const QString &uid) {
450 return uid.contains(
id, Qt::CaseInsensitive);
455 if (primaries.size() > 1) {
457 QStringLiteral(
"%1 matches more than one key").arg(recipient));
460 if (primaries.isEmpty()) {
461 setError(why, (usage == Usage::Encrypt
462 ? QStringLiteral(
"No usable public key for %1")
463 : QStringLiteral(
"No usable secret key for %1"))
467 rnp_key_handle_t key =
nullptr;
468 rnp_locate_key(ffi,
"fingerprint", primaries.first().toLatin1().constData(),
474auto passwordCallback(rnp_ffi_t ,
void *appCtx, rnp_key_handle_t key,
477 size_t bufLen) ->
bool {
478 const auto *provider =
480 if (provider ==
nullptr || !*provider || bufLen == 0) {
484 char *rawId =
nullptr;
485 if (rnp_key_get_keyid(key, &rawId) == RNP_SUCCESS) {
486 keyId = QString::fromLatin1(rawId);
487 rnp_buffer_destroy(rawId);
489 QByteArray passphrase = (*provider)(keyId).toUtf8();
490 if (passphrase.isEmpty() ||
491 static_cast<size_t>(passphrase.size()) >= bufLen) {
492 passphrase.fill(
'\0');
495 std::memcpy(buf, passphrase.constData(),
496 static_cast<size_t>(passphrase.size()));
497 buf[passphrase.size()] =
'\0';
498 passphrase.fill(
'\0');
505 rnp_ffi_t ffi =
nullptr;
506 if (rnp_ffi_create(&ffi,
"GPG",
"GPG") == RNP_SUCCESS) {
513 if (
m_ffi !=
nullptr) {
514 rnp_ffi_destroy(
m_ffi);
527 std::min<qint64>(bytes, std::numeric_limits<quint32>::max());
537 if (
m_ffi ==
nullptr) {
538 setError(error, QStringLiteral(
"RNP is not available"));
541 Input input = memoryInput(keyData, error,
"Cannot read the key data");
545 char *json =
nullptr;
546 const rnp_result_t result = rnp_import_keys(
547 m_ffi, input.get(), RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS,
549 if (result != RNP_SUCCESS) {
550 setError(error, rnpError(
"Cannot import the keys", result));
553 QJsonParseError parseError{};
554 const QJsonDocument doc = QJsonDocument::fromJson(json, &parseError);
555 rnp_buffer_destroy(json);
556 const QJsonValue keysValue = doc.object().value(QStringLiteral(
"keys"));
557 if (parseError.error != QJsonParseError::NoError || !keysValue.isArray()) {
559 setError(error, QStringLiteral(
"Cannot read RNP's import report: %1")
560 .arg(parseError.error != QJsonParseError::NoError
561 ? parseError.errorString()
562 : QStringLiteral(
"no key list")));
565 QStringList primaries;
566 const QJsonArray keys = keysValue.toArray();
567 for (
const auto &entry : keys) {
569 entry.toObject().value(QStringLiteral(
"fingerprint")).toString();
570 rnp_key_handle_t key =
nullptr;
571 if (rnp_locate_key(
m_ffi,
"fingerprint", fpr.toLatin1().constData(),
572 &key) != RNP_SUCCESS ||
576 bool primary =
false;
577 rnp_key_is_primary(key, &primary);
578 rnp_key_handle_destroy(key);
579 if (primary && !primaries.contains(fpr)) {
587 const QStringList &recipients, QString *error)
589 if (
m_ffi ==
nullptr) {
590 setError(error, QStringLiteral(
"RNP is not available"));
593 if (recipients.isEmpty()) {
594 setError(error, QStringLiteral(
"No recipients"));
597 Input input = memoryInput(plaintext, error,
"Cannot read the entry");
598 Output output = memoryOutput(error,
"Cannot encrypt");
599 if (!input || !output) {
602 rnp_op_encrypt_t op =
nullptr;
603 rnp_result_t result =
604 rnp_op_encrypt_create(&op,
m_ffi, input.get(), output.get());
605 if (result != RNP_SUCCESS) {
606 setError(error, rnpError(
"Cannot encrypt", result));
609 const std::unique_ptr<rnp_op_encrypt_st, rnp_result_t (*)(rnp_op_encrypt_t)>
610 guard(op, rnp_op_encrypt_destroy);
611 for (
const QString &recipient : recipients) {
613 const Key key = locateKey(
m_ffi, recipient, &why);
615 setError(error, why);
618 result = rnp_op_encrypt_add_recipient(op, key.get());
619 if (result != RNP_SUCCESS) {
621 rnpError(QStringLiteral(
"Cannot encrypt to %1").arg(recipient),
626 rnp_op_encrypt_set_armor(op,
false);
627 result = rnp_op_encrypt_execute(op);
628 if (result != RNP_SUCCESS) {
629 setError(error, rnpError(
"Cannot encrypt", result));
632 std::optional<QByteArray> cipher = outputBytes(output.get());
634 setError(error, QStringLiteral(
"Cannot encrypt: no output from RNP"));
645 if (
m_ffi ==
nullptr) {
646 setError(error, QStringLiteral(
"RNP is not available"));
649 Input input = memoryInput(ciphertext, error,
"Cannot read the entry");
650 Output output = memoryOutput(error,
"Cannot decrypt",
m_maxPlaintext);
651 if (!input || !output) {
657 rnp_op_verify_t op =
nullptr;
658 rnp_result_t result =
659 rnp_op_verify_create(&op,
m_ffi, input.get(), output.get());
660 if (result != RNP_SUCCESS) {
661 setError(error, rnpError(
"Cannot decrypt", result));
664 const std::unique_ptr<rnp_op_verify_st, rnp_result_t (*)(rnp_op_verify_t)>
665 guard(op, rnp_op_verify_destroy);
668 rnp_op_verify_set_flags(op, RNP_VERIFY_IGNORE_SIGS_ON_DECRYPT);
669 result = rnp_op_verify_execute(op);
670 if (result != RNP_SUCCESS) {
671 setError(error, rnpError(
"Cannot decrypt", result));
674 char *mode =
nullptr;
675 bool protectedOk =
false;
676 if (rnp_op_verify_get_protection_info(op, &mode,
nullptr, &protectedOk) !=
680 QStringLiteral(
"Refusing a message without integrity "
682 .arg(QString::fromLatin1(mode !=
nullptr ? mode :
"unknown")));
683 rnp_buffer_destroy(mode);
686 rnp_buffer_destroy(mode);
687 std::optional<QByteArray> plain = outputBytes(output.get());
689 setError(error, QStringLiteral(
"Cannot decrypt: no output from RNP"));
701auto fingerprintOf(rnp_key_handle_t key) -> QString {
703 if (rnp_key_get_fprint(key, &fpr) != RNP_SUCCESS) {
706 QString result = QString::fromLatin1(fpr);
707 rnp_buffer_destroy(fpr);
711auto isRevoked(rnp_key_handle_t key) ->
bool {
712 bool revoked =
false;
713 return rnp_key_is_revoked(key, &revoked) == RNP_SUCCESS && revoked;
722auto firstSecretOfUnknownProtection(rnp_ffi_t ffi) -> std::optional<QString> {
724 rnp_identifier_iterator_t it =
nullptr;
725 if (rnp_identifier_iterator_create(ffi, &it,
"fingerprint") != RNP_SUCCESS) {
729 const char *fpr =
nullptr;
730 while (found.isEmpty() && !failed) {
731 if (rnp_identifier_iterator_next(it, &fpr) != RNP_SUCCESS) {
735 if (fpr ==
nullptr) {
738 rnp_key_handle_t handle =
nullptr;
739 if (rnp_locate_key(ffi,
"fingerprint", fpr, &handle) != RNP_SUCCESS ||
744 const Key key(handle);
746 if (rnp_key_have_secret(handle, &secret) != RNP_SUCCESS) {
753 char *type =
nullptr;
754 if (rnp_key_get_protection_type(handle, &type) != RNP_SUCCESS ||
759 if (QString::fromLatin1(type) == QLatin1String(
"Unknown")) {
760 found = QString::fromLatin1(fpr);
762 rnp_buffer_destroy(type);
764 rnp_identifier_iterator_destroy(it);
772auto primaryOf(rnp_ffi_t ffi, rnp_key_handle_t key) -> Key {
773 const QString primary = primaryFingerprint(key);
774 rnp_key_handle_t handle =
nullptr;
775 if (primary.isEmpty() ||
776 rnp_locate_key(ffi,
"fingerprint", primary.toLatin1().constData(),
777 &handle) != RNP_SUCCESS) {
787 if (
m_ffi ==
nullptr) {
790 rnp_identifier_iterator_t it =
nullptr;
791 if (rnp_identifier_iterator_create(
m_ffi, &it,
"fingerprint") !=
795 const char *fpr =
nullptr;
796 while (rnp_identifier_iterator_next(it, &fpr) == RNP_SUCCESS &&
798 rnp_key_handle_t handle =
nullptr;
799 if (rnp_locate_key(
m_ffi,
"fingerprint", fpr, &handle) != RNP_SUCCESS ||
803 const Key key(handle);
804 bool primary =
false;
805 if (rnp_key_is_primary(handle, &primary) != RNP_SUCCESS || !primary) {
810 char *keyId =
nullptr;
811 if (rnp_key_get_keyid(handle, &keyId) == RNP_SUCCESS) {
812 info.
keyId = QString::fromLatin1(keyId);
813 rnp_buffer_destroy(keyId);
815 info.
userIds = liveUserIds(handle);
816 uint32_t created = 0;
817 if (rnp_key_get_creation(handle, &created) == RNP_SUCCESS) {
818 info.
created = QDateTime::fromSecsSinceEpoch(created, QTimeZone::UTC);
820 uint32_t lifetime = 0;
821 if (rnp_key_get_expiration(handle, &lifetime) == RNP_SUCCESS &&
825 info.
revoked = isRevoked(handle);
826 bool expired =
false;
828 rnp_key_is_expired(handle, &expired) == RNP_SUCCESS && expired;
830 info.
hasSecret = forEachSecretPart(handle, [&info](rnp_key_handle_t part) {
836 info.
canSign = signingKeyOf(handle) !=
nullptr;
841 rnp_identifier_iterator_destroy(it);
846 QString *error) -> QByteArray {
847 if (
m_ffi ==
nullptr) {
848 setError(error, QStringLiteral(
"RNP is not available"));
854 const Key named = locateKey(
m_ffi, signer, &why, Usage::Sign);
856 setError(error, why);
859 const Key primary = primaryOf(
m_ffi, named.get());
860 const Key signing = primary ? signingKeyOf(primary.get()) :
nullptr;
862 setError(error, QStringLiteral(
"The key %1 cannot sign").arg(signer));
865 Input input = memoryInput(data, error,
"Cannot read the data to sign");
866 Output output = memoryOutput(error,
"Cannot sign");
867 if (!input || !output) {
870 rnp_op_sign_t op =
nullptr;
871 rnp_result_t result =
872 rnp_op_sign_detached_create(&op,
m_ffi, input.get(), output.get());
873 if (result != RNP_SUCCESS) {
874 setError(error, rnpError(
"Cannot sign", result));
877 const std::unique_ptr<rnp_op_sign_st, rnp_result_t (*)(rnp_op_sign_t)> guard(
878 op, rnp_op_sign_destroy);
879 rnp_op_sign_set_armor(op,
false);
880 result = rnp_op_sign_add_signature(op, signing.get(),
nullptr);
881 if (result == RNP_SUCCESS) {
883 result = rnp_op_sign_execute(op);
885 if (result != RNP_SUCCESS) {
886 setError(error, rnpError(
"Cannot sign", result));
889 std::optional<QByteArray> signature = outputBytes(output.get());
890 if (!signature || signature->isEmpty()) {
891 setError(error, QStringLiteral(
"Cannot sign: no output from RNP"));
898 QString *error) -> QStringList {
899 if (
m_ffi ==
nullptr) {
900 setError(error, QStringLiteral(
"RNP is not available"));
903 Input input = memoryInput(data, error,
"Cannot read the signed data");
904 Input sigInput = memoryInput(signature, error,
"Cannot read the signature");
905 if (!input || !sigInput) {
908 rnp_op_verify_t op =
nullptr;
909 rnp_result_t result =
910 rnp_op_verify_detached_create(&op,
m_ffi, input.get(), sigInput.get());
911 if (result != RNP_SUCCESS) {
912 setError(error, rnpError(
"Cannot verify", result));
915 const std::unique_ptr<rnp_op_verify_st, rnp_result_t (*)(rnp_op_verify_t)>
916 guard(op, rnp_op_verify_destroy);
918 result = rnp_op_verify_execute(op);
920 rnp_op_verify_get_signature_count(op, &count);
921 for (
size_t i = 0; i < count; ++i) {
922 rnp_op_verify_signature_t sig =
nullptr;
923 if (rnp_op_verify_get_signature_at(op, i, &sig) != RNP_SUCCESS ||
924 rnp_op_verify_signature_get_status(sig) != RNP_SUCCESS) {
927 rnp_key_handle_t handle =
nullptr;
928 if (rnp_op_verify_signature_get_key(sig, &handle) != RNP_SUCCESS ||
932 const Key key(handle);
933 const Key primary = primaryOf(
m_ffi, handle);
938 if (isRevoked(handle) || !primary || isRevoked(primary.get())) {
939 setError(error, QStringLiteral(
"The signature is by a revoked key (%1)")
940 .arg(primaryFingerprint(handle)));
943 return {fingerprintOf(handle), fingerprintOf(primary.get())};
945 setError(error, result != RNP_SUCCESS ? rnpError(
"No valid signature", result)
946 : QStringLiteral(
"No valid signature"));
951 const QString &passphrase, QString *error) ->
bool {
952 if (
m_ffi ==
nullptr) {
953 setError(error, QStringLiteral(
"RNP is not available"));
956 if (passphrase.isEmpty()) {
957 setError(error, QStringLiteral(
"An empty passphrase protects nothing"));
960 rnp_key_handle_t handle =
nullptr;
961 rnp_locate_key(
m_ffi,
"fingerprint",
962 fingerprint.toUpper().toLatin1().constData(), &handle);
963 const Key key(handle);
972 : forEachSecretPart(handle, [&unprotected](rnp_key_handle_t part) {
973 unprotected += isProtected(part) ? 0 : 1;
977 setError(error, QStringLiteral(
"No secret key for %1").arg(fingerprint));
980 if (unprotected == 0) {
982 QStringLiteral(
"The secret key %1 is protected already; its "
983 "passphrase is not changed here")
987 QByteArray secret = passphrase.toUtf8();
989 forEachSecretPart(handle, [&](rnp_key_handle_t part) {
990 if (isProtected(part)) {
994 const rnp_result_t result =
995 rnp_key_protect(part, secret.constData(),
nullptr,
nullptr,
nullptr, 0);
996 if (result != RNP_SUCCESS) {
999 rnpError(QStringLiteral(
"Cannot protect %1").arg(fingerprintOf(part)),
1010 QString *error)
const ->
bool {
1011 if (
m_ffi ==
nullptr) {
1012 setError(error, QStringLiteral(
"RNP is not available"));
1019 QStringLiteral(
"%1 is a link, not a keyring folder").arg(dir));
1024 if (!allowUnprotected) {
1026 if (!key.secretProtected) {
1027 setError(error, QStringLiteral(
"The secret key %1 is not protected "
1029 .arg(key.fingerprint));
1036 const std::optional<QString> unknown =
1037 firstSecretOfUnknownProtection(
m_ffi);
1039 setError(error, QStringLiteral(
"Cannot tell how every secret key is "
1040 "protected; nothing is written"));
1043 if (!unknown->isEmpty()) {
1044 setError(error, QStringLiteral(
"The secret key %1 has a protection RNP "
1055 std::optional<QByteArray> ring = QByteArray();
1057 Output output = memoryOutput(error,
"Cannot save the keyring");
1061 const rnp_result_t result =
1062 rnp_save_keys(
m_ffi,
"GPG", output.get(),
1063 RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS);
1064 if (result != RNP_SUCCESS) {
1065 setError(error, rnpError(
"Cannot save the keyring", result));
1068 ring = outputBytes(output.get());
1070 setError(error, QStringLiteral(
"Cannot save the keyring: no output"));
1076 setError(error, QStringLiteral(
"The keyring is larger than %1 bytes")
1082 if (!QDir().mkpath(dir) ||
1083 !QFile::setPermissions(dir, QFile::ReadOwner | QFile::WriteOwner |
1085 setError(error, QStringLiteral(
"Cannot create %1").arg(dir));
1092 QDir(dir).filePath(QStringLiteral(
"keyring.gpg")), *ring,
true,
1094 setError(error, QStringLiteral(
"Cannot write the keyring: %1").arg(why));
1101 if (
m_ffi ==
nullptr) {
1102 setError(error, QStringLiteral(
"RNP is not available"));
1108 QStringLiteral(
"%1 is a link, not a keyring folder").arg(dir));
1111 const QString path = QDir(dir).filePath(QStringLiteral(
"keyring.gpg"));
1112 const QFileInfo info(path);
1113 if (!info.exists() && !info.isSymLink()) {
1116 const std::optional<QByteArray> bytes =
1119 setError(error, QStringLiteral(
"Cannot read %1 as a keyring (a regular "
1120 "file of at most %2 bytes)")
1125 if (bytes->isEmpty()) {
1128 Input input = memoryInput(*bytes, error,
"Cannot read the keyring");
1132 const rnp_result_t result =
1133 rnp_load_keys(
m_ffi,
"GPG", input.get(),
1134 RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS);
1135 if (result != RNP_SUCCESS) {
1137 rnpError(QStringLiteral(
"Cannot load %1").arg(path), result));
static constexpr qint64 kMaxKeyringBytes
Most bytes of a keyring file: saveKeyring() writes no larger one, loadKeyring() reads no larger one.
auto sign(const QByteArray &data, const QString &signer, QString *error=nullptr) -> QByteArray
Makes a detached signature over data, as gpg –detach-sign –local-user signer does (for ....
void setPassphraseProvider(PassphraseProvider provider)
Sets how secret-key passphrases are obtained.
std::function< QString(const QString &keyId)> PassphraseProvider
Asks for the passphrase of a secret key.
RnpCrypto()
Creates an empty keyring.
auto importKeys(const QByteArray &keyData, QString *error=nullptr) -> QStringList
Imports public and secret keys from armored or binary key data.
qint64 m_maxPlaintext
Largest plaintext decrypt() returns, in bytes.
auto verify(const QByteArray &data, const QByteArray &signature, QString *error=nullptr) -> QStringList
Checks a detached signature over data, as QtPass reads gpg's VALIDSIG.
void setMaxPlaintextBytes(qint64 bytes)
Sets the most bytes of plaintext decrypt() may produce.
~RnpCrypto()
Frees the keyring and everything RNP holds for it.
rnp_ffi_st * m_ffi
RNP's handle holding this object's keyring; null when RNP could not be initialised,...
auto protectKey(const QString &fingerprint, const QString &passphrase, QString *error=nullptr) -> bool
Protects the secret parts of a key with passphrase.
auto decrypt(const QByteArray &ciphertext, QString *error=nullptr, bool *ok=nullptr) -> QByteArray
Decrypts an OpenPGP message with any secret key in the keyring.
auto listKeys(bool secretOnly=false) const -> QList< KeyInfo >
The primary keys in the keyring.
auto isValid() const -> bool
Whether the RNP context could be created at all.
auto loadKeyring(const QString &dir, QString *error=nullptr) -> bool
Adds the keyring in dir (as saveKeyring() wrote it) to this one. Secret keys stay protected until an ...
auto encrypt(const QByteArray &plaintext, const QStringList &recipients, QString *error=nullptr) -> QByteArray
Encrypts plaintext to every recipient, as one message.
PassphraseProvider m_passphrase
Asked for the passphrase of a locked secret key during decryption; set with setPassphraseProvider(),...
auto saveKeyring(const QString &dir, bool allowUnprotected=false, QString *error=nullptr) const -> bool
Writes the keyring to dir: keyring.gpg, public and secret keys in one file (OpenPGP keys,...
static auto writeFileReplacing(const QString &path, const QByteArray &bytes, bool replace, QString *error=nullptr) -> bool
Write bytes as the file path through stageFileReplacing().
static auto readRegularFile(const QString &path, qint64 maxBytes) -> std::optional< QByteArray >
Read path whole, as the regular file it is (openRegularFile()), when it holds at most maxBytes.
static auto isLinkedFolder(const QString &path) -> bool
Whether path names a symbolic link or NTFS junction, with or without a trailing separator (which woul...
RnpCrypto, in-process OpenPGP encryption and decryption through the RNP library.
One primary key in the keyring, as the Users dialog lists keys.
QStringList userIds
Its user IDs that are still its own (valid, not revoked), in the keyring's order.
QDateTime expires
When it expires; invalid when it does not.
bool canSign
Whether it can sign now, with a secret key that is here.
bool canEncrypt
Whether a message can be encrypted to it now.
bool secretProtected
Whether every secret part of it that is here is protected by a passphrase; true when none is here.
bool expired
Whether it has expired.
QString fingerprint
The primary key's fingerprint, upper-case hex.
QString keyId
The primary key's 16-hex-digit key ID.
QDateTime created
When it was created.
bool hasSecret
Whether a secret part of it (primary or subkey) is here to use: a GnuPG stub (a key left at home,...
bool revoked
Whether it has been revoked.
Util: static helpers for the process environment, binary discovery, store paths, URL safety and the l...