summaryrefslogtreecommitdiffstats
path: root/src/lib/generate-key.cpp
blob: dfd5556dd68fa191f9b1316da0e89c315d670d15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
/*
 * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>

#include <rekey/rnp_key_store.h>
#include <librekey/key_store_pgp.h>
#include <librekey/key_store_g10.h>
#include <librepgp/stream-packet.h>
#include "crypto.h"
#include "pgp-key.h"
#include "defaults.h"
#include "utils.h"

static const uint8_t DEFAULT_SYMMETRIC_ALGS[] = {
  PGP_SA_AES_256, PGP_SA_AES_192, PGP_SA_AES_128};
static const uint8_t DEFAULT_HASH_ALGS[] = {
  PGP_HASH_SHA256, PGP_HASH_SHA384, PGP_HASH_SHA512, PGP_HASH_SHA224};
static const uint8_t DEFAULT_COMPRESS_ALGS[] = {
  PGP_C_ZLIB, PGP_C_BZIP2, PGP_C_ZIP, PGP_C_NONE};

static const id_str_pair pubkey_alg_map[] = {
  {PGP_PKA_RSA, "RSA (Encrypt or Sign)"},
  {PGP_PKA_RSA_ENCRYPT_ONLY, "RSA Encrypt-Only"},
  {PGP_PKA_RSA_SIGN_ONLY, "RSA Sign-Only"},
  {PGP_PKA_ELGAMAL, "Elgamal (Encrypt-Only)"},
  {PGP_PKA_DSA, "DSA"},
  {PGP_PKA_ECDH, "ECDH"},
  {PGP_PKA_ECDSA, "ECDSA"},
  {PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN, "Reserved (formerly Elgamal Encrypt or Sign"},
  {PGP_PKA_RESERVED_DH, "Reserved for Diffie-Hellman (X9.42)"},
  {PGP_PKA_EDDSA, "EdDSA"},
  {PGP_PKA_SM2, "SM2"},
  {PGP_PKA_PRIVATE00, "Private/Experimental"},
  {PGP_PKA_PRIVATE01, "Private/Experimental"},
  {PGP_PKA_PRIVATE02, "Private/Experimental"},
  {PGP_PKA_PRIVATE03, "Private/Experimental"},
  {PGP_PKA_PRIVATE04, "Private/Experimental"},
  {PGP_PKA_PRIVATE05, "Private/Experimental"},
  {PGP_PKA_PRIVATE06, "Private/Experimental"},
  {PGP_PKA_PRIVATE07, "Private/Experimental"},
  {PGP_PKA_PRIVATE08, "Private/Experimental"},
  {PGP_PKA_PRIVATE09, "Private/Experimental"},
  {PGP_PKA_PRIVATE10, "Private/Experimental"},
  {0, NULL}};

static bool
load_generated_g10_key(pgp_key_t *           dst,
                       pgp_key_pkt_t *       newkey,
                       pgp_key_t *           primary_key,
                       pgp_key_t *           pubkey,
                       rnp::SecurityContext &ctx)
{
    // this should generally be zeroed
    assert(dst->type() == 0);
    // if a primary is provided, make sure it's actually a primary key
    assert(!primary_key || primary_key->is_primary());
    // if a pubkey is provided, make sure it's actually a public key
    assert(!pubkey || pubkey->is_public());
    // G10 always needs pubkey here
    assert(pubkey);

    // this would be better on the stack but the key store does not allow it
    std::unique_ptr<rnp_key_store_t> key_store(new (std::nothrow) rnp_key_store_t(ctx));
    if (!key_store) {
        return false;
    }
    /* Write g10 seckey */
    rnp::MemoryDest memdst(NULL, 0);
    if (!g10_write_seckey(&memdst.dst(), newkey, NULL, ctx)) {
        RNP_LOG("failed to write generated seckey");
        return false;
    }

    std::vector<pgp_key_t *> key_ptrs; /* holds primary and pubkey, when used */
    // if this is a subkey, add the primary in first
    if (primary_key) {
        key_ptrs.push_back(primary_key);
    }
    // G10 needs the pubkey for copying some attributes (key version, creation time, etc)
    key_ptrs.push_back(pubkey);

    rnp::MemorySource  memsrc(memdst.memory(), memdst.writeb(), false);
    pgp_key_provider_t prov(rnp_key_provider_key_ptr_list, &key_ptrs);
    if (!rnp_key_store_g10_from_src(key_store.get(), &memsrc.src(), &prov)) {
        return false;
    }
    if (rnp_key_store_get_key_count(key_store.get()) != 1) {
        return false;
    }
    // if a primary key is provided, it should match the sub with regards to type
    assert(!primary_key || (primary_key->is_secret() == key_store->keys.front().is_secret()));
    *dst = pgp_key_t(key_store->keys.front());
    return true;
}

