Coverage Report

Created: 2025-08-28 06:21

/src/rnp/src/librekey/key_store_kbx.cpp
Line
Count
Source (jump to first uncovered line)
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
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
18
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
 * POSSIBILITY OF SUCH DAMAGE.
25
 */
26
27
#include <sys/types.h>
28
#ifdef HAVE_SYS_PARAM_H
29
#include <sys/param.h>
30
#else
31
#include "uniwin.h"
32
#endif
33
#include <string.h>
34
#include <stdint.h>
35
#include <time.h>
36
#ifndef __STDC_FORMAT_MACROS
37
#define __STDC_FORMAT_MACROS
38
#endif
39
#include <cinttypes>
40
#include <cassert>
41
42
#include "key.hpp"
43
#include "kbx_blob.hpp"
44
#include "rekey/rnp_key_store.h"
45
#include <librepgp/stream-sig.h>
46
47
/* same limit with GnuPG 2.1 */
48
186k
#define BLOB_SIZE_LIMIT (5 * 1024 * 1024)
49
/* limit the number of keys/sigs/uids in the blob */
50
207k
#define BLOB_OBJ_LIMIT 0x8000
51
52
420k
#define BLOB_HEADER_SIZE 0x5
53
13.4k
#define BLOB_FIRST_SIZE 0x20
54
158k
#define BLOB_KEY_SIZE 0x1C
55
98.1k
#define BLOB_UID_SIZE 0x0C
56
820k
#define BLOB_SIG_SIZE 0x04
57
68.8k
#define BLOB_VALIDITY_SIZE 0x10
58
59
uint8_t
60
kbx_blob_t::ru8(size_t idx)
61
331k
{
62
331k
    return image_[idx];
63
331k
}
64
65
uint16_t
66
kbx_blob_t::ru16(size_t idx)
67
674k
{
68
674k
    return read_uint16(image_.data() + idx);
69
674k
}
70
71
uint32_t
72
kbx_blob_t::ru32(size_t idx)
73
1.25M
{
74
1.25M
    return read_uint32(image_.data() + idx);
75
1.25M
}
76
77
kbx_blob_t::kbx_blob_t(std::vector<uint8_t> &data)
78
92.4k
{
79
92.4k
    if (data.size() < BLOB_HEADER_SIZE) {
80
0
        RNP_LOG("Too small KBX blob.");
81
0
        throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
82
0
    }
83
92.4k
    uint32_t len = read_uint32(data.data());
84
92.4k
    if (len > BLOB_SIZE_LIMIT) {
85
0
        RNP_LOG("Too large KBX blob.");
86
0
        throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
87
0
    }
88
92.4k
    if (len != data.size()) {
89
0
        RNP_LOG("KBX blob size mismatch.");
90
0
        throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
91
0
    }
92
92.4k
    image_ = data;
93
92.4k
    type_ = (kbx_blob_type_t) ru8(4);
94
92.4k
}
95
96
bool
97
kbx_header_blob_t::parse()
98
3.12k
{
99
3.12k
    if (length() != BLOB_FIRST_SIZE) {
100
21
        RNP_LOG("The first blob has wrong length: %" PRIu32 " but expected %d",
101
0
                length(),
102
0
                (int) BLOB_FIRST_SIZE);
103
0
        return false;
104
21
    }
105
106
3.10k
    size_t idx = BLOB_HEADER_SIZE;
107
3.10k
    version_ = ru8(idx++);
108
3.10k
    if (version_ != 1) {
109
11
        RNP_LOG("Wrong version, expect 1 but has %" PRIu8, version_);
110
0
        return false;
111
11
    }
112
113
3.09k
    flags_ = ru16(idx);
114
3.09k
    idx += 2;
115
116
    // blob should contains a magic KBXf
117
3.09k
    if (memcmp(image_.data() + idx, "KBXf", 4)) {
118
33
        RNP_LOG("The first blob hasn't got a KBXf magic string");
119
0
        return false;
120
33
    }
121
3.05k
    idx += 4;
122
    // RFU
123
3.05k
    idx += 4;
124
    // File creation time
125
3.05k
    file_created_at_ = ru32(idx);
126
3.05k
    idx += 4;
127
    // Duplicated?
128
3.05k
    file_created_at_ = ru32(idx);
129
    // RFU +4 bytes
130
    // RFU +4 bytes
131
3.05k
    return true;
132
3.09k
}
133
134
bool
135
kbx_pgp_blob_t::parse()
136
69.2k
{
137
    /* Skip parsing of X.509 and empty blobs. */
138
69.2k
    if (type_ != KBX_PGP_BLOB) {
139
0
        return true;
140
0
    }
141
69.2k
    if (image_.size() < 15 + BLOB_HEADER_SIZE) {
142
6
        RNP_LOG("Too few data in the blob.");
143
0
        return false;
144
6
    }
145
146
69.2k
    size_t idx = BLOB_HEADER_SIZE;
147
    /* version */
148
69.2k
    version_ = ru8(idx++);
149
69.2k
    if (version_ != 1) {
150
13
        RNP_LOG("Wrong version: %" PRIu8, version_);
151
0
        return false;
152
13
    }
153
    /* flags */
154
69.2k
    flags_ = ru16(idx);
155
69.2k
    idx += 2;
156
    /* keyblock offset */
157
69.2k
    keyblock_offset_ = ru32(idx);
158
69.2k
    idx += 4;
159
    /* keyblock length */
160
69.2k
    keyblock_length_ = ru32(idx);
161
69.2k
    idx += 4;
162
163
69.2k
    if ((keyblock_offset_ > image_.size()) ||
164
69.2k
        (keyblock_offset_ > (UINT32_MAX - keyblock_length_)) ||
165
69.2k
        (image_.size() < (keyblock_offset_ + keyblock_length_))) {
166
90
        RNP_LOG("Wrong keyblock offset/length, blob size: %zu"
167
0
                ", keyblock offset: %" PRIu32 ", length: %" PRIu32,
168
0
                image_.size(),
169
0
                keyblock_offset_,
170
0
                keyblock_length_);
171
0
        return false;
172
90
    }
173
    /* number of key blocks */
174
69.1k
    size_t nkeys = ru16(idx);
175
69.1k
    idx += 2;
176
69.1k
    if (nkeys < 1) {
177
7
        RNP_LOG("PGP blob should contain at least 1 key");
178
0
        return false;
179
7
    }
180
69.1k
    if (nkeys > BLOB_OBJ_LIMIT) {
181
7
        RNP_LOG("Too many keys in the PGP blob");
182
0
        return false;
183
7
    }
184
185
    /* Size of the single key record */
186
69.1k
    size_t keys_len = ru16(idx);
187
69.1k
    idx += 2;
188
69.1k
    if (keys_len < BLOB_KEY_SIZE) {
189
9
        RNP_LOG(
190
0
          "Key record needs %d bytes, but contains: %zu bytes", (int) BLOB_KEY_SIZE, keys_len);
191
0
        return false;
192
9
    }
193
194
158k
    for (size_t i = 0; i < nkeys; i++) {
195
89.7k
        if (image_.size() - idx < keys_len) {
196
51
            RNP_LOG("Too few bytes left for key blob");
197
0
            return false;
198
51
        }
199
200
89.6k
        kbx_pgp_key_t nkey = {};
201
        /* copy fingerprint */
202
89.6k
        memcpy(nkey.fp, &image_[idx], 20);
203
89.6k
        idx += 20;
204
        /* keyid offset */
205
89.6k
        nkey.keyid_offset = ru32(idx);
206
89.6k
        idx += 4;
207
        /* flags */
208
89.6k
        nkey.flags = ru16(idx);
209
89.6k
        idx += 2;
210
        /* RFU */
211
89.6k
        idx += 2;
212
        /* skip padding bytes if it existed */
213
89.6k
        idx += keys_len - BLOB_KEY_SIZE;
214
89.6k
        keys_.push_back(std::move(nkey));
215
89.6k
    }
216
217
69.1k
    if (image_.size() - idx < 2) {
218
2
        RNP_LOG("No data for sn_size");
219
0
        return false;
220
2
    }
221
69.0k
    size_t sn_size = ru16(idx);
222
69.0k
    idx += 2;
223
224
69.0k
    if (image_.size() - idx < sn_size) {
225
40
        RNP_LOG("SN is %zu, while bytes left are %zu", sn_size, image_.size() - idx);
226
0
        return false;
227
40
    }
228
229
69.0k
    if (sn_size) {
230
1.02k
        sn_ = {image_.begin() + idx, image_.begin() + idx + sn_size};
231
1.02k
        idx += sn_size;
232
1.02k
    }
233
234
69.0k
    if (image_.size() - idx < 4) {
235
9
        RNP_LOG("Too few data for uids");
236
0
        return false;
237
9
    }
238
69.0k
    size_t nuids = ru16(idx);
239
69.0k
    if (nuids > BLOB_OBJ_LIMIT) {
240
12
        RNP_LOG("Too many uids in the PGP blob");
241
0
        return false;
242
12
    }
243
244
69.0k
    size_t uids_len = ru16(idx + 2);
245
69.0k
    idx += 4;
246
247
69.0k
    if (uids_len < BLOB_UID_SIZE) {
248
17
        RNP_LOG("Too few bytes for uid struct: %zu", uids_len);
249
0
        return false;
250
17
    }
251
252
98.1k
    for (size_t i = 0; i < nuids; i++) {
253
29.1k
        if (image_.size() - idx < uids_len) {
254
58
            RNP_LOG("Too few bytes to read uid struct.");
255
0
            return false;
256
58
        }
257
29.0k
        kbx_pgp_uid_t nuid = {};
258
        /* offset */
259
29.0k
        nuid.offset = ru32(idx);
260
29.0k
        idx += 4;
261
        /* length */
262
29.0k
        nuid.length = ru32(idx);
263
29.0k
        idx += 4;
264
        /* flags */
265
29.0k
        nuid.flags = ru16(idx);
266
29.0k
        idx += 2;
267
        /* validity */
268
29.0k
        nuid.validity = ru8(idx);
269
29.0k
        idx++;
270
        /* RFU */
271
29.0k
        idx++;
272
        // skip padding bytes if it existed
273
29.0k
        idx += uids_len - BLOB_UID_SIZE;
274
275
29.0k
        uids_.push_back(std::move(nuid));
276
29.0k
    }
277
278
68.9k
    if (image_.size() - idx < 4) {
279
4
        RNP_LOG("No data left for sigs");
280
0
        return false;
281
4
    }
282
283
68.9k
    size_t nsigs = ru16(idx);
284
68.9k
    if (nsigs > BLOB_OBJ_LIMIT) {
285
5
        RNP_LOG("Too many sigs in the PGP blob");
286
0
        return false;
287
5
    }
288
289
68.9k
    size_t sigs_len = ru16(idx + 2);
290
68.9k
    idx += 4;
291
292
68.9k
    if (sigs_len < BLOB_SIG_SIZE) {
293
7
        RNP_LOG("Too small SIGN structure: %zu", sigs_len);
294
0
        return false;
295
7
    }
296
297
820k
    for (size_t i = 0; i < nsigs; i++) {
298
751k
        if (image_.size() - idx < sigs_len) {
299
72
            RNP_LOG("Too few data for sig");
300
0
            return false;
301
72
        }
302
303
751k
        kbx_pgp_sig_t nsig = {};
304
751k
        nsig.expired = ru32(idx);
305
751k
        idx += 4;
306
307
        // skip padding bytes if it existed
308
751k
        idx += (sigs_len - BLOB_SIG_SIZE);
309
310
751k
        sigs_.push_back(nsig);
311
751k
    }
312
313
68.8k
    if (image_.size() - idx < BLOB_VALIDITY_SIZE) {
314
6
        RNP_LOG("Too few data for trust/validities");
315
0
        return false;
316
6
    }
317
318
68.8k
    ownertrust_ = ru8(idx);
319
68.8k
    idx++;
320
68.8k
    all_validity_ = ru8(idx);
321
68.8k
    idx++;
322
    // RFU
323
68.8k
    idx += 2;
324
68.8k
    recheck_after_ = ru32(idx);
325
68.8k
    idx += 4;
326
68.8k
    latest_timestamp_ = ru32(idx);
327
68.8k
    idx += 4;
328
68.8k
    blob_created_at_ = ru32(idx);
329
    // do not forget to idx += 4 on further expansion
330
331
    // here starts keyblock, UID and reserved space for future usage
332
333
    // Maybe we should add checksum verify but GnuPG never checked it
334
    // Checksum is last 20 bytes of blob and it is SHA-1, if it invalid MD5 and starts from 4
335
    // zero it is MD5.
336
337
68.8k
    return true;
338
68.8k
}
339
340
namespace rnp {
341
namespace {
342
std::unique_ptr<kbx_blob_t>
343
kbx_parse_blob(const uint8_t *image, size_t image_len)
344
92.5k
{
345
92.5k
    std::unique_ptr<kbx_blob_t> blob;
346
    // a blob shouldn't be less of length + type
347
92.5k
    if (image_len < BLOB_HEADER_SIZE) {
348
0
        RNP_LOG("Blob size is %zu but it shouldn't be less of header", image_len);
349
0
        return blob;
350
0
    }
351
352
92.5k
    try {
353
92.5k
        std::vector<uint8_t> data(image, image + image_len);
354
92.5k
        kbx_blob_type_t      type = (kbx_blob_type_t) image[4];
355
356
92.5k
        switch (type) {
357
13.6k
        case KBX_EMPTY_BLOB:
358
13.6k
            blob = std::unique_ptr<kbx_blob_t>(new kbx_blob_t(data));
359
13.6k
            break;
360
3.12k
        case KBX_HEADER_BLOB:
361
3.12k
            blob = std::unique_ptr<kbx_blob_t>(new kbx_header_blob_t(data));
362
3.12k
            break;
363
69.2k
        case KBX_PGP_BLOB:
364
69.2k
            blob = std::unique_ptr<kbx_blob_t>(new kbx_pgp_blob_t(data));
365
69.2k
            break;
366
6.45k
        case KBX_X509_BLOB:
367
            // current we doesn't parse X509 blob, so, keep it as is
368
6.45k
            blob = std::unique_ptr<kbx_blob_t>(new kbx_blob_t(data));
369
6.45k
            break;
370
        // unsupported blob type
371
13
        default:
372
13
            RNP_LOG("Unsupported blob type: %d", (int) type);
373
0
            return blob;
374
92.5k
        }
375
376
92.4k
        if (!blob->parse()) {
377
480
            return NULL;
378
480
        }
379
92.4k
    } catch (const std::exception &e) {
380
        /* LCOV_EXCL_START */
381
0
        RNP_LOG("%s", e.what());
382
0
        return NULL;
383
        /* LCOV_EXCL_END */
384
0
    }
385
92.0k
    return blob;
386
92.5k
}
387
} // namespace
388
389
bool
390
KeyStore::load_kbx(pgp_source_t &src, const KeyProvider *key_provider)
391
10.3k
{
392
10.3k
    try {
393
10.3k
        MemorySource mem(src);
394
10.3k
        size_t       has_bytes = mem.size();
395
10.3k
        uint8_t *    buf = (uint8_t *) mem.memory();
396
397
10.3k
        if (has_bytes < BLOB_FIRST_SIZE) {
398
14
            RNP_LOG("Too few bytes for valid KBX");
399
0
            return false;
400
14
        }
401
99.1k
        while (has_bytes > 4) {
402
94.1k
            size_t blob_length = read_uint32(buf);
403
94.1k
            if (blob_length > BLOB_SIZE_LIMIT) {
404
774
                RNP_LOG("Blob size is %zu bytes but limit is %d bytes",
405
0
                        blob_length,
406
0
                        (int) BLOB_SIZE_LIMIT);
407
0
                return false;
408
774
            }
409
93.3k
            if (blob_length < BLOB_HEADER_SIZE) {
410
97
                RNP_LOG("Too small blob header size");
411
0
                return false;
412
97
            }
413
93.2k
            if (has_bytes < blob_length) {
414
750
                RNP_LOG("Blob have size %zu bytes but file contains only %zu bytes",
415
0
                        blob_length,
416
0
                        has_bytes);
417
0
                return false;
418
750
            }
419
92.5k
            auto blob = kbx_parse_blob(buf, blob_length);
420
92.5k
            if (!blob.get()) {
421
493
                RNP_LOG("Failed to parse blob");
422
0
                return false;
423
493
            }
424
92.0k
            kbx_blob_t *pblob = blob.get();
425
92.0k
            blobs.push_back(std::move(blob));
426
427
92.0k
            if (pblob->type() == KBX_PGP_BLOB) {
428
                // parse keyblock if it existed
429
68.8k
                kbx_pgp_blob_t &pgp_blob = dynamic_cast<kbx_pgp_blob_t &>(*pblob);
430
68.8k
                if (!pgp_blob.keyblock_length()) {
431
1
                    RNP_LOG("PGP blob have zero size");
432
0
                    return false;
433
1
                }
434
435
68.8k
                MemorySource blsrc(pgp_blob.image().data() + pgp_blob.keyblock_offset(),
436
68.8k
                                   pgp_blob.keyblock_length(),
437
68.8k
                                   false);
438
68.8k
                if (load_pgp(blsrc.src())) {
439
3.14k
                    return false;
440
3.14k
                }
441
68.8k
            }
442
443
88.8k
            has_bytes -= blob_length;
444
88.8k
            buf += blob_length;
445
88.8k
        }
446
5.04k
        if (has_bytes) {
447
1.17k
            RNP_LOG("KBX source has excess trailing bytes");
448
1.17k
        }
449
0
        return true;
450
10.2k
    } catch (const std::exception &e) {
451
        /* LCOV_EXCL_START */
452
0
        RNP_LOG("%s", e.what());
453
0
        return false;
454
        /* LCOV_EXCL_END */
455
0
    }
456
10.3k
}
457
458
namespace {
459
bool
460
pbuf(pgp_dest_t &dst, const void *buf, size_t len)
461
0
{
462
0
    dst_write(&dst, buf, len);
463
0
    return dst.werr == RNP_SUCCESS;
464
0
}
465
466
bool
467
pu8(pgp_dest_t &dst, uint8_t p)
468
0
{
469
0
    return pbuf(dst, &p, 1);
470
0
}
471
472
bool
473
pu16(pgp_dest_t &dst, uint16_t f)
474
0
{
475
0
    uint8_t p[2];
476
0
    p[0] = (uint8_t)(f >> 8);
477
0
    p[1] = (uint8_t) f;
478
0
    return pbuf(dst, p, 2);
479
0
}
480
481
bool
482
pu32(pgp_dest_t &dst, uint32_t f)
483
0
{
484
0
    uint8_t p[4];
485
0
    write_uint32(p, f);
486
0
    return pbuf(dst, p, 4);
487
0
}
488
489
bool
490
kbx_write_header(const KeyStore &key_store, pgp_dest_t &dst)
491
0
{
492
0
    uint16_t flags = 0;
493
0
    uint32_t file_created_at = key_store.secctx.time();
494
495
0
    if (!key_store.blobs.empty() && (key_store.blobs[0]->type() == KBX_HEADER_BLOB)) {
496
0
        kbx_header_blob_t &blob = dynamic_cast<kbx_header_blob_t &>(*key_store.blobs[0]);
497
0
        file_created_at = blob.file_created_at();
498
0
    }
499
500
0
    return !(!pu32(dst, BLOB_FIRST_SIZE) || !pu8(dst, KBX_HEADER_BLOB) ||
501
0
             !pu8(dst, 1)                                                   // version
502
0
             || !pu16(dst, flags) || !pbuf(dst, "KBXf", 4) || !pu32(dst, 0) // RFU
503
0
             || !pu32(dst, 0)                                               // RFU
504
0
             || !pu32(dst, file_created_at) || !pu32(dst, key_store.secctx.time()) ||
505
0
             !pu32(dst, 0)); // RFU
506
0
}
507
508
bool
509
kbx_write_pgp(const KeyStore &key_store, const Key &key, pgp_dest_t &dst)
510
0
{
511
0
    MemoryDest mem(NULL, BLOB_SIZE_LIMIT);
512
513
0
    if (!pu32(mem.dst(), 0)) { // length, we don't know length of blob yet, so it's 0
514
0
        return false;
515
0
    }
516
517
0
    if (!pu8(mem.dst(), KBX_PGP_BLOB) || !pu8(mem.dst(), 1)) { // type, version
518
0
        return false;
519
0
    }
520
521
0
    if (!pu16(mem.dst(), 0)) { // flags, not used by GnuPG
522
0
        return false;
523
0
    }
524
525
0
    if (!pu32(mem.dst(), 0) ||
526
0
        !pu32(mem.dst(), 0)) { // offset and length of keyblock, update later
527
0
        return false;
528
0
    }
529
530
0
    if (!pu16(mem.dst(), 1 + key.subkey_count())) { // number of keys in keyblock
531
0
        return false;
532
0
    }
533
0
    if (!pu16(mem.dst(), 28)) { // size of key info structure)
534
0
        return false;
535
0
    }
536
537
0
    if (!pbuf(mem.dst(), key.fp().data(), key.fp().size()) ||
538
0
        !pu32(mem.dst(), mem.writeb() - 8) || // offset to keyid (part of fpr for V4)
539
0
        !pu16(mem.dst(), 0) ||                // flags, not used by GnuPG
540
0
        !pu16(mem.dst(), 0)) {                // RFU
541
0
        return false;
542
0
    }
543
544
    // same as above, for each subkey
545
0
    std::vector<uint32_t> subkey_sig_expirations;
546
0
    for (auto &sfp : key.subkey_fps()) {
547
0
        auto *subkey = key_store.get_key(sfp);
548
0
        if (!subkey || !pbuf(mem.dst(), subkey->fp().data(), subkey->fp().size()) ||
549
0
            !pu32(mem.dst(), mem.writeb() - 8) || // offset to keyid (part of fpr for V4)
550
0
            !pu16(mem.dst(), 0) ||                // flags, not used by GnuPG
551
0
            !pu16(mem.dst(), 0)) {                // RFU
552
0
            return false;
553
0
        }
554
        // load signature expirations while we're at it
555
0
        for (size_t i = 0; i < subkey->sig_count(); i++) {
556
0
            uint32_t expiration = subkey->get_sig(i).sig.key_expiration();
557
0
            subkey_sig_expirations.push_back(expiration);
558
0
        }
559
0
    }
560
561
0
    if (!pu16(mem.dst(), 0)) { // Zero size of serial number
562
0
        return false;
563
0
    }
564
565
    // skip serial number
566
0
    if (!pu16(mem.dst(), key.uid_count()) || !pu16(mem.dst(), 12)) {
567
0
        return false;
568
0
    }
569
570
0
    size_t uid_start = mem.writeb();
571
0
    for (size_t i = 0; i < key.uid_count(); i++) {
572
0
        if (!pu32(mem.dst(), 0) ||
573
0
            !pu32(mem.dst(), 0)) { // UID offset and length, update when blob has done
574
0
            return false;
575
0
        }
576
577
0
        if (!pu16(mem.dst(), 0)) { // flags, (not yet used)
578
0
            return false;
579
0
        }
580
581
0
        if (!pu8(mem.dst(), 0) || !pu8(mem.dst(), 0)) { // Validity & RFU
582
0
            return false;
583
0
        }
584
0
    }
585
586
0
    if (!pu16(mem.dst(), key.sig_count() + subkey_sig_expirations.size()) ||
587
0
        !pu16(mem.dst(), 4)) {
588
0
        return false;
589
0
    }
590
591
0
    for (size_t i = 0; i < key.sig_count(); i++) {
592
0
        if (!pu32(mem.dst(), key.get_sig(i).sig.key_expiration())) {
593
0
            return false;
594
0
        }
595
0
    }
596
0
    for (auto &expiration : subkey_sig_expirations) {
597
0
        if (!pu32(mem.dst(), expiration)) {
598
0
            return false;
599
0
        }
600
0
    }
601
602
0
    if (!pu8(mem.dst(), 0) ||
603
0
        !pu8(mem.dst(), 0)) { // Assigned ownertrust & All_Validity (not yet used)
604
0
        return false;
605
0
    }
606
607
0
    if (!pu16(mem.dst(), 0) || !pu32(mem.dst(), 0)) { // RFU & Recheck_after
608
0
        return false;
609
0
    }
610
611
0
    if (!pu32(mem.dst(), key_store.secctx.time()) ||
612
0
        !pu32(mem.dst(), key_store.secctx.time())) { // Latest timestamp && created
613
0
        return false;
614
0
    }
615
616
0
    if (!pu32(mem.dst(), 0)) { // Size of reserved space
617
0
        return false;
618
0
    }
619
620
    // wrtite UID, we might redesign PGP write and use this information from keyblob
621
0
    for (size_t i = 0; i < key.uid_count(); i++) {
622
0
        const auto &uid = key.get_uid(i);
623
0
        uint8_t *   p = (uint8_t *) mem.memory() + uid_start + (12 * i);
624
        /* store absolute uid offset in the output stream */
625
0
        uint32_t pt = mem.writeb() + dst.writeb;
626
0
        write_uint32(p, pt);
627
        /* and uid length */
628
0
        pt = uid.str.size();
629
0
        write_uint32(p + 4, pt);
630
        /* uid data itself */
631
0
        if (!pbuf(mem.dst(), uid.str.c_str(), pt)) {
632
0
            return false;
633
0
        }
634
0
    }
635
636
    /* write keyblock and fix the offset/length */
637
0
    size_t   key_start = mem.writeb();
638
0
    uint32_t pt = key_start;
639
0
    uint8_t *p = (uint8_t *) mem.memory() + 8;
640
0
    write_uint32(p, pt);
641
642
0
    key.write(mem.dst());
643
0
    if (mem.werr()) {
644
0
        return false;
645
0
    }
646
647
0
    for (auto &sfp : key.subkey_fps()) {
648
0
        auto *subkey = key_store.get_key(sfp);
649
0
        if (!subkey) {
650
0
            return false;
651
0
        }
652
0
        subkey->write(mem.dst());
653
0
        if (mem.werr()) {
654
0
            return false;
655
0
        }
656
0
    }
657
658
    /* key blob length */
659
0
    pt = mem.writeb() - key_start;
660
0
    p = (uint8_t *) mem.memory() + 12;
661
0
    write_uint32(p, pt);
662
663
    // fix the length of blob
664
0
    pt = mem.writeb() + 20;
665
0
    p = (uint8_t *) mem.memory();
666
0
    write_uint32(p, pt);
667
668
    // checksum
669
0
    auto hash = rnp::Hash::create(PGP_HASH_SHA1);
670
0
    hash->add(mem.memory(), mem.writeb());
671
0
    uint8_t checksum[PGP_SHA1_HASH_SIZE];
672
0
    assert(hash->size() == sizeof(checksum));
673
0
    hash->finish(checksum);
674
675
0
    if (!(pbuf(mem.dst(), checksum, PGP_SHA1_HASH_SIZE))) {
676
0
        return false;
677
0
    }
678
679
    /* finally write to the output */
680
0
    dst_write(&dst, mem.memory(), mem.writeb());
681
0
    return !dst.werr;
682
0
}
683
684
bool
685
kbx_write_x509(const KeyStore &key_store, pgp_dest_t &dst)
686
0
{
687
0
    for (auto &blob : key_store.blobs) {
688
0
        if (blob->type() != KBX_X509_BLOB) {
689
0
            continue;
690
0
        }
691
0
        if (!pbuf(dst, blob->image().data(), blob->length())) {
692
0
            return false;
693
0
        }
694
0
    }
695
0
    return true;
696
0
}
697
} // namespace
698
699
bool
700
KeyStore::write_kbx(pgp_dest_t &dst)
701
0
{
702
0
    try {
703
0
        if (!kbx_write_header(*this, dst)) {
704
0
            RNP_LOG("Can't write KBX header");
705
0
            return false;
706
0
        }
707
708
0
        for (auto &key : keys) {
709
0
            if (!key.is_primary()) {
710
0
                continue;
711
0
            }
712
0
            if (!kbx_write_pgp(*this, key, dst)) {
713
0
                RNP_LOG("Can't write PGP blobs for key %p", &key);
714
0
                return false;
715
0
            }
716
0
        }
717
718
0
        if (!kbx_write_x509(*this, dst)) {
719
0
            RNP_LOG("Can't write X509 blobs");
720
0
            return false;
721
0
        }
722
0
        return true;
723
0
    } catch (const std::exception &e) {
724
        /* LCOV_EXCL_START */
725
0
        RNP_LOG("Failed to write KBX store: %s", e.what());
726
0
        return false;
727
        /* LCOV_EXCL_END */
728
0
    }
729
0
}
730
} // namespace rnp