Coverage Report

Created: 2025-08-29 06:22

/src/rnp/src/lib/keygen.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2024 [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 "keygen.hpp"
28
#include <cassert>
29
#include <algorithm>
30
#include "librekey/key_store_g10.h"
31
32
namespace rnp {
33
KeygenParams::KeygenParams(pgp_pubkey_alg_t alg, SecurityContext &ctx)
34
0
    : alg_(alg), hash_(PGP_HASH_UNKNOWN), version_(PGP_V4), ctx_(ctx)
35
0
{
36
0
    key_params_ = pgp::KeyParams::create(alg);
37
0
}
38
39
void
40
KeygenParams::check_defaults() noexcept
41
0
{
42
0
    if (hash_ == PGP_HASH_UNKNOWN) {
43
0
        hash_ = alg_ == PGP_PKA_SM2 ? PGP_HASH_SM3 : DEFAULT_PGP_HASH_ALG;
44
0
    }
45
0
    pgp_hash_alg_t min_hash = key_params_->min_hash();
46
0
    if (Hash::size(hash_) < Hash::size(min_hash)) {
47
0
        hash_ = min_hash;
48
0
    }
49
0
    key_params_->check_defaults();
50
0
}
51
52
bool
53
KeygenParams::validate() const noexcept
54
0
{
55
0
#if defined(ENABLE_PQC)
56
0
    switch (alg()) {
57
0
    case PGP_PKA_SPHINCSPLUS_SHA2:
58
0
        FALLTHROUGH_STATEMENT;
59
0
    case PGP_PKA_SPHINCSPLUS_SHAKE: {
60
0
        auto &slhdsa = dynamic_cast<const pgp::SlhdsaKeyParams &>(key_params());
61
0
        if (!sphincsplus_hash_allowed(alg(), slhdsa.param(), hash())) {
62
0
            RNP_LOG("invalid hash algorithm for the slhdsa key");
63
0
            return false;
64
0
        }
65
0
        break;
66
0
    }
67
0
    case PGP_PKA_DILITHIUM3_ED25519:
68
0
        FALLTHROUGH_STATEMENT;
69
    // TODO: Add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT;
70
0
    case PGP_PKA_DILITHIUM3_P256:
71
0
        FALLTHROUGH_STATEMENT;
72
0
    case PGP_PKA_DILITHIUM5_P384:
73
0
        FALLTHROUGH_STATEMENT;
74
0
    case PGP_PKA_DILITHIUM3_BP256:
75
0
        FALLTHROUGH_STATEMENT;
76
0
    case PGP_PKA_DILITHIUM5_BP384:
77
0
        if (!dilithium_hash_allowed(hash())) {
78
0
            RNP_LOG("invalid hash algorithm for the dilithium key");
79
0
            return false;
80
0
        }
81
0
        break;
82
0
    default:
83
0
        break;
84
0
    }
85
0
#endif
86
0
    return true;
87
0
}
88
89
bool
90
KeygenParams::validate(const CertParams &cert) const noexcept
91
0
{
92
    /* Confirm that the specified pk alg can certify.
93
     * gpg requires this, though the RFC only says that a V4 primary
94
     * key SHOULD be a key capable of certification.
95
     */
96
0
    if (!(pgp_pk_alg_capabilities(alg()) & PGP_KF_CERTIFY)) {
97
0
        RNP_LOG("primary key alg (%d) must be able to sign", alg());
98
0
        return false;
99
0
    }
100
101
    // check key flags
102
0
    if (!cert.flags) {
103
        // these are probably not *technically* required
104
0
        RNP_LOG("key flags are required");
105
0
        return false;
106
0
    }
107
0
    if (cert.flags & ~pgp_pk_alg_capabilities(alg())) {
108
        // check the flags against the alg capabilities
109
0
        RNP_LOG("usage not permitted for pk algorithm");
110
0
        return false;
111
0
    }
112
    // require a userid
113
0
    if (cert.userid.empty()) {
114
0
        RNP_LOG("userid is required for primary key");
115
0
        return false;
116
0
    }
117
0
    return validate();
118
0
}
119
120
bool
121
KeygenParams::validate(const BindingParams &binding) const noexcept
122
0
{
123
0
    if (!binding.flags) {
124
0
        RNP_LOG("key flags are required");
125
0
        return false;
126
0
    }
127
0
    if (binding.flags & ~pgp_pk_alg_capabilities(alg())) {
128
        // check the flags against the alg capabilities
129
0
        RNP_LOG("usage not permitted for pk algorithm");
130
0
        return false;
131
0
    }
132
0
    return validate();
133
0
}
134
135
static const id_str_pair pubkey_alg_map[] = {{PGP_PKA_RSA, "RSA (Encrypt or Sign)"},
136
                                             {PGP_PKA_RSA_ENCRYPT_ONLY, "RSA Encrypt-Only"},
137
                                             {PGP_PKA_RSA_SIGN_ONLY, "RSA Sign-Only"},
138
                                             {PGP_PKA_ELGAMAL, "Elgamal (Encrypt-Only)"},
139
                                             {PGP_PKA_DSA, "DSA"},
140
                                             {PGP_PKA_ECDH, "ECDH"},
141
                                             {PGP_PKA_ECDSA, "ECDSA"},
142
                                             {PGP_PKA_EDDSA, "EdDSA"},
143
                                             {PGP_PKA_SM2, "SM2"},
144
#if defined(ENABLE_CRYPTO_REFRESH)
145
                                             {PGP_PKA_ED25519, "ED25519"},
146
                                             {PGP_PKA_X25519, "X25519"},
147
#endif
148
#if defined(ENABLE_PQC)
149
                                             {PGP_PKA_KYBER768_X25519, "ML-KEM-768_X25519"},
150
                                             //{PGP_PKA_KYBER1024_X448, "Kyber-X448"},
151
                                             {PGP_PKA_KYBER768_P256, "ML-KEM-768_P256"},
152
                                             {PGP_PKA_KYBER1024_P384, "ML-KEM-1024_P384"},
153
                                             {PGP_PKA_KYBER768_BP256, "ML-KEM-768_BP256"},
154
                                             {PGP_PKA_KYBER1024_BP384, "ML-KEM-1024_BP384"},
155
                                             {PGP_PKA_DILITHIUM3_ED25519, "ML-DSA-65_ED25519"},
156
                                             //{PGP_PKA_DILITHIUM5_ED448, "Dilithium-ED448"},
157
                                             {PGP_PKA_DILITHIUM3_P256, "ML-DSA-65_P256"},
158
                                             {PGP_PKA_DILITHIUM5_P384, "ML-DSA-87_P384"},
159
                                             {PGP_PKA_DILITHIUM3_BP256, "ML-DSA-65_BP256"},
160
                                             {PGP_PKA_DILITHIUM5_BP384, "ML-DSA-87_BP384"},
161
                                             {PGP_PKA_SPHINCSPLUS_SHA2, "SLH-DSA-SHA2"},
162
                                             {PGP_PKA_SPHINCSPLUS_SHAKE, "SLH-DSA-SHAKE"},
163
#endif
164
                                             {0, NULL}};
165
166
bool
167
KeygenParams::generate(pgp_key_pkt_t &seckey, bool primary)
168
0
{
169
    /* populate pgp key structure */
170
0
    seckey = {};
171
0
    seckey.version = version();
172
0
    seckey.creation_time = ctx().time();
173
0
    seckey.alg = alg();
174
0
    seckey.material = pgp::KeyMaterial::create(alg());
175
0
    if (!seckey.material) {
176
0
        RNP_LOG("Unsupported key algorithm: %d", alg());
177
0
        return false;
178
0
    }
179
0
    seckey.tag = primary ? PGP_PKT_SECRET_KEY : PGP_PKT_SECRET_SUBKEY;
180
181
0
    if (!seckey.material->generate(ctx(), key_params())) {
182
0
        return false;
183
0
    }
184
185
0
    seckey.sec_protection.s2k.usage = PGP_S2KU_NONE;
186
    /* fill the sec_data/sec_len */
187
0
    if (encrypt_secret_key(&seckey, NULL, ctx().rng)) {
188
0
        RNP_LOG("failed to fill sec_data");
189
0
        return false;
190
0
    }
191
0
    return true;
192
0
}
193
194
static bool
195
load_generated_g10_key(
196
  Key *dst, pgp_key_pkt_t *newkey, Key *primary_key, Key *pubkey, SecurityContext &ctx)
197
0
{
198
    // this should generally be zeroed
199
0
    assert(dst->type() == 0);
200
    // if a primary is provided, make sure it's actually a primary key
201
0
    assert(!primary_key || primary_key->is_primary());
202
    // if a pubkey is provided, make sure it's actually a public key
203
0
    assert(!pubkey || pubkey->is_public());
204
    // G10 always needs pubkey here
205
0
    assert(pubkey);
206
207
    // this would be better on the stack but the key store does not allow it
208
0
    std::unique_ptr<KeyStore> key_store(new (std::nothrow) KeyStore(ctx));
209
0
    if (!key_store) {
210
0
        return false;
211
0
    }
212
    /* Write g10 seckey */
213
0
    MemoryDest memdst(NULL, 0);
214
0
    if (!g10_write_seckey(&memdst.dst(), newkey, NULL, ctx)) {
215
0
        RNP_LOG("failed to write generated seckey");
216
0
        return false;
217
0
    }
218
219
0
    std::vector<Key *> key_ptrs; /* holds primary and pubkey, when used */
220
    // if this is a subkey, add the primary in first
221
0
    if (primary_key) {
222
0
        key_ptrs.push_back(primary_key);
223
0
    }
224
    // G10 needs the pubkey for copying some attributes (key version, creation time, etc)
225
0
    key_ptrs.push_back(pubkey);
226
227
0
    MemorySource memsrc(memdst.memory(), memdst.writeb(), false);
228
0
    KeyProvider  prov(rnp_key_provider_key_ptr_list, &key_ptrs);
229
0
    if (!key_store.get()->load_g10(memsrc.src(), &prov)) {
230
0
        return false;
231
0
    }
232
0
    if (key_store.get()->key_count() != 1) {
233
0
        return false;
234
0
    }
235
    // if a primary key is provided, it should match the sub with regards to type
236
0
    assert(!primary_key || (primary_key->is_secret() == key_store->keys.front().is_secret()));
237
0
    *dst = Key(key_store->keys.front());
238
0
    return true;
239
0
}
240
241
bool
242
KeygenParams::generate(CertParams &cert,
243
                       Key &       primary_sec,
244
                       Key &       primary_pub,
245
                       KeyFormat   secformat)
246
0
{
247
0
    primary_sec = {};
248
0
    primary_pub = {};
249
250
    // merge some defaults in
251
0
    check_defaults();
252
0
    cert.check_defaults(*this);
253
    // now validate the keygen fields
254
0
    if (!validate(cert)) {
255
0
        return false;
256
0
    }
257
258
    // generate the raw key and fill tag/secret fields
259
0
    pgp_key_pkt_t secpkt;
260
0
    if (!generate(secpkt, true)) {
261
0
        return false;
262
0
    }
263
264
0
    Key sec(secpkt);
265
0
    Key pub(secpkt, true);
266
0
#if defined(ENABLE_CRYPTO_REFRESH)
267
    // for v6 packets, a direct-key sig is mandatory.
268
0
    if (sec.version() == PGP_V6) {
269
0
        sec.add_direct_sig(cert, hash(), ctx(), &pub);
270
0
    }
271
0
#endif
272
0
    sec.add_uid_cert(cert, hash(), ctx(), &pub);
273
274
0
    switch (secformat) {
275
0
    case KeyFormat::GPG:
276
0
    case KeyFormat::KBX:
277
0
        primary_sec = std::move(sec);
278
0
        primary_pub = std::move(pub);
279
0
        break;
280
0
    case KeyFormat::G10:
281
0
        primary_pub = std::move(pub);
282
0
        if (!load_generated_g10_key(&primary_sec, &secpkt, NULL, &primary_pub, ctx())) {
283
0
            RNP_LOG("failed to load generated key");
284
0
            return false;
285
0
        }
286
0
        break;
287
0
    default:
288
0
        RNP_LOG("invalid format");
289
0
        return false;
290
0
    }
291
292
    /* mark it as valid */
293
0
    primary_pub.mark_valid();
294
0
    primary_sec.mark_valid();
295
    /* refresh key's data */
296
0
    return primary_pub.refresh_data(ctx()) && primary_sec.refresh_data(ctx());
297
0
}
298
299
bool
300
KeygenParams::generate(BindingParams &                binding,
301
                       Key &                          primary_sec,
302
                       Key &                          primary_pub,
303
                       Key &                          subkey_sec,
304
                       Key &                          subkey_pub,
305
                       const pgp_password_provider_t &password_provider,
306
                       KeyFormat                      secformat)
307
0
{
308
    // validate args
309
0
    if (!primary_sec.is_primary() || !primary_pub.is_primary() || !primary_sec.is_secret() ||
310
0
        !primary_pub.is_public()) {
311
0
        RNP_LOG("invalid parameters");
312
0
        return false;
313
0
    }
314
0
    subkey_sec = {};
315
0
    subkey_pub = {};
316
317
    // merge some defaults in
318
0
    check_defaults();
319
0
    binding.check_defaults(*this);
320
321
    // now validate the keygen fields
322
0
    if (!validate(binding)) {
323
0
        return false;
324
0
    }
325
326
    /* decrypt the primary seckey if needed (for signatures) */
327
0
    KeyLocker primlock(primary_sec);
328
0
    if (primary_sec.encrypted() && !primary_sec.unlock(password_provider, PGP_OP_ADD_SUBKEY)) {
329
0
        RNP_LOG("Failed to unlock primary key.");
330
0
        return false;
331
0
    }
332
    /* generate the raw subkey */
333
0
    pgp_key_pkt_t secpkt;
334
0
    if (!generate(secpkt, false)) {
335
0
        return false;
336
0
    }
337
0
    pgp_key_pkt_t pubpkt = pgp_key_pkt_t(secpkt, true);
338
0
    Key           sec(secpkt, primary_sec);
339
0
    Key           pub(pubpkt, primary_pub);
340
    /* add binding */
341
0
    primary_sec.add_sub_binding(sec, pub, binding, hash(), ctx());
342
    /* copy to the result */
343
0
    subkey_pub = std::move(pub);
344
0
    switch (secformat) {
345
0
    case KeyFormat::GPG:
346
0
    case KeyFormat::KBX:
347
0
        subkey_sec = std::move(sec);
348
0
        break;
349
0
    case KeyFormat::G10:
350
0
        if (!load_generated_g10_key(&subkey_sec, &secpkt, &primary_sec, &subkey_pub, ctx())) {
351
0
            RNP_LOG("failed to load generated key");
352
0
            return false;
353
0
        }
354
0
        break;
355
0
    default:
356
0
        RNP_LOG("invalid format");
357
0
        return false;
358
0
    }
359
360
0
    subkey_pub.mark_valid();
361
0
    subkey_sec.mark_valid();
362
0
    return subkey_pub.refresh_data(&primary_pub, ctx()) &&
363
0
           subkey_sec.refresh_data(&primary_sec, ctx());
364
0
}
365
366
UserPrefs::UserPrefs(const pgp::pkt::Signature &sig)
367
0
{
368
0
    symm_algs = sig.preferred_symm_algs();
369
0
    hash_algs = sig.preferred_hash_algs();
370
0
    z_algs = sig.preferred_z_algs();
371
372
0
    if (sig.has_subpkt(PGP_SIG_SUBPKT_KEYSERV_PREFS)) {
373
0
        ks_prefs = {sig.key_server_prefs()};
374
0
    }
375
376
0
    if (sig.has_subpkt(PGP_SIG_SUBPKT_PREF_KEYSERV)) {
377
0
        key_server = sig.key_server();
378
0
    }
379
0
}
380
381
void
382
UserPrefs::add_uniq(std::vector<uint8_t> &vec, uint8_t val)
383
0
{
384
0
    if (std::find(vec.begin(), vec.end(), val) == vec.end()) {
385
0
        vec.push_back(val);
386
0
    }
387
0
}
388
389
void
390
UserPrefs::add_symm_alg(pgp_symm_alg_t alg)
391
0
{
392
0
    add_uniq(symm_algs, alg);
393
0
}
394
395
void
396
UserPrefs::add_hash_alg(pgp_hash_alg_t alg)
397
0
{
398
0
    add_uniq(hash_algs, alg);
399
0
}
400
401
void
402
UserPrefs::add_z_alg(pgp_compression_type_t alg)
403
0
{
404
0
    add_uniq(z_algs, alg);
405
0
}
406
407
void
408
UserPrefs::add_ks_pref(pgp_key_server_prefs_t pref)
409
0
{
410
0
    add_uniq(ks_prefs, pref);
411
0
}
412
413
#if defined(ENABLE_CRYPTO_REFRESH)
414
void
415
UserPrefs::add_aead_prefs(pgp_symm_alg_t sym_alg, pgp_aead_alg_t aead_alg)
416
0
{
417
0
    for (size_t i = 0; i < aead_prefs.size(); i += 2) {
418
0
        if (aead_prefs[i] == sym_alg && aead_prefs[i + 1] == aead_alg) {
419
0
            return;
420
0
        }
421
0
    }
422
0
    aead_prefs.push_back(sym_alg);
423
0
    aead_prefs.push_back(aead_alg);
424
0
}
425
#endif
426
427
void
428
UserPrefs::check_defaults(pgp_version_t version)
429
0
{
430
0
    if (symm_algs.empty()) {
431
0
        symm_algs = {PGP_SA_AES_256, PGP_SA_AES_192, PGP_SA_AES_128};
432
0
    }
433
0
    if (hash_algs.empty()) {
434
0
        hash_algs = {PGP_HASH_SHA256, PGP_HASH_SHA384, PGP_HASH_SHA512, PGP_HASH_SHA224};
435
0
    }
436
0
    if (z_algs.empty()) {
437
0
        z_algs = {PGP_C_ZLIB, PGP_C_BZIP2, PGP_C_ZIP, PGP_C_NONE};
438
0
    }
439
0
#if defined(ENABLE_CRYPTO_REFRESH)
440
0
    if (aead_prefs.empty() && (version == PGP_V6)) {
441
0
        for (auto sym_alg : symm_algs) {
442
0
            aead_prefs.push_back(sym_alg);
443
0
            aead_prefs.push_back(PGP_AEAD_OCB);
444
0
        }
445
0
    }
446
0
#endif
447
0
}
448
449
void
450
CertParams::check_defaults(const KeygenParams &params)
451
0
{
452
0
    prefs.check_defaults(params.version());
453
454
0
    if (!flags) {
455
        // set some default key flags if none are provided
456
0
        flags = pgp_pk_alg_capabilities(params.alg());
457
0
    }
458
0
    if (userid.empty()) {
459
0
        std::string alg = id_str_pair::lookup(pubkey_alg_map, params.alg());
460
0
        std::string bits = std::to_string(params.key_params().bits());
461
0
        std::string name = getenv_logname() ? getenv_logname() : "";
462
        /* Awkward but better then sprintf */
463
0
        userid = alg + " " + bits + "-bit key <" + name + "@localhost>";
464
0
    }
465
0
}
466
467
void
468
CertParams::populate(pgp_userid_pkt_t &uid) const
469
0
{
470
0
    uid.tag = PGP_PKT_USER_ID;
471
0
    uid.uid.assign(userid.data(), userid.data() + userid.size());
472
0
}
473
474
void
475
CertParams::populate(pgp::pkt::Signature &sig) const
476
0
{
477
0
    if (key_expiration) {
478
0
        sig.set_key_expiration(key_expiration);
479
0
    }
480
0
    if (primary) {
481
0
        sig.set_primary_uid(true);
482
0
    }
483
0
#if defined(ENABLE_CRYPTO_REFRESH)
484
0
    if ((sig.version == PGP_V6) && (sig.type() != PGP_SIG_DIRECT)) {
485
        /* only set key expiraton and primary uid for v6 self-signatures
486
         * since most information is stored in the direct-key signature of the primary key.
487
         */
488
489
0
        if (flags && (sig.type() == PGP_SIG_SUBKEY)) {
490
            /* for v6 subkeys signatures we also add the key flags */
491
0
            sig.set_key_flags(flags);
492
0
        }
493
0
        return;
494
0
    } else if ((sig.version == PGP_V6) && (sig.type() == PGP_SIG_DIRECT)) {
495
        /* set some additional packets for v6 direct-key self signatures */
496
0
        sig.set_key_features(PGP_KEY_FEATURE_MDC | PGP_KEY_FEATURE_SEIPDV2);
497
0
        if (!prefs.aead_prefs.empty()) {
498
0
            sig.set_preferred_aead_algs(prefs.aead_prefs);
499
0
        }
500
0
    }
501
0
#endif
502
0
    if (flags) {
503
0
        sig.set_key_flags(flags);
504
0
    }
505
0
    if (!prefs.symm_algs.empty()) {
506
0
        sig.set_preferred_symm_algs(prefs.symm_algs);
507
0
    }
508
0
    if (!prefs.hash_algs.empty()) {
509
0
        sig.set_preferred_hash_algs(prefs.hash_algs);
510
0
    }
511
0
    if (!prefs.z_algs.empty()) {
512
0
        sig.set_preferred_z_algs(prefs.z_algs);
513
0
    }
514
0
    if (!prefs.ks_prefs.empty()) {
515
0
        sig.set_key_server_prefs(prefs.ks_prefs[0]);
516
0
    }
517
0
    if (!prefs.key_server.empty()) {
518
0
        sig.set_key_server(prefs.key_server);
519
0
    }
520
0
}
521
522
void
523
CertParams::populate(pgp_userid_pkt_t &uid, pgp::pkt::Signature &sig) const
524
0
{
525
0
    sig.set_type(PGP_CERT_POSITIVE);
526
0
    populate(sig);
527
0
    populate(uid);
528
0
}
529
530
void
531
BindingParams::check_defaults(const KeygenParams &params)
532
0
{
533
0
    if (!flags) {
534
        // set some default key flags if none are provided
535
0
        flags = pgp_pk_alg_capabilities(params.alg());
536
0
    }
537
0
}
538
539
} // namespace rnp