static uint8_t
pk_alg_default_flags(pgp_pubkey_alg_t alg)
{
    // just use the full capabilities as the ultimate fallback
    return pgp_pk_alg_capabilities(alg);
}

// TODO: Similar as pgp_pick_hash_alg but different enough to
//       keep another version. This will be changed when refactoring crypto
static void
adjust_hash_alg(rnp_keygen_crypto_params_t &crypto)
{
    if (!crypto.hash_alg) {
        crypto.hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0];
    }

    if ((crypto.key_alg != PGP_PKA_DSA) && (crypto.key_alg != PGP_PKA_ECDSA)) {
        return;
    }

    pgp_hash_alg_t min_hash = (crypto.key_alg == PGP_PKA_ECDSA) ?
                                ecdsa_get_min_hash(crypto.ecc.curve) :
                                dsa_get_min_hash(crypto.dsa.q_bitlen);

    if (rnp::Hash::size(crypto.hash_alg) < rnp::Hash::size(min_hash)) {
        crypto.hash_alg = min_hash;
    }
}

static void
keygen_merge_crypto_defaults(rnp_keygen_crypto_params_t &crypto)
{
    // default to RSA
    if (!crypto.key_alg) {
        crypto.key_alg = PGP_PKA_RSA;
    }

    switch (crypto.key_alg) {
    case PGP_PKA_RSA:
        if (!crypto.rsa.modulus_bit_len) {
            crypto.rsa.modulus_bit_len = DEFAULT_RSA_NUMBITS;
        }
        break;

    case PGP_PKA_SM2:
        if (!crypto.hash_alg) {
            crypto.hash_alg = PGP_HASH_SM3;
        }
        if (!crypto.ecc.curve) {
            crypto.ecc.curve = PGP_CURVE_SM2_P_256;
        }
        break;

    case PGP_PKA_ECDH:
    case PGP_PKA_ECDSA: {
        if (!crypto.hash_alg) {
            crypto.hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0];
        }
        break;
    }

    case PGP_PKA_EDDSA:
        if (!crypto.ecc.curve) {
            crypto.ecc.curve = PGP_CURVE_ED25519;
        }
        break;

    case PGP_PKA_DSA: {
        if (!crypto.dsa.p_bitlen) {
            crypto.dsa.p_bitlen = DSA_DEFAULT_P_BITLEN;
        }
        if (!crypto.dsa.q_bitlen) {
            crypto.dsa.q_bitlen = dsa_choose_qsize_by_psize(crypto.dsa.p_bitlen);
        }
        break;
    }
    default:
        break;
    }

    adjust_hash_alg(crypto);
}

static bool
validate_keygen_primary(const rnp_keygen_primary_desc_t &desc)
{
    /* Confirm that the specified pk alg can certify.
     * gpg requires this, though the RFC only says that a V4 primary
     * key SHOULD be a key capable of certification.
     */
    if (!(pgp_pk_alg_capabilities(desc.crypto.key_alg) & PGP_KF_CERTIFY)) {
        RNP_LOG("primary key alg (%d) must be able to sign", desc.crypto.key_alg);
        return false;
    }

    // check key flags
    if (!desc.cert.key_flags) {
        // these are probably not *technically* required
        RNP_LOG("key flags are required");
        return false;
    } else if (desc.cert.key_flags & ~pgp_pk_alg_capabilities(desc.crypto.key_alg)) {
        // check the flags against the alg capabilities
        RNP_LOG("usage not permitted for pk algorithm");
        return false;
    }
    // require a userid
    if (!desc.cert.userid[0]) {
        RNP_LOG("userid is required for primary key");
        return false;
    }
    return true;
}

