1 /*
2     SPDX-FileCopyrightText: 2008, 2009, 2010, 2011, 2012, 2013, 2018 Rolf Eike Beer <kde@opensource.sf-tec.de>
3     SPDX-License-Identifier: GPL-2.0-or-later
4 */
5 
6 #include "kgpggeneratekey.h"
7 
8 #include "gpgproc.h"
9 
10 #include <KEmailAddress>
11 #include <KLocalizedString>
12 #include <QApplication>
13 
KGpgGenerateKey(QObject * parent,const QString & name,const QString & email,const QString & comment,const KgpgCore::KgpgKeyAlgo & algorithm,const uint size,const unsigned int expire,const char expireunit,const KgpgCore::KgpgSubKeyType capabilities)14 KGpgGenerateKey::KGpgGenerateKey(QObject *parent, const QString &name, const QString &email, const QString &comment,
15 		const KgpgCore::KgpgKeyAlgo &algorithm, const uint size, const unsigned int expire,
16 		const char expireunit, const KgpgCore::KgpgSubKeyType capabilities)
17 	: KGpgTransaction(parent),
18 	m_name(name),
19 	m_email(email),
20 	m_comment(comment),
21 	m_algorithm(algorithm),
22 	m_capabilities(capabilities),
23 	m_size(size),
24 	m_expire(expire),
25 	m_expireunit(expireunit)
26 {
27 	Q_ASSERT((expireunit == 'd') || (expireunit == 'w') ||
28 			(expireunit == 'm') || (expireunit == 'y'));
29 
30 	addArguments( { QLatin1String("--status-fd=1"),
31 			QLatin1String("--command-fd=0"),
32 			QLatin1String("--no-verbose"),
33 			QLatin1String("--gen-key"),
34 			QLatin1String("--batch")
35 			} );
36 
37 	getProcess()->setOutputChannelMode(KProcess::SeparateChannels);
38 }
39 
~KGpgGenerateKey()40 KGpgGenerateKey::~KGpgGenerateKey()
41 {
42 }
43 
44 bool
preStart()45 KGpgGenerateKey::preStart()
46 {
47 	if (!m_email.isEmpty() && !KEmailAddress::isValidSimpleAddress(m_email)) {
48 		setSuccess(TS_INVALID_EMAIL);
49 		return false;
50 	}
51 
52 	m_fingerprint.clear();
53 
54 	setSuccess(TS_MSG_SEQUENCE);
55 
56 	setDescription(i18n("Generating New Key for %1", m_name));
57 
58 	return true;
59 }
60 
61 void
postStart()62 KGpgGenerateKey::postStart()
63 {
64 	QByteArray keymessage = "Key-Type: ";
65 
66 	switch (m_algorithm) {
67 	case KgpgCore::ALGO_RSA:
68 		keymessage.append("RSA");
69 		break;
70 	case KgpgCore::ALGO_RSA_RSA:
71 		keymessage.append("RSA\nSubkey-Type: RSA");
72 		break;
73 	case KgpgCore::ALGO_DSA_ELGAMAL:
74 		keymessage.append("DSA\nSubkey-Type: ELG-E");
75 		break;
76 	default:
77 		Q_ASSERT(m_algorithm == KgpgCore::ALGO_RSA);
78 		return;
79 	}
80 
81 	const QByteArray keylen = QByteArray::number(m_size);
82 
83 	keymessage.append("\nKey-Length: ");
84 	keymessage.append(keylen);
85 	keymessage.append("\nSubkey-Length: ");
86 	keymessage.append(keylen);
87 	keymessage.append("\nName-Real: ");
88 	keymessage.append(m_name.toUtf8());
89 
90 	if (!m_email.isEmpty()) {
91 		keymessage.append("\nName-Email: ");
92 		keymessage.append(m_email.toLatin1());
93 	}
94 
95 	if (!m_comment.isEmpty()) {
96 		keymessage.append("\nName-Comment: ");
97 		keymessage.append(m_comment.toUtf8());
98 	}
99 
100 	if (m_expire != 0) {
101 		keymessage.append("\nExpire-Date: ");
102 		keymessage.append(QByteArray::number(m_expire));
103 		keymessage.append(m_expireunit);
104 	}
105 
106 	if (m_capabilities) {
107 		keymessage.append("\nKey-Usage: ");
108 		QStringList usage;
109 #if 0
110 		// GnuPG always adds cert, but it does not allow this to be
111 		// explicitly specified
112 		if (m_capabilities & KgpgCore::SKT_CERTIFICATION)
113 			usage << QLatin1String("cert");
114 #endif
115 		if (m_capabilities & KgpgCore::SKT_AUTHENTICATION)
116 			usage << QLatin1String("auth");
117 		if (m_capabilities & KgpgCore::SKT_ENCRYPTION)
118 			usage << QLatin1String("encrypt");
119 		if (m_capabilities & KgpgCore::SKT_SIGNATURE)
120 			usage << QLatin1String("sign");
121 		keymessage.append(usage.join(QLatin1Char(' ')).toLatin1());
122 	}
123 
124 	keymessage.append("\nPassphrase: ");
125 	write(keymessage, false);
126 
127 	QString passdlgmessage;
128 	if (!m_email.isEmpty()) {
129 		passdlgmessage = i18n("<p><b>Enter passphrase for %1 &lt;%2&gt;</b>:<br />Passphrase should include non alphanumeric characters and random sequences.</p>",
130 				m_name, m_email);
131 	} else {
132 		passdlgmessage = i18n("<p><b>Enter passphrase for %1</b>:<br />Passphrase should include non alphanumeric characters and random sequences.</p>",
133 				m_name);
134 	}
135 
136 	QApplication::restoreOverrideCursor();
137 	askNewPassphrase(passdlgmessage);
138 }
139 
140 bool
nextLine(const QString & line)141 KGpgGenerateKey::nextLine(const QString &line)
142 {
143 	QString msg = i18n("Generating Key");
144 
145 	if (!line.startsWith(QLatin1String("[GNUPG:] ")))
146 		return false;
147 
148 	int result = false;
149 
150 	if (line.contains(QLatin1String( "PROGRESS" ))) {
151 		const QStringList parts = line.mid(18).split(QLatin1Char(' '));
152 
153 		if (parts.count() >= 4) {
154 			const QString p0(parts.at(0));
155 			if (p0 == QLatin1String( "primegen" )) {
156 				msg = i18n("Generating prime numbers");
157 			} else if (p0 == QLatin1String( "pk_dsa" )) {
158 				msg = i18n("Generating DSA key");
159 			} else if (p0 == QLatin1String( "pk_elg" )) {
160 				msg = i18n("Generating ElGamal key");
161 			} else if (p0 == QLatin1String( "need_entropy" )) {
162 				msg = i18n("Waiting for entropy");
163 
164 				// This message is currenlty not displayed. Nevertheless it's
165 				// included here so string freeze is not broken if it will be
166 				// displayed later on.
167 				QString msglong = i18n("The entropy pool ran empty. The key generation process is stalled until enough entropy is present. You can generate entropy e.g. by moving the mouse or typing at the keyboard. The easiest way is by using another application until the key generation continues.");
168 			}
169 			if (parts.at(3) != QLatin1String( "0" ))
170 				Q_EMIT infoProgress(parts.at(2).toUInt(), parts.at(3).toUInt());
171 		}
172 	} else if (line.contains(QLatin1String( "GOOD_PASSPHRASE" ))) {
173 		setSuccess(TS_MSG_SEQUENCE);
174 	} else if (line.contains(QLatin1String( "KEY_CREATED" ))) {
175 		m_fingerprint = line.right(40);
176 		setSuccess(TS_OK);
177 		result = true;
178 	} else if (line.contains(QLatin1String( "NEED_PASSPHRASE" ))) {
179 		setSuccess(TS_USER_ABORTED);
180 	} else if (line.contains(QLatin1String( "GET_" ))) {
181 		setSuccess(TS_MSG_SEQUENCE);
182 		result = true;
183 	} else if (line.contains(QLatin1String("KEY_NOT_CREATED"))) {
184 		result = true;
185 	} else
186 		m_errorOutput << line;
187 
188 	Q_EMIT statusMessage(msg);
189 
190 	return result;
191 }
192 
193 void
finish()194 KGpgGenerateKey::finish()
195 {
196 	switch (getSuccess()) {
197 	case TS_BAD_PASSPHRASE:
198 		Q_EMIT statusMessage(i18n("Bad passphrase. Cannot generate a new key pair."));
199 		break;
200 	case TS_USER_ABORTED:
201 		Q_EMIT statusMessage(i18n("Aborted by the user. Cannot generate a new key pair."));
202 		break;
203 	case TS_INVALID_EMAIL:
204 		Q_EMIT statusMessage(i18n("The email address is not valid. Cannot generate a new key pair."));
205 		break;
206 	case TS_INVALID_NAME:
207 		Q_EMIT statusMessage(i18n("The name is not accepted by gpg. Cannot generate a new key pair."));
208 		break;
209 	case TS_OK:
210 		Q_EMIT statusMessage(i18n("Key %1 generated", getFingerprint()));
211 		break;
212 	default:
213 		while (getProcess()->hasLineStandardError()) {
214 			QByteArray b;
215 			getProcess()->readLineStandardError(&b);
216 			m_errorOutput << QString::fromUtf8(b);
217 		}
218 
219 		Q_EMIT statusMessage(i18n("gpg process did not finish. Cannot generate a new key pair."));
220 	}
221 }
222 
223 void
newPassphraseEntered()224 KGpgGenerateKey::newPassphraseEntered()
225 {
226 	QApplication::setOverrideCursor(Qt::BusyCursor);
227 	write("%commit");
228 }
229 
230 QString
getName() const231 KGpgGenerateKey::getName() const
232 {
233 	return m_name;
234 }
235 
236 QString
getEmail() const237 KGpgGenerateKey::getEmail() const
238 {
239 	return m_email;
240 }
241 
242 QString
getFingerprint() const243 KGpgGenerateKey::getFingerprint() const
244 {
245 	return m_fingerprint;
246 }
247 
248 QString
gpgErrorMessage() const249 KGpgGenerateKey::gpgErrorMessage() const
250 {
251 	return m_errorOutput.join(QLatin1Char('\n'));
252 }
253