Coverage Report

Created: 2026-01-15 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/pki_container_openssh.c
Line
Count
Source
1
/*
2
 * pki_container_openssh.c
3
 * This file is part of the SSH Library
4
 *
5
 * Copyright (c) 2013,2014 Aris Adamantiadis <aris@badcode.be>
6
 *
7
 * The SSH Library is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as published by
9
 * the Free Software Foundation; either version 2.1 of the License, or (at your
10
 * option) any later version.
11
 *
12
 * The SSH Library is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15
 * License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with the SSH Library; see the file COPYING.  If not, write to
19
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20
 * MA 02111-1307, USA.
21
 */
22
23
/**
24
 * @ingroup libssh_pki
25
 * *
26
 * @{
27
 */
28
29
#include "config.h"
30
31
#include <ctype.h>
32
#include <string.h>
33
#include <stdbool.h>
34
35
#include "libssh/libssh.h"
36
#include "libssh/priv.h"
37
#include "libssh/pki.h"
38
#include "libssh/pki_priv.h"
39
#include "libssh/buffer.h"
40
41
42
/**
43
 * @internal
44
 *
45
 * @brief Import a private key from a ssh buffer.
46
 *
47
 * @param[in] key_blob_buffer The key blob to import as specified in
48
 *                            key.c:key_private_serialize in OpenSSH source
49
 *                            code.
50
 *
51
 * @param[out] pkey     A pointer where the allocated key can be stored. You
52
 *                      need to free the memory using ssh_key_free().
53
 *
54
 * @return              SSH_OK on success, SSH_ERROR on error.
55
 *
56
 * @see ssh_key_free()
57
 */
58
static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
59
                                           ssh_key *pkey)
60
0
{
61
0
    enum ssh_keytypes_e type;
62
0
    char *type_s = NULL;
63
0
    ssh_key key = NULL;
64
0
    int rc;
65
66
0
    if (pkey == NULL) {
67
0
        return SSH_ERROR;
68
0
    }
69
70
0
    rc = ssh_buffer_unpack(key_blob_buffer, "s", &type_s);
71
0
    if (rc == SSH_ERROR){
72
0
        SSH_LOG(SSH_LOG_TRACE, "Unpack error");
73
0
        return SSH_ERROR;
74
0
    }
75
76
0
    type = ssh_key_type_from_name(type_s);
77
0
    if (type == SSH_KEYTYPE_UNKNOWN) {
78
0
        SSH_LOG(SSH_LOG_TRACE, "Unknown key type '%s' found!", type_s);
79
0
        return SSH_ERROR;
80
0
    }
81
0
    SAFE_FREE(type_s);
82
83
0
    rc = pki_import_privkey_buffer(type, key_blob_buffer, &key);
84
0
    if (rc != SSH_OK) {
85
0
        SSH_LOG(SSH_LOG_TRACE, "Failed to read key in OpenSSH format");
86
0
        goto fail;
87
0
    }
88
89
0
    *pkey = key;
90
0
    return SSH_OK;
91
0
fail:
92
0
    ssh_key_free(key);
93
94
0
    return SSH_ERROR;
95
0
}
96
97
/**
98
 * @brief decrypts an encrypted private key blob in OpenSSH format.
99
 *
100
 */
101
static int pki_private_key_decrypt(ssh_string blob,
102
                                   const char* passphrase,
103
                                   const char *ciphername,
104
                                   const char *kdfname,
105
                                   ssh_string kdfoptions,
106
                                   ssh_auth_callback auth_fn,
107
                                   void *auth_data)