static uint32_t
get_numbits(const rnp_keygen_crypto_params_t *crypto)
{
    switch (crypto->key_alg) {
    case PGP_PKA_RSA:
    case PGP_PKA_RSA_ENCRYPT_ONLY:
    case PGP_PKA_RSA_SIGN_ONLY:
        return crypto->rsa.modulus_bit_len;
    case PGP_PKA_ECDSA:
    case PGP_PKA_ECDH:
    case PGP_PKA_EDDSA:
    case PGP_PKA_SM2: {
        if (const ec_curve_desc_t *curve = get_curve_desc(crypto->ecc.curve)) {
            return curve->bitlen;
        } else {
            return 0;
        }
    }
    case PGP_PKA_DSA:
        return crypto->dsa.p_bitlen;
    case PGP_PKA_ELGAMAL:
    case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
        return crypto->elgamal.key_bitlen;
    default:
        return 0;
    }
}

static void
set_default_user_prefs(pgp_user_prefs_t &prefs)
{
    if (prefs.symm_algs.empty()) {
        prefs.set_symm_algs(
          std::vector<uint8_t>(DEFAULT_SYMMETRIC_ALGS,
                               DEFAULT_SYMMETRIC_ALGS + ARRAY_SIZE(DEFAULT_SYMMETRIC_ALGS)));
    }
    if (prefs.hash_algs.empty()) {
        prefs.set_hash_algs(std::vector<uint8_t>(
          DEFAULT_HASH_ALGS, DEFAULT_HASH_ALGS + ARRAY_SIZE(DEFAULT_HASH_ALGS)));
    }
    if (prefs.z_algs.empty()) {
        prefs.set_z_algs(std::vector<uint8_t>(
          DEFAULT_COMPRESS_ALGS, DEFAULT_COMPRESS_ALGS + ARRAY_SIZE(DEFAULT_COMPRESS_ALGS)));
    }
}

static void
keygen_primary_merge_defaults(rnp_keygen_primary_desc_t &desc)
{
    keygen_merge_crypto_defaults(desc.crypto);
    set_default_user_prefs(desc.cert.prefs);

    if (!desc.cert.key_flags) {
        // set some default key flags if none are provided
        desc.cert.key_flags = pk_alg_default_flags(desc.crypto.key_alg);
    }
    if (desc.cert.userid.empty()) {
        char uid[MAX_ID_LENGTH] = {0};
        snprintf(uid,
                 sizeof(uid),
                 "%s %d-bit key <%s@localhost>",
                 id_str_pair::lookup(pubkey_alg_map, desc.crypto.key_alg),
                 get_numbits(&desc.crypto),
                 getenv_logname());
        desc.cert.userid = uid;
    }
}

bool
pgp_generate_primary_key(rnp_keygen_primary_desc_t &desc,
                         bool                       merge_defaults,
                         pgp_key_t &                primary_sec,
                         pgp_key_t &                primary_pub,
                         pgp_key_store_format_t     secformat)
{
    // validate args
    if (primary_sec.type() || primary_pub.type()) {
        RNP_LOG("invalid parameters (should be zeroed)");
        return false;
    }

    try {
        // merge some defaults in, if requested
        if (merge_defaults) {
            keygen_primary_merge_defaults(desc);
        }
        // now validate the keygen fields
        if (!validate_keygen_primary(desc)) {
            return false;
        }

        // generate the raw key and fill tag/secret fields
        pgp_key_pkt_t secpkt;
        if (!pgp_generate_seckey(desc.crypto, secpkt, true)) {
            return false;
        }

        pgp_key_t sec(secpkt);
        pgp_key_t pub(secpkt, true);
        sec.add_uid_cert(desc.cert, desc.crypto.hash_alg, *desc.crypto.ctx, &pub);

        switch (secformat) {
        case PGP_KEY_STORE_GPG:
        case PGP_KEY_STORE_KBX:
            primary_sec = std::move(sec);
            primary_pub = std::move(pub);
            break;
        case PGP_KEY_STORE_G10:
            primary_pub = std::move(pub);
            if (!load_generated_g10_key(
                  &primary_sec, &secpkt, NULL, &primary_pub, *desc.crypto.ctx)) {
                RNP_LOG("failed to load generated key");
                return false;
            }
            break;
        default:
            RNP_LOG("invalid format");
            return false;
        }
    } catch (const std::exception &e) {
        RNP_LOG("Failure: %s", e.what());
        return false;
    }

    /* mark it as valid */
    primary_pub.mark_valid();
    primary_sec.mark_valid();
    /* refresh key's data */
    return primary_pub.refresh_data(*desc.crypto.ctx) &&
           primary_sec.refresh_data(*desc.crypto.ctx);
}

