1/*!
2 * @file openpgp.c
3 *
4 * vim: expandtab:ts=2:sts=2:sw=2
5 *
6 * @authors
7 * Copyright (C) 2020 Anoxinon e.V.
8 *
9 * @copyright
10 * This file is part of xmppc.
11 *
12 * xmppc is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * xmppc is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with Foobar. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * German
26 *
27 * Diese Datei ist Teil von xmppc.
28 *
29 * xmppc ist Freie Software: Sie können es unter den Bedingungen
30 * der GNU General Public License, wie von der Free Software Foundation,
31 * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren
32 * veröffentlichten Version, weiter verteilen und/oder modifizieren.
33 *
34 * xmppc wird in der Hoffnung, dass es nützlich sein wird, aber
35 * OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
36 * Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
37 * Siehe die GNU General Public License für weitere Details.
38 *
39 * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
40 * Programm erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.
41 */
42
43#define _GNU_SOURCE
44
45#include "openpgp.h"
46#include "string.h"
47
48#include <locale.h>
49#include <stdlib.h>
50#include <glib.h>
51#include <time.h>
52#include <gpgme.h>
53
54static void _openpgp_send_text(xmppc_t *xmppc, char* to, char* text);
55static xmpp_stanza_t* _openpgp_signcrypt(xmppc_t *xmppc, char* to, char* text);
56static char* _openpgp_gpg_signcrypt(xmppc_t *xmppc, char* recipient, char* message);
57static gpgme_error_t _openpgp_lookup_key(xmppc_t *xmppc, char* name, gpgme_ctx_t* ctx, gpgme_key_t* key);
58static char* _generate_rpad();
59
60// RFC-4648 - The Base16, Base32, and Base64 Data Encodings
61// https://tools.ietf.org/html/rfc4648#section-4
62
63/** RFC-4648 - The Base 64 Alphabet **/
64static const char rfc_4648_base64_alphabet[] = {
65 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
66 'Q','R','S','T','V','V','W','X','Y','Z','a','b','c','d','e','f',
67 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
68 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
69};
70//static const char rfc_4648_base64_pad = '=';
71
72static unsigned int seed = 0;
73
74char* _generate_rpad() {
75 if ( !seed) seed = time(NULL);
76 int size = (rand_r(&seed) % 201) ;
77 if(size > 0 ) {
78 char* rpad = malloc( sizeof(char) * (size+1));
79 rpad[size] = '\0';
80 for(int i = 0;i < size; i++)
81 rpad[i] = rfc_4648_base64_alphabet[(rand_r(&seed)%64)];
82 return rpad;
83 }
84 return NULL;
85}
86
87void openpgp_execute_command(xmppc_t *xmppc, int argc, char *argv[]) {
88 if(argc > 0) {
89 if(strcmp("signcrypt", argv[0]) == 0) {
90 _openpgp_send_text(xmppc, argv[1], argv[2]);
91 } else {
92 logError(xmppc, "Unbekanner Befehl: %s\n", argv[0]);
93 }
94 }
95 sleep(10);
96 xmpp_disconnect(xmppc->conn);
97}
98
99void _openpgp_send_text(xmppc_t *xmppc, char* to, char* text) {
100 xmpp_conn_t *conn = xmppc->conn;
101 xmpp_stanza_t *message;
102 char* id = xmpp_uuid_gen(xmppc->ctx);
103 message = xmpp_message_new(xmpp_conn_get_context(conn), NULL, to, id);
104 xmpp_message_set_body(message, "This message is *encrypted* with OpenPGP (See :XEP:`0373`)");
105// xmpp_stanza_set_type(message,"chat");
106 xmpp_stanza_t *openpgp = xmpp_stanza_new(xmppc->ctx);
107 xmpp_stanza_set_name(openpgp, "openpgp");
108 xmpp_stanza_set_ns(openpgp, "urn:xmpp:openpgp:0");
109
110 xmpp_stanza_t * signcrypt = _openpgp_signcrypt(xmppc, to, text);
111 char* c;
112 size_t s;
113 xmpp_stanza_to_text(signcrypt, &c,&s);
114 char* signcrypt_e = _openpgp_gpg_signcrypt(xmppc,to, c);
115 if( signcrypt_e == NULL ) {
116 logError(xmppc, "Message not signcrypted.\n");
117 return;
118 }
119 // BASE64_OPENPGP_MESSAGE
120 xmpp_stanza_t* base64_openpgp_message = xmpp_stanza_new(xmppc->ctx);
121 xmpp_stanza_set_text(base64_openpgp_message,signcrypt_e);
122 xmpp_stanza_add_child(openpgp, base64_openpgp_message);
123 xmpp_stanza_add_child(message, openpgp);
124
125 xmpp_stanza_to_text(message, &c,&s);
126
127 xmpp_send(conn, message);
128
129}
130
131xmpp_stanza_t* _openpgp_signcrypt(xmppc_t *xmppc, char* to, char* text) {
132
133 time_t now = time(NULL);
134 struct tm* tm = localtime(&now);
135 char buf[255];
136 strftime(buf, sizeof(buf), "%FT%T%z", tm);
137 char* rpad_data = _generate_rpad();
138
139 // signcrypt
140 xmpp_stanza_t *signcrypt = xmpp_stanza_new(xmppc->ctx);
141 xmpp_stanza_set_name(signcrypt, "signcrypt");
142 xmpp_stanza_set_ns(signcrypt, "urn:xmpp:openpgp:0");
143 // to
144 xmpp_stanza_t *s_to = xmpp_stanza_new(xmppc->ctx);
145 xmpp_stanza_set_name(s_to, "to");
146 xmpp_stanza_set_attribute(s_to, "jid", to);
147 // time
148 xmpp_stanza_t *time = xmpp_stanza_new(xmppc->ctx);
149 xmpp_stanza_set_name(time, "time");
150 xmpp_stanza_set_attribute(time, "stamp", buf);
151 xmpp_stanza_set_name(time, "time");
152 // rpad
153 xmpp_stanza_t *rpad = xmpp_stanza_new(xmppc->ctx);
154 xmpp_stanza_set_name(rpad, "rpad");
155 xmpp_stanza_t *rpad_text = xmpp_stanza_new(xmppc->ctx);
156 xmpp_stanza_set_text(rpad_text, rpad_data);
157 // payload
158 xmpp_stanza_t *payload= xmpp_stanza_new(xmppc->ctx);
159 xmpp_stanza_set_name(payload, "payload");
160 // body
161 xmpp_stanza_t *body = xmpp_stanza_new(xmppc->ctx);
162 xmpp_stanza_set_name(body, "body");
163 xmpp_stanza_set_ns(body, "jabber:client");
164 // text
165 xmpp_stanza_t *body_text = xmpp_stanza_new(xmppc->ctx);
166 xmpp_stanza_set_text(body_text, text);
167 xmpp_stanza_add_child(signcrypt,s_to);
168 xmpp_stanza_add_child(signcrypt,time);
169 xmpp_stanza_add_child(signcrypt,rpad);
170 xmpp_stanza_add_child(rpad,rpad_text);
171 xmpp_stanza_add_child(signcrypt,payload);
172 xmpp_stanza_add_child(payload, body);
173 xmpp_stanza_add_child(body, body_text);
174
175 return signcrypt;
176}
177
178char* _openpgp_gpg_signcrypt(xmppc_t *xmppc, char* recipient, char* message) {
179 setlocale (LC_ALL, "");
180 gpgme_check_version (NULL);
181 gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
182 gpgme_ctx_t ctx;
183 gpgme_error_t error = gpgme_new (&ctx);
184 if(GPG_ERR_NO_ERROR != error ) {
185 printf("gpgme_new: %d\n", error);
186 return NULL;
187 }
188 error = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP);
189 if(error != 0) {
190 logError(xmppc,"GpgME Error: %s\n", gpgme_strerror(error));
191 }
192 gpgme_set_armor(ctx,0);
193 gpgme_set_textmode(ctx,0);
194 gpgme_set_offline(ctx,1);
195 gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL);
196 if(error != 0) {
197 logError(xmppc,"GpgME Error: %s\n", gpgme_strerror(error));
198 }
199
200 gpgme_key_t recp[3];
201 recp[0] = NULL,
202 recp[1] = NULL;
203 const char *jid = xmpp_conn_get_jid(xmppc->conn);
204
205 char* xmpp_jid_me = alloca( (strlen(jid)+6) * sizeof(char) );
206 char* xmpp_jid_recipient = alloca( (strlen(recipient)+6) * sizeof(char) );
207
208 strcpy(xmpp_jid_me, "xmpp:");
209 strcpy(xmpp_jid_recipient, "xmpp:");
210 strcat(xmpp_jid_me, jid);
211 strcat(xmpp_jid_recipient,recipient);
212
213 gpgme_signers_clear(ctx);
214
215 // lookup own key
216 error = _openpgp_lookup_key(xmppc,xmpp_jid_me, &ctx, &recp[0]);
217 if(error != 0) {
218 logError(xmppc,"Key not found for %s. GpgME Error: %s\n", xmpp_jid_me, gpgme_strerror(error));
219 return NULL;
220 }
221
222 gpgme_signers_add (ctx, recp[0]);
223
224 error = gpgme_signers_add(ctx,recp[0]);
225 if(error != 0) {
226 logError(xmppc,"gpgme_signers_add %s. GpgME Error: %s\n", xmpp_jid_me, gpgme_strerror(error));
227 return NULL;
228 }
229
230
231 // lookup key of recipient
232 error = _openpgp_lookup_key(xmppc,xmpp_jid_recipient, &ctx, &recp[1]);
233 if(error != 0) {
234 logError(xmppc,"Key not found for %s. GpgME Error: %s\n", xmpp_jid_recipient, gpgme_strerror(error));
235 return NULL;
236 }
237 recp[2] = NULL;
238 logInfo(xmppc, "%s <%s>\n", recp[0]->uids->name, recp[0]->uids->email);
239 logInfo(xmppc, "%s <%s>\n", recp[1]->uids->name, recp[1]->uids->email);
240
241#ifdef XMPPC_DEVELOPMENT
242 gpgme_encrypt_flags_t flags = GPGME_ENCRYPT_ALWAYS_TRUST;
243#else
244 gpgme_encrypt_flags_t flags = 0;
245#endif
246
247 gpgme_data_t plain;
248 gpgme_data_t cipher;
249
250 error = gpgme_data_new (&plain);
251 if(error != 0) {
252 logError(xmppc,"GpgME Error: %s\n", gpgme_strerror(error));
253 return NULL;
254 }
255
256 error = gpgme_data_new_from_mem(&plain, message, strlen(message),0);
257 if(error != 0) {
258 logError(xmppc,"GpgME Error: %s\n", gpgme_strerror(error));
259 return NULL;
260 }
261 error = gpgme_data_new (&cipher);
262 if(error != 0) {
263 logError(xmppc,"GpgME Error: %s\n", gpgme_strerror(error));
264 return NULL;
265 }
266
267 error = gpgme_op_encrypt_sign ( ctx, recp, flags, plain, cipher);
268 if(error != 0) {
269 logError(xmppc,"GpgME Error: %s\n", gpgme_strerror(error));
270 return NULL;
271 }
272
273 size_t len;
274 char *cipher_str = gpgme_data_release_and_get_mem(cipher, &len);
275 char* result = g_base64_encode( (unsigned char*) cipher_str,len);
276 gpgme_key_release (recp[0]);
277 gpgme_key_release (recp[1]);
278 gpgme_release (ctx);
279 return result;
280}
281
282gpgme_error_t _openpgp_lookup_key(xmppc_t *xmppc,char* name, gpgme_ctx_t* ctx, gpgme_key_t* key) {
283 logDebug(xmppc, "Looking for key: %s ...\n", name);
284 gpgme_error_t error = gpgme_op_keylist_start (*ctx, NULL, 0);
285 while (!error) {
286 error = gpgme_op_keylist_next (*ctx, key);
287 if(!error) {
288 gpgme_user_id_t uids = (*key)->uids;
289 while (uids) {
290 if(strcmp(uids->name, name) == 0) {
291 logDebug(xmppc, "Key found: %s ...\n", uids->name);
292 return error;
293 }
294 uids=uids->next;
295 }
296 } else {
297 gpgme_key_release((*key));
298 }
299 }
300 return error;
301}
302