108
0
{
109
0
    struct ssh_cipher_struct *ciphers = ssh_get_ciphertab();
110
0
    struct ssh_cipher_struct cipher;
111
0
    uint8_t key_material[128] = {0};
112
0
    char passphrase_buffer[128] = {0};
113
0
    size_t key_material_len;
114
0
    ssh_buffer buffer = NULL;
115
0
    ssh_string salt = NULL;
116
0
    uint32_t rounds;
117
0
    int cmp;
118
0
    int rc;
119
0
    int i;
120
121
0
    cmp = strcmp(ciphername, "none");
122
0
    if (cmp == 0){
123
        /* no decryption required */
124
0
        return SSH_OK;
125
0
    }
126
127
0
    for (i = 0; ciphers[i].name != NULL; i++) {
128
0
        cmp = strcmp(ciphername, ciphers[i].name);
129
0
        if (cmp == 0){
130
0
            memcpy(&cipher, &ciphers[i], sizeof(cipher));
131
0
            break;
132
0
        }
133
0
    }
134
135
0
    if (ciphers[i].name == NULL){
136
0
        SSH_LOG(SSH_LOG_TRACE, "Unsupported cipher %s", ciphername);
137
0
        return SSH_ERROR;
138
0
    }
139
140
0
    cmp = strcmp(kdfname, "bcrypt");
141
0
    if (cmp != 0) {
142
0
        SSH_LOG(SSH_LOG_TRACE, "Unsupported KDF %s", kdfname);
143
0
        return SSH_ERROR;
144
0
    }
145
0
    if (ssh_string_len(blob) % cipher.blocksize != 0) {
146
0
        SSH_LOG(SSH_LOG_TRACE,
147
0
                "Encrypted string not multiple of blocksize: %zu",
148
0
                ssh_string_len(blob));
149
0
        return SSH_ERROR;
150
0
    }
151
152
0
    buffer = ssh_buffer_new();
153
0
    if (buffer == NULL){
154
0
        return SSH_ERROR;
155
0
    }
156
0
    rc = ssh_buffer_add_data(buffer,
157
0
                             ssh_string_data(kdfoptions),
158
0
                             (uint32_t)ssh_string_len(kdfoptions));
159
0
    if (rc != SSH_ERROR){
160
0
        rc = ssh_buffer_unpack(buffer, "Sd", &salt, &rounds);
161
0
    }
162
0
    SSH_BUFFER_FREE(buffer);
163
0
    if (rc == SSH_ERROR){
164
0
        return SSH_ERROR;
165
0
    }
166
167
    /* We need material for key (keysize bits / 8) and IV (blocksize)  */
168
0
    key_material_len =  cipher.keysize/8 + cipher.blocksize;
169
0
    if (key_material_len > sizeof(key_material)) {
170
0
        SSH_LOG(SSH_LOG_TRACE, "Key material too big");
171
0
        return SSH_ERROR;
172
0
    }
173
174
0
    SSH_LOG(SSH_LOG_DEBUG,
175
0
            "Decryption: %d key, %d IV, %" PRIu32 " rounds, %zu bytes salt",
176
0
            cipher.keysize/8,
177
0
            cipher.blocksize,
178
0
            rounds,
179
0
            ssh_string_len(salt));
180
181
0
    if (passphrase == NULL) {
182
0
        if (auth_fn == NULL) {
183
0
            SAFE_FREE(salt);
184
0
            SSH_LOG(SSH_LOG_TRACE, "No passphrase provided");
185
0
            return SSH_ERROR;
186
0
        }
187
0
        rc = auth_fn("Passphrase",
188
0
                     passphrase_buffer,
189
0
                     sizeof(passphrase_buffer),
190
0
                     0,
191
0
                     0,
192
0
                     auth_data);
193
0
        if (rc != SSH_OK) {
194
0
            SAFE_FREE(salt);
195
0
            return SSH_ERROR;
196
0
        }
197
0
        passphrase = passphrase_buffer;
198
0
    }
199
200
0
    rc = bcrypt_pbkdf(passphrase,
201
0
                      strlen(passphrase),
202
0
                      ssh_string_data(salt),
203
0
                      ssh_string_len(salt),
204
0
                      key_material,
205
0
                      key_material_len,
206
0
                      rounds);
207
0
    SAFE_FREE(salt);
208
0
    if (rc < 0){
209
0
        return SSH_ERROR;
210
0
    }
211
0
    ssh_burn(passphrase_buffer, sizeof(passphrase_buffer));
212
213
0
    cipher.set_decrypt_key(&cipher,
214
0
                           key_material,
215
0
                           key_material + cipher.keysize/8);
216
0
    cipher.decrypt(&cipher,
217
0
                   ssh_string_data(blob),
218
0
                   ssh_string_data(blob),
219
0
                   ssh_string_len(blob));
220
0
    ssh_cipher_clear(&cipher);
221
0
    return SSH_OK;
222
0
}
223
224
225
/** @internal
226
 * @brief Import a private key in OpenSSH (new) format. This format is
227
 * typically used with ed25519 keys but can be used for others.
228
 */