static bool
validate_keygen_subkey(rnp_keygen_subkey_desc_t &desc)
{
    if (!desc.binding.key_flags) {
        RNP_LOG("key flags are required");
        return false;
    } else if (desc.binding.key_flags & ~pgp_pk_alg_capabilities(desc.crypto.key_alg)) {
        // check the flags against the alg capabilities
        RNP_LOG("usage not permitted for pk algorithm");
        return false;
    }
    return true;
}

static void
keygen_subkey_merge_defaults(rnp_keygen_subkey_desc_t &desc)
{
    keygen_merge_crypto_defaults(desc.crypto);
    if (!desc.binding.key_flags) {
        // set some default key flags if none are provided
        desc.binding.key_flags = pk_alg_default_flags(desc.crypto.key_alg);
    }
}

bool
pgp_generate_subkey(rnp_keygen_subkey_desc_t &     desc,
                    bool                           merge_defaults,
                    pgp_key_t &                    primary_sec,
                    pgp_key_t &                    primary_pub,
                    pgp_key_t &                    subkey_sec,
                    pgp_key_t &                    subkey_pub,
                    const pgp_password_provider_t &password_provider,
                    pgp_key_store_format_t         secformat)
{
    // validate args
    if (!primary_sec.is_primary() || !primary_pub.is_primary() || !primary_sec.is_secret() ||
        !primary_pub.is_public()) {
        RNP_LOG("invalid parameters");
        return false;
    }
    if (subkey_sec.type() || subkey_pub.type()) {
        RNP_LOG("invalid parameters (should be zeroed)");
        return false;
    }

    // merge some defaults in, if requested
    if (merge_defaults) {
        keygen_subkey_merge_defaults(desc);
    }

    // now validate the keygen fields
    if (!validate_keygen_subkey(desc)) {
        return false;
    }

    try {
        /* decrypt the primary seckey if needed (for signatures) */
        rnp::KeyLocker primlock(primary_sec);
        if (primary_sec.encrypted() &&
            !primary_sec.unlock(password_provider, PGP_OP_ADD_SUBKEY)) {
            RNP_LOG("Failed to unlock primary key.");
            return false;
        }
        /* generate the raw subkey */
        pgp_key_pkt_t secpkt;
        if (!pgp_generate_seckey(desc.crypto, secpkt, false)) {
            return false;
        }
        pgp_key_pkt_t pubpkt = pgp_key_pkt_t(secpkt, true);
        pgp_key_t     sec(secpkt, primary_sec);
        pgp_key_t     pub(pubpkt, primary_pub);
        /* add binding */
        primary_sec.add_sub_binding(
          sec, pub, desc.binding, desc.crypto.hash_alg, *desc.crypto.ctx);
        /* copy to the result */
        subkey_pub = std::move(pub);
        switch (secformat) {
        case PGP_KEY_STORE_GPG:
        case PGP_KEY_STORE_KBX:
            subkey_sec = std::move(sec);
            break;
        case PGP_KEY_STORE_G10:
            if (!load_generated_g10_key(
                  &subkey_sec, &secpkt, &primary_sec, &subkey_pub, *desc.crypto.ctx)) {
                RNP_LOG("failed to load generated key");
                return false;
            }
            break;
        default:
            RNP_LOG("invalid format");
            return false;
        }

        subkey_pub.mark_valid();
        subkey_sec.mark_valid();
        return subkey_pub.refresh_data(&primary_pub, *desc.crypto.ctx) &&
               subkey_sec.refresh_data(&primary_sec, *desc.crypto.ctx);
    } catch (const std::exception &e) {
        RNP_LOG("Subkey generation failed: %s", e.what());
        return false;
    }
}