/src/rnp/src/librekey/rnp_key_store.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2017-2023 [Ribose Inc](https://www.ribose.com). |
3 | | * All rights reserved. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without modification, |
6 | | * are permitted provided that the following conditions are met: |
7 | | * |
8 | | * 1. Redistributions of source code must retain the above copyright notice, |
9 | | * this list of conditions and the following disclaimer. |
10 | | * |
11 | | * 2. Redistributions in binary form must reproduce the above copyright notice, |
12 | | * this list of conditions and the following disclaimer in the documentation |
13 | | * and/or other materials provided with the distribution. |
14 | | * |
15 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
16 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
17 | | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
18 | | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
19 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
21 | | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
22 | | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
23 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
24 | | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | */ |
26 | | |
27 | | #include "config.h" |
28 | | #include <sys/stat.h> |
29 | | #include <sys/types.h> |
30 | | #ifdef HAVE_SYS_PARAM_H |
31 | | #include <sys/param.h> |
32 | | #else |
33 | | #include "uniwin.h" |
34 | | #endif |
35 | | |
36 | | #include <assert.h> |
37 | | #include <stdio.h> |
38 | | #include <string.h> |
39 | | #include <stdint.h> |
40 | | #include <stdlib.h> |
41 | | #include <dirent.h> |
42 | | #include <errno.h> |
43 | | #include <algorithm> |
44 | | #include <stdexcept> |
45 | | |
46 | | #include <rekey/rnp_key_store.h> |
47 | | #include <librepgp/stream-packet.h> |
48 | | |
49 | | #include "key_store_g10.h" |
50 | | #include "kbx_blob.hpp" |
51 | | |
52 | | #include "key.hpp" |
53 | | #include "fingerprint.hpp" |
54 | | #include "crypto/hash.hpp" |
55 | | #include "crypto/mem.h" |
56 | | #include "file-utils.h" |
57 | | #ifdef _WIN32 |
58 | | #include "str-utils.h" |
59 | | #endif |
60 | | |
61 | | namespace rnp { |
62 | | bool |
63 | | KeyStore::load(const KeyProvider *key_provider) |
64 | 0 | { |
65 | 0 | pgp_source_t src = {}; |
66 | |
|
67 | 0 | if (format == KeyFormat::G10) { |
68 | 0 | auto dir = rnp_opendir(path.c_str()); |
69 | 0 | if (!dir) { |
70 | 0 | RNP_LOG("Can't open G10 directory %s: %s", path.c_str(), strerror(errno)); |
71 | 0 | return false; |
72 | 0 | } |
73 | | |
74 | 0 | std::string dirname; |
75 | 0 | while (!((dirname = rnp_readdir_name(dir)).empty())) { |
76 | 0 | std::string apath = path::append(path, dirname); |
77 | |
|
78 | 0 | if (init_file_src(&src, apath.c_str())) { |
79 | 0 | RNP_LOG("failed to read file %s", apath.c_str()); |
80 | 0 | continue; |
81 | 0 | } |
82 | | // G10 may fail to read one file, so ignore it! |
83 | 0 | if (!load_g10(src, key_provider)) { |
84 | 0 | RNP_LOG("Can't parse file: %s", apath.c_str()); // TODO: %S ? |
85 | 0 | } |
86 | 0 | src.close(); |
87 | 0 | } |
88 | 0 | rnp_closedir(dir); |
89 | 0 | return true; |
90 | 0 | } |
91 | | |
92 | | /* init file source and load from it */ |
93 | 0 | if (init_file_src(&src, path.c_str())) { |
94 | 0 | RNP_LOG("failed to read file %s", path.c_str()); |
95 | 0 | return false; |
96 | 0 | } |
97 | | |
98 | 0 | bool rc = load(src, key_provider); |
99 | 0 | src.close(); |
100 | 0 | return rc; |
101 | 0 | } |
102 | | |
103 | | bool |
104 | | KeyStore::load(pgp_source_t &src, const KeyProvider *key_provider) |
105 | 21.9k | { |
106 | 21.9k | switch (format) { |
107 | 12.4k | case KeyFormat::GPG: |
108 | 12.4k | return !load_pgp(src); |
109 | 9.49k | case KeyFormat::KBX: |
110 | 9.49k | return load_kbx(src, key_provider); |
111 | 0 | case KeyFormat::G10: |
112 | 0 | return load_g10(src, key_provider); |
113 | 0 | default: |
114 | 0 | RNP_LOG("Unsupported load from memory for key-store format: %d", |
115 | 21.9k | static_cast<int>(format)); |
116 | 21.9k | } |
117 | | |
118 | 0 | return false; |
119 | 21.9k | } |
120 | | |
121 | | bool |
122 | | KeyStore::write() |
123 | 0 | { |
124 | 0 | bool rc; |
125 | 0 | pgp_dest_t keydst = {}; |
126 | | |
127 | | /* write g10 key store to the directory */ |
128 | 0 | if (format == KeyFormat::G10) { |
129 | 0 | char chpath[MAXPATHLEN]; |
130 | |
|
131 | 0 | struct stat path_stat; |
132 | 0 | if (rnp_stat(path.c_str(), &path_stat) != -1) { |
133 | 0 | if (!S_ISDIR(path_stat.st_mode)) { |
134 | 0 | RNP_LOG("G10 keystore should be a directory: %s", path.c_str()); |
135 | 0 | return false; |
136 | 0 | } |
137 | 0 | } else { |
138 | 0 | if (errno != ENOENT) { |
139 | 0 | RNP_LOG("stat(%s): %s", path.c_str(), strerror(errno)); |
140 | 0 | return false; |
141 | 0 | } |
142 | 0 | if (RNP_MKDIR(path.c_str(), S_IRWXU) != 0) { |
143 | 0 | RNP_LOG("mkdir(%s, S_IRWXU): %s", path.c_str(), strerror(errno)); |
144 | 0 | return false; |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | 0 | for (auto &key : keys) { |
149 | 0 | auto grip = bin_to_hex(key.grip().data(), key.grip().size()); |
150 | 0 | snprintf(chpath, sizeof(chpath), "%s/%s.key", path.c_str(), grip.c_str()); |
151 | |
|
152 | 0 | if (init_tmpfile_dest(&keydst, chpath, true)) { |
153 | 0 | RNP_LOG("failed to create file"); |
154 | 0 | return false; |
155 | 0 | } |
156 | | |
157 | 0 | if (!rnp_key_store_gnupg_sexp_to_dst(key, keydst)) { |
158 | 0 | RNP_LOG("failed to write key to file"); |
159 | 0 | dst_close(&keydst, true); |
160 | 0 | return false; |
161 | 0 | } |
162 | | |
163 | 0 | rc = dst_finish(&keydst) == RNP_SUCCESS; |
164 | 0 | dst_close(&keydst, !rc); |
165 | |
|
166 | 0 | if (!rc) { |
167 | 0 | return false; |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | 0 | return true; |
172 | 0 | } |
173 | | |
174 | | /* write kbx/gpg store to the single file */ |
175 | 0 | if (init_tmpfile_dest(&keydst, path.c_str(), true)) { |
176 | 0 | RNP_LOG("failed to create keystore file"); |
177 | 0 | return false; |
178 | 0 | } |
179 | | |
180 | 0 | if (!write(keydst)) { |
181 | 0 | RNP_LOG("failed to write keys to file"); |
182 | 0 | dst_close(&keydst, true); |
183 | 0 | return false; |
184 | 0 | } |
185 | | |
186 | 0 | rc = dst_finish(&keydst) == RNP_SUCCESS; |
187 | 0 | dst_close(&keydst, !rc); |
188 | 0 | return rc; |
189 | 0 | } |
190 | | |
191 | | bool |
192 | | KeyStore::write(pgp_dest_t &dst) |
193 | 0 | { |
194 | 0 | switch (format) { |
195 | 0 | case KeyFormat::GPG: |
196 | 0 | return write_pgp(dst); |
197 | 0 | case KeyFormat::KBX: |
198 | 0 | return write_kbx(dst); |
199 | 0 | default: |
200 | 0 | RNP_LOG("Unsupported write to memory for key-store format: %d", |
201 | 0 | static_cast<int>(format)); |
202 | 0 | } |
203 | | |
204 | 0 | return false; |
205 | 0 | } |
206 | | |
207 | | void |
208 | | KeyStore::clear() |
209 | 362k | { |
210 | 362k | keybyfp.clear(); |
211 | 362k | keys.clear(); |
212 | 362k | blobs.clear(); |
213 | 362k | } |
214 | | |
215 | | size_t |
216 | | KeyStore::key_count() const |
217 | 0 | { |
218 | 0 | return keys.size(); |
219 | 0 | } |
220 | | |
221 | | bool |
222 | | KeyStore::refresh_subkey_grips(Key &key) |
223 | 215k | { |
224 | 215k | if (key.is_subkey()) { |
225 | 0 | RNP_LOG("wrong argument"); |
226 | 0 | return false; |
227 | 0 | } |
228 | | |
229 | 1.30M | for (auto &skey : keys) { |
230 | 1.30M | bool found = false; |
231 | | |
232 | | /* if we have primary_grip then we also added to subkey_grips */ |
233 | 1.30M | if (!skey.is_subkey() || skey.has_primary_fp()) { |
234 | 1.29M | continue; |
235 | 1.29M | } |
236 | | |
237 | 19.3k | for (size_t i = 0; i < skey.sig_count(); i++) { |
238 | 15.1k | auto &subsig = skey.get_sig(i); |
239 | | |
240 | 15.1k | if (subsig.sig.type() != PGP_SIG_SUBKEY) { |
241 | 11.6k | continue; |
242 | 11.6k | } |
243 | 3.49k | if (subsig.sig.has_keyfp() && (key.fp() == subsig.sig.keyfp())) { |
244 | 340 | found = true; |
245 | 340 | break; |
246 | 340 | } |
247 | 3.15k | if (subsig.sig.has_keyid() && (key.keyid() == subsig.sig.keyid())) { |
248 | 369 | found = true; |
249 | 369 | break; |
250 | 369 | } |
251 | 3.15k | } |
252 | | |
253 | 4.90k | if (found) { |
254 | 709 | try { |
255 | 709 | key.link_subkey_fp(skey); |
256 | 709 | } catch (const std::exception &e) { |
257 | | /* LCOV_EXCL_START */ |
258 | 0 | RNP_LOG("%s", e.what()); |
259 | 0 | return false; |
260 | | /* LCOV_EXCL_END */ |
261 | 0 | } |
262 | 709 | } |
263 | 4.90k | } |
264 | | |
265 | 215k | return true; |
266 | 215k | } |
267 | | |
268 | | Key * |
269 | | KeyStore::add_subkey(Key &srckey, Key *oldkey) |
270 | 247k | { |
271 | 247k | Key *primary = NULL; |
272 | 247k | if (oldkey) { |
273 | 128k | primary = primary_key(*oldkey); |
274 | 128k | } |
275 | 247k | if (!primary) { |
276 | 135k | primary = primary_key(srckey); |
277 | 135k | } |
278 | | |
279 | 247k | if (oldkey) { |
280 | | /* check for the weird case when same subkey has different primary keys */ |
281 | 128k | if (srckey.has_primary_fp() && oldkey->has_primary_fp() && |
282 | 115k | (srckey.primary_fp() != oldkey->primary_fp())) { |
283 | 40.1k | RNP_LOG_KEY("Warning: different primary keys for subkey %s", &srckey); |
284 | 40.1k | auto *srcprim = get_key(srckey.primary_fp()); |
285 | 40.1k | if (srcprim && (srcprim != primary)) { |
286 | 29.5k | srcprim->remove_subkey_fp(srckey.fp()); |
287 | 29.5k | } |
288 | 40.1k | } |
289 | | /* in case we already have key let's merge it in */ |
290 | 128k | if (!oldkey->merge(srckey, primary)) { |
291 | 0 | RNP_LOG_KEY("failed to merge subkey %s", &srckey); |
292 | 0 | RNP_LOG_KEY("primary key is %s", primary); |
293 | 0 | return NULL; |
294 | 0 | } |
295 | 128k | } else { |
296 | 119k | try { |
297 | 119k | keys.emplace_back(); |
298 | 119k | oldkey = &keys.back(); |
299 | 119k | keybyfp[srckey.fp()] = std::prev(keys.end()); |
300 | 119k | *oldkey = Key(srckey); |
301 | 119k | if (primary) { |
302 | 98.6k | primary->link_subkey_fp(*oldkey); |
303 | 98.6k | } |
304 | 119k | } catch (const std::exception &e) { |
305 | | /* LCOV_EXCL_START */ |
306 | 0 | RNP_LOG_KEY("key %s copying failed", &srckey); |
307 | 0 | RNP_LOG_KEY("primary key is %s", primary); |
308 | 0 | RNP_LOG("%s", e.what()); |
309 | 0 | if (oldkey) { |
310 | 0 | keys.pop_back(); |
311 | 0 | keybyfp.erase(srckey.fp()); |
312 | 0 | } |
313 | 0 | return nullptr; |
314 | | /* LCOV_EXCL_END */ |
315 | 0 | } |
316 | 119k | } |
317 | | |
318 | | /* validate all added keys if not disabled */ |
319 | 247k | if (!disable_validation && !oldkey->validated()) { |
320 | 22.5k | oldkey->validate_subkey(primary, secctx); |
321 | 22.5k | } |
322 | 247k | if (!oldkey->refresh_data(primary, secctx)) { |
323 | 0 | RNP_LOG_KEY("Failed to refresh subkey %s data", &srckey); |
324 | 0 | RNP_LOG_KEY("primary key is %s", primary); |
325 | 0 | } |
326 | 247k | return oldkey; |
327 | 247k | } |
328 | | |
329 | | /* add a key to keyring */ |
330 | | Key * |
331 | | KeyStore::add_key(Key &srckey) |
332 | 714k | { |
333 | 714k | assert(srckey.type() && srckey.version()); |
334 | 714k | auto *added_key = get_key(srckey.fp()); |
335 | | /* we cannot merge G10 keys - so just return it */ |
336 | 714k | if (added_key && (srckey.format == KeyFormat::G10)) { |
337 | 0 | return added_key; |
338 | 0 | } |
339 | | /* different processing for subkeys */ |
340 | 714k | if (srckey.is_subkey()) { |
341 | 247k | return add_subkey(srckey, added_key); |
342 | 247k | } |
343 | | |
344 | 466k | if (added_key) { |
345 | 251k | if (!added_key->merge(srckey)) { |
346 | 0 | RNP_LOG_KEY("failed to merge key %s", &srckey); |
347 | 0 | return NULL; |
348 | 0 | } |
349 | 251k | } else { |
350 | 215k | try { |
351 | 215k | keys.emplace_back(); |
352 | 215k | added_key = &keys.back(); |
353 | 215k | keybyfp[srckey.fp()] = std::prev(keys.end()); |
354 | 215k | *added_key = Key(srckey); |
355 | | /* primary key may be added after subkeys, so let's handle this case correctly */ |
356 | 215k | if (!refresh_subkey_grips(*added_key)) { |
357 | 0 | RNP_LOG_KEY("failed to refresh subkey grips for %s", added_key); |
358 | 0 | } |
359 | 215k | } catch (const std::exception &e) { |
360 | | /* LCOV_EXCL_START */ |
361 | 0 | RNP_LOG_KEY("key %s copying failed", &srckey); |
362 | 0 | RNP_LOG("%s", e.what()); |
363 | 0 | if (added_key) { |
364 | 0 | keys.pop_back(); |
365 | 0 | keybyfp.erase(srckey.fp()); |
366 | 0 | } |
367 | 0 | return NULL; |
368 | | /* LCOV_EXCL_END */ |
369 | 0 | } |
370 | 215k | } |
371 | | |
372 | | /* validate all added keys if not disabled or already validated */ |
373 | 466k | if (!disable_validation && !added_key->validated()) { |
374 | 147 | added_key->revalidate(*this); |
375 | 466k | } else if (!added_key->refresh_data(secctx)) { |
376 | 5.36k | RNP_LOG_KEY("Failed to refresh key %s data", &srckey); |
377 | 5.36k | } |
378 | | /* Revalidate non-self revocations for all keys in keyring, as added_key key could be a |
379 | | * revoker. Should not be time-consuming as `validate_desig_revokes()` has early exit. */ |
380 | 4.45M | for (auto &key : keys) { |
381 | 4.45M | if (&key == added_key) { |
382 | 466k | continue; |
383 | 466k | } |
384 | 3.98M | if (key.validate_desig_revokes(*this)) { |
385 | 56 | key.revalidate(*this); |
386 | 56 | } |
387 | 3.98M | } |
388 | 466k | return added_key; |
389 | 466k | } |
390 | | |
391 | | Signature * |
392 | | KeyStore::add_key_sig(const pgp::Fingerprint & keyfp, |
393 | | const pgp::pkt::Signature &sig, |
394 | | const pgp_userid_pkt_t * uid, |
395 | | bool front) |
396 | 0 | { |
397 | 0 | auto *key = get_key(keyfp); |
398 | 0 | if (!key) { |
399 | 0 | return nullptr; |
400 | 0 | } |
401 | | |
402 | 0 | bool desig_rev = false; |
403 | 0 | auto *signer = get_signer(sig); |
404 | 0 | switch (sig.type()) { |
405 | 0 | case PGP_SIG_REV_KEY: |
406 | 0 | desig_rev = signer && (signer->fp() != key->fp()); |
407 | 0 | break; |
408 | 0 | case PGP_SIG_REV_SUBKEY: |
409 | 0 | desig_rev = signer && (signer->fp() != key->primary_fp()); |
410 | 0 | break; |
411 | 0 | default: |
412 | 0 | break; |
413 | 0 | } |
414 | | /* Add to the keyring(s) */ |
415 | 0 | uint32_t uididx = UserID::None; |
416 | 0 | if (uid) { |
417 | 0 | uididx = key->uid_idx(*uid); |
418 | 0 | if (uididx == UserID::None) { |
419 | 0 | RNP_LOG("Attempt to add signature on non-existing userid."); |
420 | 0 | return nullptr; |
421 | 0 | } |
422 | 0 | } |
423 | 0 | auto &newsig = key->add_sig(sig, uididx, front); |
424 | 0 | if (desig_rev) { |
425 | 0 | key->validate_desig_revokes(*this); |
426 | 0 | } |
427 | 0 | if (key->is_primary()) { |
428 | 0 | key->refresh_data(secctx); |
429 | 0 | } else { |
430 | 0 | key->refresh_data(primary_key(*key), secctx); |
431 | 0 | } |
432 | 0 | return &newsig; |
433 | 0 | } |
434 | | |
435 | | Key * |
436 | | KeyStore::import_key(Key &srckey, bool pubkey, pgp_key_import_status_t *status) |
437 | 228k | { |
438 | | /* add public key */ |
439 | 228k | auto * exkey = get_key(srckey.fp()); |
440 | 228k | size_t expackets = exkey ? exkey->rawpkt_count() : 0; |
441 | 228k | try { |
442 | 228k | Key keycp(srckey, pubkey); |
443 | 228k | disable_validation = true; |
444 | 228k | exkey = add_key(keycp); |
445 | 228k | disable_validation = false; |
446 | 228k | if (!exkey) { |
447 | 0 | RNP_LOG("failed to add key to the keyring"); |
448 | 0 | return nullptr; |
449 | 0 | } |
450 | 228k | bool changed = exkey->rawpkt_count() > expackets; |
451 | 228k | if (changed || !exkey->validated()) { |
452 | | /* this will revalidate primary key with all of its subkeys */ |
453 | 223k | exkey->revalidate(*this); |
454 | 223k | } |
455 | 228k | if (status) { |
456 | 180k | *status = changed ? (expackets ? PGP_KEY_IMPORT_STATUS_UPDATED : |
457 | 88.9k | PGP_KEY_IMPORT_STATUS_NEW) : |
458 | 180k | PGP_KEY_IMPORT_STATUS_UNCHANGED; |
459 | 180k | } |
460 | 228k | return exkey; |
461 | 228k | } catch (const std::exception &e) { |
462 | | /* LCOV_EXCL_START */ |
463 | 138 | RNP_LOG("%s", e.what()); |
464 | 138 | disable_validation = false; |
465 | 138 | return nullptr; |
466 | | /* LCOV_EXCL_END */ |
467 | 138 | } |
468 | 228k | } |
469 | | |
470 | | pgp_sig_import_status_t |
471 | | KeyStore::import_subkey_signature(Key &key, const pgp::pkt::Signature &sig) |
472 | 0 | { |
473 | 0 | if ((sig.type() != PGP_SIG_SUBKEY) && (sig.type() != PGP_SIG_REV_SUBKEY)) { |
474 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
475 | 0 | } |
476 | 0 | auto *primary = get_signer(sig); |
477 | 0 | if (!primary || !key.has_primary_fp()) { |
478 | 0 | RNP_LOG("No primary grip or primary key"); |
479 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN_KEY; |
480 | 0 | } |
481 | 0 | if (primary->fp() != key.primary_fp()) { |
482 | 0 | RNP_LOG("Wrong subkey signature's signer."); |
483 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
484 | 0 | } |
485 | | |
486 | 0 | try { |
487 | 0 | Key tmpkey(key.pkt()); |
488 | 0 | tmpkey.add_sig(sig); |
489 | 0 | if (!tmpkey.refresh_data(primary, secctx)) { |
490 | 0 | RNP_LOG("Failed to add signature to the key."); |
491 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
492 | 0 | } |
493 | | |
494 | 0 | size_t expackets = key.rawpkt_count(); |
495 | 0 | auto nkey = add_key(tmpkey); |
496 | 0 | if (!nkey) { |
497 | 0 | RNP_LOG("Failed to add key with imported sig to the keyring"); |
498 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
499 | 0 | } |
500 | 0 | return (nkey->rawpkt_count() > expackets) ? PGP_SIG_IMPORT_STATUS_NEW : |
501 | 0 | PGP_SIG_IMPORT_STATUS_UNCHANGED; |
502 | 0 | } catch (const std::exception &e) { |
503 | | /* LCOV_EXCL_START */ |
504 | 0 | RNP_LOG("%s", e.what()); |
505 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
506 | | /* LCOV_EXCL_END */ |
507 | 0 | } |
508 | 0 | } |
509 | | |
510 | | pgp_sig_import_status_t |
511 | | KeyStore::import_signature(Key &key, const pgp::pkt::Signature &sig) |
512 | 0 | { |
513 | 0 | if (key.is_subkey()) { |
514 | 0 | return import_subkey_signature(key, sig); |
515 | 0 | } |
516 | 0 | if ((sig.type() != PGP_SIG_DIRECT) && (sig.type() != PGP_SIG_REV_KEY)) { |
517 | 0 | RNP_LOG("Wrong signature type: %d", (int) sig.type()); |
518 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
519 | 0 | } |
520 | | |
521 | 0 | try { |
522 | 0 | Key tmpkey(key.pkt()); |
523 | 0 | tmpkey.add_sig(sig); |
524 | 0 | if (!tmpkey.refresh_data(secctx)) { |
525 | 0 | RNP_LOG("Failed to add signature to the key."); |
526 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
527 | 0 | } |
528 | | |
529 | 0 | size_t expackets = key.rawpkt_count(); |
530 | 0 | auto nkey = add_key(tmpkey); |
531 | 0 | if (!nkey) { |
532 | 0 | RNP_LOG("Failed to add key with imported sig to the keyring"); |
533 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
534 | 0 | } |
535 | 0 | return (nkey->rawpkt_count() > expackets) ? PGP_SIG_IMPORT_STATUS_NEW : |
536 | 0 | PGP_SIG_IMPORT_STATUS_UNCHANGED; |
537 | 0 | } catch (const std::exception &e) { |
538 | | /* LCOV_EXCL_START */ |
539 | 0 | RNP_LOG("%s", e.what()); |
540 | 0 | return PGP_SIG_IMPORT_STATUS_UNKNOWN; |
541 | | /* LCOV_EXCL_END */ |
542 | 0 | } |
543 | 0 | } |
544 | | |
545 | | Key * |
546 | | KeyStore::import_signature(const pgp::pkt::Signature &sig, pgp_sig_import_status_t *status) |
547 | 13.5k | { |
548 | 13.5k | pgp_sig_import_status_t tmp_status = PGP_SIG_IMPORT_STATUS_UNKNOWN; |
549 | 13.5k | if (!status) { |
550 | 0 | status = &tmp_status; |
551 | 0 | } |
552 | 13.5k | *status = PGP_SIG_IMPORT_STATUS_UNKNOWN; |
553 | | |
554 | | /* we support only direct-key and key revocation signatures here */ |
555 | 13.5k | if ((sig.type() != PGP_SIG_DIRECT) && (sig.type() != PGP_SIG_REV_KEY)) { |
556 | 5.60k | return nullptr; |
557 | 5.60k | } |
558 | | |
559 | 7.91k | auto *res_key = get_signer(sig); |
560 | 7.91k | if (!res_key || !res_key->is_primary()) { |
561 | 7.91k | *status = PGP_SIG_IMPORT_STATUS_UNKNOWN_KEY; |
562 | 7.91k | return nullptr; |
563 | 7.91k | } |
564 | 0 | *status = import_signature(*res_key, sig); |
565 | 0 | return res_key; |
566 | 7.91k | } |
567 | | |
568 | | bool |
569 | | KeyStore::remove_key(const Key &key, bool subkeys) |
570 | 1.58k | { |
571 | 1.58k | auto it = keybyfp.find(key.fp()); |
572 | 1.58k | if (it == keybyfp.end()) { |
573 | 0 | return false; |
574 | 0 | } |
575 | | |
576 | | /* cleanup primary_grip (or subkey)/subkey_grips */ |
577 | 1.58k | if (key.is_primary() && key.subkey_count()) { |
578 | 3.71k | for (size_t i = 0; i < key.subkey_count(); i++) { |
579 | 2.51k | auto its = keybyfp.find(key.get_subkey_fp(i)); |
580 | 2.51k | if (its == keybyfp.end()) { |
581 | 0 | continue; |
582 | 0 | } |
583 | | /* if subkeys are deleted then no need to update grips */ |
584 | 2.51k | if (subkeys) { |
585 | 0 | keys.erase(its->second); |
586 | 0 | keybyfp.erase(its); |
587 | 0 | continue; |
588 | 0 | } |
589 | 2.51k | its->second->unset_primary_fp(); |
590 | 2.51k | } |
591 | 1.20k | } |
592 | 1.58k | if (key.is_subkey() && key.has_primary_fp()) { |
593 | 50 | auto *primary = primary_key(key); |
594 | 50 | if (primary) { |
595 | 49 | primary->remove_subkey_fp(key.fp()); |
596 | 49 | } |
597 | 50 | } |
598 | | |
599 | 1.58k | keys.erase(it->second); |
600 | 1.58k | keybyfp.erase(it); |
601 | 1.58k | return true; |
602 | 1.58k | } |
603 | | |
604 | | const Key * |
605 | | KeyStore::get_key(const pgp::Fingerprint &fpr) const |
606 | 0 | { |
607 | 0 | auto it = keybyfp.find(fpr); |
608 | 0 | if (it == keybyfp.end()) { |
609 | 0 | return nullptr; |
610 | 0 | } |
611 | 0 | return &*it->second; |
612 | 0 | } |
613 | | |
614 | | Key * |
615 | | KeyStore::get_key(const pgp::Fingerprint &fpr) |
616 | 2.65M | { |
617 | 2.65M | auto it = keybyfp.find(fpr); |
618 | 2.65M | if (it == keybyfp.end()) { |
619 | 603k | return nullptr; |
620 | 603k | } |
621 | 2.05M | return &*it->second; |
622 | 2.65M | } |
623 | | |
624 | | Key * |
625 | | KeyStore::get_subkey(const Key &key, size_t idx) |
626 | 522k | { |
627 | 522k | if (idx >= key.subkey_count()) { |
628 | 0 | return nullptr; |
629 | 0 | } |
630 | 522k | return get_key(key.get_subkey_fp(idx)); |
631 | 522k | } |
632 | | |
633 | | Key * |
634 | | KeyStore::primary_key(const Key &subkey) |
635 | 361k | { |
636 | 361k | if (!subkey.is_subkey()) { |
637 | 1.15k | return nullptr; |
638 | 1.15k | } |
639 | | |
640 | 360k | if (subkey.has_primary_fp()) { |
641 | 308k | Key *primary = get_key(subkey.primary_fp()); |
642 | 308k | return primary && primary->is_primary() ? primary : nullptr; |
643 | 308k | } |
644 | | |
645 | 297k | for (size_t i = 0; i < subkey.sig_count(); i++) { |
646 | 250k | auto &subsig = subkey.get_sig(i); |
647 | 250k | if (subsig.sig.type() != PGP_SIG_SUBKEY) { |
648 | 218k | continue; |
649 | 218k | } |
650 | | |
651 | 31.9k | Key *primary = get_signer(subsig.sig); |
652 | 31.9k | if (primary && primary->is_primary()) { |
653 | 4.41k | return primary; |
654 | 4.41k | } |
655 | 31.9k | } |
656 | 47.2k | return nullptr; |
657 | 51.6k | } |
658 | | |
659 | | Key * |
660 | | KeyStore::search(const KeySearch &search, Key *after) |
661 | 399k | { |
662 | | // since keys are distinguished by fingerprint then just do map lookup |
663 | 399k | if (search.type() == KeySearch::Type::Fingerprint) { |
664 | 90.3k | auto fpsearch = dynamic_cast<const KeyFingerprintSearch *>(&search); |
665 | 90.3k | assert(fpsearch != nullptr); |
666 | 90.3k | auto key = get_key(fpsearch->get_fp()); |
667 | 90.3k | if (after && (after != key)) { |
668 | 0 | RNP_LOG("searching with invalid after param"); |
669 | 0 | return nullptr; |
670 | 0 | } |
671 | | // return NULL if after is specified |
672 | 90.3k | return after ? nullptr : key; |
673 | 90.3k | } |
674 | | |
675 | | // if after is provided, make sure it is a member of the appropriate list |
676 | 309k | auto it = std::find_if( |
677 | 309k | keys.begin(), keys.end(), [after](const Key &key) { return !after || (after == &key); }); |
678 | 309k | if (after && (it == keys.end())) { |
679 | 0 | RNP_LOG("searching with non-keyrings after param"); |
680 | 0 | return nullptr; |
681 | 0 | } |
682 | 309k | if (after) { |
683 | 0 | it = std::next(it); |
684 | 0 | } |
685 | 309k | it = |
686 | 309k | std::find_if(it, keys.end(), [&search](const Key &key) { return search.matches(key); }); |
687 | 309k | return (it == keys.end()) ? nullptr : &(*it); |
688 | 309k | } |
689 | | |
690 | | Key * |
691 | | KeyStore::get_signer(const pgp::pkt::Signature &sig, const KeyProvider *prov) |
692 | 121k | { |
693 | | /* if we have fingerprint let's check it */ |
694 | 121k | std::unique_ptr<KeySearch> ks; |
695 | 121k | if (sig.has_keyfp()) { |
696 | 88.6k | ks = KeySearch::create(sig.keyfp()); |
697 | 88.6k | } else if (sig.has_keyid()) { |
698 | 16.4k | ks = KeySearch::create(sig.keyid()); |
699 | 16.6k | } else { |
700 | 16.6k | RNP_LOG("No way to search for the signer."); |
701 | 16.6k | return nullptr; |
702 | 16.6k | } |
703 | | |
704 | 105k | auto key = search(*ks); |
705 | 105k | if (key || !prov) { |
706 | 105k | return key; |
707 | 105k | } |
708 | 0 | return prov->request_key(*ks, PGP_OP_VERIFY); |
709 | 105k | } |
710 | | |
711 | | KeyStore::KeyStore(const std::string &_path, SecurityContext &ctx, KeyFormat _format) |
712 | 358k | : secctx(ctx) |
713 | 358k | { |
714 | 358k | if (_format == KeyFormat::Unknown) { |
715 | 0 | RNP_LOG("Invalid key store format"); |
716 | 0 | throw std::invalid_argument("format"); |
717 | 0 | } |
718 | 358k | format = _format; |
719 | 358k | path = _path; |
720 | 358k | } |
721 | | |
722 | | KeyStore::~KeyStore() |
723 | 362k | { |
724 | 362k | clear(); |
725 | 362k | } |
726 | | } // namespace rnp |