229
static ssh_key
230
ssh_pki_openssh_import(const char *text_key,
231
                       const char *passphrase,
232
                       ssh_auth_callback auth_fn,
233
                       void *auth_data,
234
                       bool private)
235
363
{
236
363
    const char *ptr = text_key;
237
363
    const char *end = NULL;
238
363
    char *base64 = NULL;
239
363
    int cmp;
240
363
    int rc;
241
363
    int i;
242
363
    ssh_buffer buffer = NULL, privkey_buffer = NULL;
243
363
    char *magic = NULL, *ciphername = NULL, *kdfname = NULL;
244
363
    uint32_t nkeys = 0, checkint1 = 0, checkint2 = 0xFFFF;
245
363
    ssh_string kdfoptions = NULL;
246
363
    ssh_string pubkey0 = NULL;
247
363
    ssh_string privkeys = NULL;
248
363
    ssh_string comment = NULL;
249
363
    ssh_key key = NULL;
250
363
    uint8_t padding;
251
252
363
    cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
253
363
    if (cmp != 0) {
254
0
        SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (no header)");
255
0
        goto out;
256
0
    }
257
363
    ptr += strlen(OPENSSH_HEADER_BEGIN);
258
1.01k
    while(ptr[0] != '\0' && !isspace((int)ptr[0])) {
259
647
        ptr++;
260
647
    }
261
363
    end = strstr(ptr, OPENSSH_HEADER_END);
262
363
    if (end == NULL) {
263
12
        SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (no footer)");
264
12
        goto out;
265
12
    }
266
351
    base64 = malloc(end - ptr + 1);
267
351
    if (base64 == NULL) {
268
3
        goto out;
269
3
    }
270
9.10M
    for (i = 0; ptr < end; ptr++) {
271
9.10M
        if (!isspace((int)ptr[0])) {
272
9.10M
            base64[i] = ptr[0];
273
9.10M
            i++;
274
9.10M
        }
275
9.10M
    }
276
348
    base64[i] = '\0';
277
348
    buffer = base64_to_bin(base64);
278
348
    SAFE_FREE(base64);
279
348
    if (buffer == NULL) {
280
19
        SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (base64 error)");
281
19
        goto out;
282
19
    }
283
329
    rc = ssh_buffer_unpack(buffer, "PssSdSS",
284
329
                           strlen(OPENSSH_AUTH_MAGIC) + 1,
285
329
                           &magic,
286
329
                           &ciphername,
287
329
                           &kdfname,
288
329
                           &kdfoptions,
289
329
                           &nkeys,
290
329
                           &pubkey0,
291
329
                           &privkeys);
292
329
    if (rc == SSH_ERROR) {
293
152
        SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (unpack error)");
294
152
        goto out;
295
152
    }
296
177
    cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC));
297
177
    if (cmp != 0) {
298
135
        SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (bad magic)");
299
135
        goto out;
300
135
    }
301
42
    SSH_LOG(SSH_LOG_DEBUG,
302
42
            "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %" PRIu32,
303
42
            ciphername,
304
42
            kdfname,
305
42
            nkeys);
