1 // GPGEdit edits GPG encrypted files
2 // Copyright (C) 2018 Aaron Ball <nullspoon@oper.io>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17 #include "gpg.h"
18
19 #ifndef GPG_H
20 #define GPG_H
21
22 #define READSIZE 256
23
24 gpgme_error_t init_gpg(gpgme_ctx_t* ctx, gpgme_protocol_t proto) {
25 gpgme_error_t err;
26
27 gpgme_check_version (NULL);
28 setlocale (LC_ALL, "");
29 gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
30
31 err = gpgme_engine_check_version (proto);
32
33 err = gpgme_new(ctx);
34 if(err != GPG_ERR_NO_ERROR) {
35 printf("ERROR: ");
36 if(err == GPG_ERR_INV_VALUE) {
37 printf("GPG context is not valid pointer\n");
38 } else if(err == GPG_ERR_ENOMEM) {
39 printf("Insufficient memory to allocate GPG context\n");
40 } else if(err == GPG_ERR_ENOMEM) {
41 printf("Insufficient memory to allocate GPG context\n");
42 } else if(err == GPG_ERR_NOT_OPERATIONAL) {
43 printf("GPGME not initialized\n");
44 } else {
45 printf("Unknown\n");
46 }
47 return err;
48 }
49
50 gpgme_set_protocol(*ctx, proto);
51 gpgme_set_armor(*ctx, 1);
52
53 return err;
54 }
55
56
57 int gpg_data_fwrite(gpgme_data_t dh, FILE* out) {
58 char buf[READSIZE];
59 size_t total = 0;
60 size_t size = 0;
61
62 gpgme_data_seek(dh, 0, SEEK_SET);
63 if(gpgme_data_read(dh, buf, 1) < 1) {
64 printf("Error: GPG data struct is empty. Nothing to write.\n");
65 return -1;
66 }
67
68 // Set seek to beginning of file
69 gpgme_data_seek(dh, 0, SEEK_SET);
70
71 while((size = gpgme_data_read(dh, buf, READSIZE)) > 0) {
72 total += size;
73 buf[size] = '\0'; // Ensure string terminates at the correct length
74 fprintf(out, "%s", buf); // Write to file descriptor
75 }
76
77 return total;
78 }
79
80
81 int gpg_recip_count(gpgme_recipient_t firstrecip) {
82 gpgme_recipient_t recip; // Current recipient object
83 int count = 0; // Count of recipients
84
85 // Get recipient list
86 recip = firstrecip;
87 while(recip != NULL) {
88 //printf("-- recipient: %s\n", recip->keyid);
89 recip = recip->next;
90 count++;
91 }
92
93 return count;
94 }
95
96
97 char** gpg_recip_get_keyids(gpgme_recipient_t firstrecip) {
98 gpgme_recipient_t recip; // Current recipient object
99 int count = 0; // Number of recipients
100 int i = 0; // Key index
101 char** keyids = NULL;
102
103 count = gpg_recip_count(firstrecip);
104 keyids = (char**) malloc(sizeof(char*) * count + 1);
105
106 // Write each recipient key id to the char array
107 recip = firstrecip;
108 while(recip != NULL) {
109 // For debugging purposes
110 //printf("Getting key for %s\n", recip->keyid);
111 keyids[i] = malloc(strlen(recip->keyid) + 1);
112 strcpy(keyids[i], recip->keyid);
113
114 i++;
115 recip = recip->next;
116 }
117
118 keyids[i] = NULL;
119 return keyids;
120 }
121
122
123 gpgme_key_t* gpg_keyids_to_key_t(gpgme_ctx_t ctx, char** keyids) {
124 gpgme_key_t* out;
125 gpgme_error_t err;
126 int count = 0; // Number of recipients
127 int i = 0;
128
129 // Count the keys
130 while(keyids[count] != NULL)
131 count++;
132
133 // Allocate the gpgme_key_t pointer array
134 out = malloc(sizeof(gpgme_key_t) * (count + 1));
135
136 // Loop over the keys back to zero
137 while(i < count) {
138 err = gpgme_get_key(ctx, keyids[i], &out[i], 0);
139 if(err != GPG_ERR_NO_ERROR) {
140 printf("Ooops! Something went wrong getting key %s\n", keyids[i]);
141 }
142 i++;
143 }
144 out[i] = NULL;
145 return out;
146 }
147
148 void gpg_keyids_release(char** keyids) {
149 int i = 0;
150
151 while(keyids[i] != NULL) {
152 free(keyids[i]);
153 i++;
154 }
155 free(keyids);
156 }
157
158
159
160 void gpg_dump_key(gpgme_key_t key) {
161 printf("Recipient: %s\n", key->fpr);
162 printf(" protocol: %d\n", key->protocol);
163 printf(" revoked: %d\n", key->revoked);
164 printf(" expired: %d\n", key->expired);
165 printf(" disabled: %d\n", key->disabled);
166 printf(" invalid: %d\n", key->invalid);
167 printf(" can_encrypt: %d\n", key->can_encrypt);
168 printf(" can_sign: %d\n", key->can_sign);
169 printf(" can_certify: %d\n", key->can_certify);
170 printf(" can_auth: %d\n", key->can_authenticate);
171 printf(" qualified: %d\n", key->is_qualified);
172 printf(" secret: %d\n", key->secret);
173 printf(" origin: %d\n", key->origin);
174 printf(" issuer_serial: %s\n", key->issuer_serial);
175 printf(" issuer_name: %s\n", key->issuer_name);
176 printf(" chain_id: %s\n", key->chain_id);
177 printf(" last_update: %ld\n", key->last_update);
178 }
179
180
181 size_t gpg_encrypt_file(char* infile, char* outfile, char** keyids) {
182 gpgme_ctx_t ctx;
183 gpgme_data_t indata;
184 gpgme_data_t outdata;
185 gpgme_error_t err;
186 gpgme_key_t* keys = NULL;
187
188 err = init_gpg(&ctx, GPGME_PROTOCOL_OPENPGP);
189 if(err != GPG_ERR_NO_ERROR) {
190 printf("Error: %s\n", gpgme_strerror(err));
191 return -1;
192 }
193
194 // For debugging purposes
195 // printf("Reading updated data from memory\n");
196 // Initialize data structures
197 err = gpgme_data_new_from_file(&indata, infile, 1);
198 if(err != GPG_ERR_NO_ERROR)
199 printf("Error: %s\n", gpgme_strerror(err));
200
201 err = gpgme_data_new(&outdata);
202 if(err != GPG_ERR_NO_ERROR)
203 printf("Error: %s\n", gpgme_strerror(err));
204
205 // Get key id objects from keyids char array
206 keys = gpg_keyids_to_key_t(ctx, keyids);
207 // For debugging purposes
208 // int i = 0;
209 // while(keys[i] != NULL) {
210 // gpg_dump_key(keys[i]);
211 // i++;
212 // }
213
214 // Encrypt!
215 err = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, indata, outdata);
216
217 if(err != GPG_ERR_NO_ERROR) {
218 if(err == GPG_ERR_UNUSABLE_PUBKEY) {
219 printf("One or more public keys unusable\n");
220 } else if(err == GPG_ERR_INV_VALUE) {
221 printf("Invalid data structure\n");
222 } else if(err == GPG_ERR_BAD_PASSPHRASE) {
223 printf("Incorrect passphrase\n");
224 } else {
225 printf("Unknown error: %d\n", err);
226 printf("Message:\n %s\n", gpgme_strerror(err));
227 }
228 }
229 // For debugging purposes
230 // printf("No errors detecting during encryption.\n");
231
232 // For debugging purposes
233 // printf("Writing to disk\n");
234 // Write to disk!
235 FILE* refd = fopen(outfile, "w");
236 gpg_data_fwrite(outdata, refd);
237 fclose(refd);
238
239 // cleanup
240 gpgme_data_release(indata);
241 gpgme_data_release(outdata);
242 gpgme_release(ctx);
243 return 0;
244 }
245
246
247 gpgme_decrypt_result_t
248 gpg_decrypt_file(char* infile, char* outfile, gpgme_ctx_t ctx) {
249 gpgme_error_t err;
250 gpgme_data_t indata = NULL;
251 gpgme_data_t outdata = NULL;
252
253 // Read ciphertext file into gpgme data struct, and fail if any errors
254 // returned.
255 gpgme_data_new(&outdata);
256 err = gpgme_data_new_from_file(&indata, infile, 1);
257 if(err != GPG_ERR_NO_ERROR) {
258 printf("ERROR: ");
259 if(err == GPG_ERR_INV_VALUE) {
260 printf("Invalid data struct or file path\n");
261 } else if(err == GPG_ERR_NOT_IMPLEMENTED) {
262 printf("Not implemented\n");
263 } else if(err == GPG_ERR_ENOMEM) {
264 printf("Insufficient memory to allocate data struct\n");
265 }
266 return NULL;
267 }
268
269 // Attempt to decrypt
270 err = gpgme_op_decrypt(ctx, indata, outdata);
271 if(err != GPG_ERR_NO_ERROR) {
272 printf("ERROR: ");
273 if(err == GPG_ERR_INV_VALUE) {
274 printf("Invalid pointer to cipher or plain data structs\n");
275 } else if(err == GPG_ERR_NO_DATA) {
276 printf("Cipher struct contains no encrypted data\n");
277 } else if(err == GPG_ERR_DECRYPT_FAILED) {
278 printf("Ciphertext is invalid\n");
279 } else if(err == GPG_ERR_BAD_PASSPHRASE) {
280 printf("Passphrase is incorrect\n");
281 } else {
282 printf("Unhandled error\n %s\n", gpgme_strerror(err));
283 }
284 return NULL;
285 }
286
287 // Write decrypted data to outfile
288 FILE* outfd = fopen(outfile, "w");
289 gpg_data_fwrite(outdata, outfd);
290
291 // Clean up
292 fclose(outfd);
293 gpgme_data_release(indata);
294 gpgme_data_release(outdata);
295 return gpgme_op_decrypt_result(ctx);
296 }
297 #endif
|