306
42
    if (nkeys != 1) {
307
40
        SSH_LOG(SSH_LOG_TRACE, "Opening OpenSSH private key: only 1 key supported (%" PRIu32 " available)", nkeys);
308
40
        goto out;
309
40
    }
310
311
    /* If we are interested only in public key do not progress
312
     * to the key decryption later
313
     */
314
2
    if (!private) {
315
2
        rc = ssh_pki_import_pubkey_blob(pubkey0, &key);
316
2
        if (rc != SSH_OK) {
317
2
            SSH_LOG(SSH_LOG_TRACE, "Failed to import public key blob");
318
2
        }
319
        /* in either case we clean up here */
320
2
        goto out;
321
2
    }
322
323
0
    rc = pki_private_key_decrypt(privkeys,
324
0
                                 passphrase,
325
0
                                 ciphername,
326
0
                                 kdfname,
327
0
                                 kdfoptions,
328
0
                                 auth_fn,
329
0
                                 auth_data);
330
0
    if (rc == SSH_ERROR) {
331
0
        goto out;
332
0
    }
333
334
0
    privkey_buffer = ssh_buffer_new();
335
0
    if (privkey_buffer == NULL) {
336
0
        goto out;
337
0
    }
338
339
0
    ssh_buffer_set_secure(privkey_buffer);
340
0
    ssh_buffer_add_data(privkey_buffer,
341
0
                        ssh_string_data(privkeys),
342
0
                        (uint32_t)ssh_string_len(privkeys));
343
344
0
    rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2);
345
0
    if (rc == SSH_ERROR || checkint1 != checkint2) {
346
0
        SSH_LOG(SSH_LOG_TRACE, "OpenSSH private key unpack error (correct password?)");
347
0
        goto out;
348
0
    }
349
0
    rc = pki_openssh_import_privkey_blob(privkey_buffer, &key);
350
0
    if (rc == SSH_ERROR) {
351
0
        goto out;
352
0
    }
353
0
    comment = ssh_buffer_get_ssh_string(privkey_buffer);
354
0
    SAFE_FREE(comment);
355
    /* verify that the remaining data is correct padding */
356
0
    for (i = 1; ssh_buffer_get_len(privkey_buffer) > 0; ++i) {
357
0
        ssh_buffer_get_u8(privkey_buffer, &padding);
358
0
        if (padding != i) {
359
0
            ssh_key_free(key);
360
0
            key = NULL;
361
0
            SSH_LOG(SSH_LOG_TRACE, "Invalid padding");
362
0
            goto out;
363
0
        }
364
0
    }
365
363
out:
366
363
    if (buffer != NULL) {
367
329
        SSH_BUFFER_FREE(buffer);
368
329
        buffer = NULL;
369
329
    }
370
363
    if (privkey_buffer != NULL) {
371
0
        SSH_BUFFER_FREE(privkey_buffer);
372
0
        privkey_buffer = NULL;
373
0
    }
374
363
    SAFE_FREE(magic);
375
363
    SAFE_FREE(ciphername);
376
363
    SAFE_FREE(kdfname);
377
363
    SAFE_FREE(kdfoptions);
378
363
    SAFE_FREE(pubkey0);
379
363
    SAFE_FREE(privkeys);
380
363
    return key;
381
0
}
382
383
ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
384
                                       const char *passphrase,
385
                                       ssh_auth_callback auth_fn,
386
                                       void *auth_data)
387
0
{
388
0
    return ssh_pki_openssh_import(text_key, passphrase, auth_fn, auth_data, true);
389
0
}
390
391
ssh_key ssh_pki_openssh_pubkey_import(const char *text_key)
392
363
{
393
363
    return ssh_pki_openssh_import(text_key, NULL, NULL, NULL, false);
394
363
}
395
396
397
/** @internal
398
 * @brief encrypts an ed25519 private key blob
399
 *
400
 */
401
static int pki_private_key_encrypt(ssh_buffer privkey_buffer,
402
                                   const char* passphrase,
403
                                   const char *ciphername,
404
                                   const char *kdfname,
405
                                   ssh_auth_callback auth_fn,
406
                                   void *auth_data,
407
                                   uint32_t rounds,
408
                                   ssh_string salt)
409
0
{
410
0
    struct ssh_cipher_struct *ciphers = ssh_get_ciphertab();
411
0
    struct ssh_cipher_struct cipher;
412
0
    uint8_t key_material[128] = {0};
413
0
    size_t key_material_len;
414
0
    char passphrase_buffer[128] = {0};
415
0
    int rc;
416
0
    int i;
417
0
    int cmp;
418
419
0
    cmp = strcmp(ciphername, "none");
420
0
    if (cmp == 0){
421
        /* no encryption required */
422
0
        return SSH_OK;
423
0
    }
424
425
0
    for (i = 0; ciphers[i].name != NULL; i++) {
426
0
        cmp = strcmp(ciphername, ciphers[i].name);
427
0
        if (cmp == 0){
428
0
            memcpy(&cipher, &ciphers[i], sizeof(cipher));
429
0
            break;
430
0
        }
431
0
    }
432
433
0
    if (ciphers[i].name == NULL){
434
0
        SSH_LOG(SSH_LOG_TRACE, "Unsupported cipher %s", ciphername);
435
0
        return SSH_ERROR;
436
0
    }
437
438
0
    cmp = strcmp(kdfname, "bcrypt");
439
0
    if (cmp != 0){
440
0
        SSH_LOG(SSH_LOG_TRACE, "Unsupported KDF %s", kdfname);
441
0
        return SSH_ERROR;
442
0
    }
443
    /* We need material for key (keysize bits / 8) and IV (blocksize)  */
444
0
    key_material_len =  cipher.keysize/8 + cipher.blocksize;
445
0
    if (key_material_len > sizeof(key_material)){
446
0
        SSH_LOG(SSH_LOG_TRACE, "Key material too big");
447
0
        return SSH_ERROR;
448
0
    }
449
450
0
    SSH_LOG(SSH_LOG_DEBUG, "Encryption: %d key, %d IV, %" PRIu32 " rounds, %zu bytes salt",
451
0
                cipher.keysize/8,
452
0
                cipher.blocksize, rounds, ssh_string_len(salt));
453
454
0
    if (passphrase == NULL){
455
0
        if (auth_fn == NULL){
456
0
            SSH_LOG(SSH_LOG_TRACE, "No passphrase provided");
457
0
            return SSH_ERROR;
458
0
        }
459
0
        rc = auth_fn("Passphrase",
460
0
                     passphrase_buffer,
461
0
                     sizeof(passphrase_buffer),
462
0
                     0,
463
0
                     0,
464
0
                     auth_data);
465
0
        if (rc != SSH_OK){
466
0
            return SSH_ERROR;
467
0
        }
468
0
        passphrase = passphrase_buffer;
469
0
    }
470
471
0
    rc = bcrypt_pbkdf(passphrase,
472
0
                      strlen(passphrase),
473
0
                      ssh_string_data(salt),
474
0
                      ssh_string_len(salt),
475
0
                      key_material,
476
0
                      key_material_len,
477
0
                      rounds);
478
0
    if (rc < 0){
479
0
        return SSH_ERROR;
480
0
    }
481
482
0
    cipher.set_encrypt_key(&cipher,
483
0
                           key_material,
484
0
                           key_material + cipher.keysize/8);
485
0
    cipher.encrypt(&cipher,
486
0
                   ssh_buffer_get(privkey_buffer),
487
0
                   ssh_buffer_get(privkey_buffer),
488
0
                   ssh_buffer_get_len(privkey_buffer));
489
0
    ssh_cipher_clear(&cipher);
490
0
    ssh_burn(passphrase_buffer, sizeof(passphrase_buffer));
491
492
0
    return SSH_OK;
493
0
}
494
495
496
/** @internal
497
 * generate an OpenSSH private key (defined in PROTOCOL.key) and output it in text format.
498
 * @param privkey[in] private key to export
499
 * @returns an SSH string containing the text representation of the exported key.
500
 * @warning currently only supports ED25519 key types.
501
 */
502
503
ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
504
                                          const char *passphrase,
505
                                          ssh_auth_callback auth_fn,
506
                                          void *auth_data)
507
0
{
508
0
    ssh_buffer buffer = NULL;
509
0
    ssh_string str = NULL, blob = NULL;
510
0
    ssh_string pubkey_s = NULL;
511
0
    ssh_buffer privkey_buffer = NULL;
512
0
    uint32_t rnd;
513
0
    uint32_t rounds = 16;
514
0
    ssh_string salt = NULL;
515
0
    ssh_string kdf_options = NULL;
516
0
    int to_encrypt=0;
517
0
    unsigned char *b64 = NULL;
518
0
    uint32_t str_len, len;
519
0
    uint8_t padding = 1;
520
0
    int ok;
521
0
    int rc;
522
523
0
    if (privkey == NULL) {
524
0
        return NULL;
525
0
    }
526
0
    if (passphrase != NULL || auth_fn != NULL){
527
0
        SSH_LOG(SSH_LOG_DEBUG, "Enabling encryption for private key export");
528
0
        to_encrypt = 1;
529
0
    }
530
0
    buffer = ssh_buffer_new();
531
0
    rc = ssh_pki_export_pubkey_blob(privkey, &pubkey_s);
532
0
    if (buffer == NULL || rc != SSH_OK) {
533
0
        goto error;
534
0
    }
535
536
0
    ok = ssh_get_random(&rnd, sizeof(rnd), 0);
537
0
    if (!ok) {
538
0
        goto error;
539
0
    }
540
541
0
    privkey_buffer = ssh_buffer_new();
542
0
    if (privkey_buffer == NULL) {
543
0
        goto error;
544
0
    }
545
546
0
    rc = ssh_pki_export_privkey_blob(privkey, &blob);
547
0
    if (rc != SSH_OK) {
548
0
        goto error;
549
0
    }
550
551
0
    rc = ssh_buffer_pack(privkey_buffer,
552
0
                         "ddPs",
553
0
                         rnd, /* checkint 1 & 2 */
554
0
                         rnd,
555
0
                         ssh_string_len(blob),
556
0
                         ssh_string_data(blob),
557
0
                         "" /* comment */);
558
0
    if (rc == SSH_ERROR){
559
0
        goto error;
560
0
    }
561
562
    /* Add padding regardless encryption because it is expected
563
     * by OpenSSH tools.
564
     * XXX Using 16 B as we use only AES cipher below anyway.
565
     */
566
0
    while (ssh_buffer_get_len(privkey_buffer) % 16 != 0) {
567
0
        rc = ssh_buffer_add_u8(privkey_buffer, padding);
568
0
        if (rc < 0) {
569
0
            goto error;
570
0
        }
571
0
        padding++;
572
0
    }
573
574
0
    if (to_encrypt){
575
0
        ssh_buffer kdf_buf;
576
577
0
        kdf_buf = ssh_buffer_new();
578
0
        if (kdf_buf == NULL) {
579
0
            goto error;
580
0
        }
581
582
0
        salt = ssh_string_new(16);
583
0
        if (salt == NULL){
584
0
            SSH_BUFFER_FREE(kdf_buf);
585
0
            goto error;
586
0
        }
587
588
0
        ok = ssh_get_random(ssh_string_data(salt), 16, 0);
589
0
        if (!ok) {
590
0
            SSH_BUFFER_FREE(kdf_buf);
591
0
            goto error;
592
0
        }
593
594
0
        rc = ssh_buffer_pack(kdf_buf, "Sd", salt, rounds);
595
0
        if (rc != SSH_OK) {
596
0
            SSH_BUFFER_FREE(kdf_buf);
597
0
            goto error;
598
0
        }
599
0
        kdf_options = ssh_string_new(ssh_buffer_get_len(kdf_buf));
600
0
        if (kdf_options == NULL){
601
0
            SSH_BUFFER_FREE(kdf_buf);
602
0
            goto error;
603
0
        }
604
0
        memcpy(ssh_string_data(kdf_options),
605
0
               ssh_buffer_get(kdf_buf),
606
0
               ssh_buffer_get_len(kdf_buf));
607
0
        SSH_BUFFER_FREE(kdf_buf);
608
0
        rc = pki_private_key_encrypt(privkey_buffer,
609
0
                                     passphrase,
610
0
                                     "aes128-cbc",
611
0
                                     "bcrypt",
612
0
                                     auth_fn,
613
0
                                     auth_data,
614
0
                                     rounds,
615
0
                                     salt);
616
0
        if (rc != SSH_OK){
617
0
            goto error;
618
0
        }
619
0
    } else {
620
0
        kdf_options = ssh_string_new(0);
621
0
    }
622
623
0
    rc = ssh_buffer_pack(buffer,
624
0
                         "PssSdSdP",
625
0
                         strlen(OPENSSH_AUTH_MAGIC) + 1,
626
0
                         OPENSSH_AUTH_MAGIC,
627
0
                         to_encrypt ? "aes128-cbc" : "none", /* ciphername */
628
0
                         to_encrypt ? "bcrypt" : "none",     /* kdfname */
629
0
                         kdf_options,                        /* kdfoptions */
630
0
                         (uint32_t)1,                        /* nkeys */
631
0
                         pubkey_s,
632
0
                         ssh_buffer_get_len(privkey_buffer),
633
                         /* rest of buffer is a string */
634
0
                         (size_t)ssh_buffer_get_len(privkey_buffer),
635
0
                         ssh_buffer_get(privkey_buffer));
636
0
    if (rc != SSH_OK) {
637
0
        goto error;
638
0
    }
639
640
0
    b64 = bin_to_base64(ssh_buffer_get(buffer),
641
0
                        ssh_buffer_get_len(buffer));
642
0
    if (b64 == NULL){
643
0
        goto error;
644
0
    }
645
646
    /* we can reuse the buffer */
647
0
    ssh_buffer_reinit(buffer);
648
0
    rc = ssh_buffer_pack(buffer,
649
0
                         "tttttt",
650
0
                         OPENSSH_HEADER_BEGIN,
651
0
                         "\n",
652
0
                         b64,
653
0
                         "\n",
654
0
                         OPENSSH_HEADER_END,
655
0
                         "\n");
656
0
    ssh_burn(b64, strlen((char *)b64));
657
0
    SAFE_FREE(b64);
658
659
0
    if (rc != SSH_OK){
660
0
        goto error;
661
0
    }
662
663
0
    str = ssh_string_new(ssh_buffer_get_len(buffer));
664
0
    if (str == NULL){
665
0
        goto error;
666
0
    }
667
668
0
    str_len = ssh_buffer_get_len(buffer);
669
0
    len = ssh_buffer_get_data(buffer, ssh_string_data(str), str_len);
670
0
    if (str_len != len) {
671
0
        SSH_STRING_FREE(str);
672
0
        str = NULL;
673
0
    }
674
675
0
error:
676
0
    ssh_string_burn(blob);
677
0
    ssh_string_free(blob);
678
0
    if (privkey_buffer != NULL) {
679
0
        void *bufptr = ssh_buffer_get(privkey_buffer);
680
0
        ssh_burn(bufptr, ssh_buffer_get_len(privkey_buffer));
681
0
        SSH_BUFFER_FREE(privkey_buffer);
682
0
    }
683
0
    SAFE_FREE(pubkey_s);
684
0
    SAFE_FREE(kdf_options);
685
0
    SAFE_FREE(salt);
686
0
    if (buffer != NULL) {
687
0
        SSH_BUFFER_FREE(buffer);
688
0
    }
689
690
0
    return str;
691
0
}
692
693
694
/**
695
 * @}
696
 */