Coverage Report

Created: 2025-08-03 07:08

/src/libssh2/src/pem.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) The Written Word, Inc.
2
 * Copyright (C) Simon Josefsson
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms,
6
 * with or without modification, are permitted provided
7
 * that the following conditions are met:
8
 *
9
 *   Redistributions of source code must retain the above
10
 *   copyright notice, this list of conditions and the
11
 *   following disclaimer.
12
 *
13
 *   Redistributions in binary form must reproduce the above
14
 *   copyright notice, this list of conditions and the following
15
 *   disclaimer in the documentation and/or other materials
16
 *   provided with the distribution.
17
 *
18
 *   Neither the name of the copyright holder nor the names
19
 *   of any other contributors may be used to endorse or
20
 *   promote products derived from this software without
21
 *   specific prior written permission.
22
 *
23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
35
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
36
 * OF SUCH DAMAGE.
37
 *
38
 * SPDX-License-Identifier: BSD-3-Clause
39
 */
40
41
#include "libssh2_priv.h"
42
43
static int
44
readline(char *line, int line_size, FILE * fp)
45
0
{
46
0
    size_t len;
47
48
0
    if(!line) {
49
0
        return -1;
50
0
    }
51
0
    if(!fgets(line, line_size, fp)) {
52
0
        return -1;
53
0
    }
54
55
0
    if(*line) {
56
0
        len = strlen(line);
57
0
        if(len > 0 && line[len - 1] == '\n') {
58
0
            line[len - 1] = '\0';
59
0
        }
60
0
    }
61
62
0
    if(*line) {
63
0
        len = strlen(line);
64
0
        if(len > 0 && line[len - 1] == '\r') {
65
0
            line[len - 1] = '\0';
66
0
        }
67
0
    }
68
69
0
    return 0;
70
0
}
71
72
static int
73
readline_memory(char *line, size_t line_size,
74
                const char *filedata, size_t filedata_len,
75
                size_t *filedata_offset)
76
0
{
77
0
    size_t off, len;
78
79
0
    off = *filedata_offset;
80
81
0
    for(len = 0; off + len < filedata_len && len < line_size - 1; len++) {
82
0
        if(filedata[off + len] == '\n' ||
83
0
            filedata[off + len] == '\r') {
84
0
                break;
85
0
        }
86
0
    }
87
88
0
    if(len) {
89
0
        memcpy(line, filedata + off, len);
90
0
        *filedata_offset += len;
91
0
    }
92
93
0
    line[len] = '\0';
94
0
    *filedata_offset += 1;
95
96
0
    return 0;
97
0
}
98
99
0
#define LINE_SIZE 128
100
101
static const char *crypt_annotation = "Proc-Type: 4,ENCRYPTED";
102
103
static unsigned char hex_decode(char digit)
104
0
{
105
0
    return (unsigned char)
106
0
        ((digit >= 'A') ? (0xA + (digit - 'A')) : (digit - '0'));
107
0
}
108
109
int
110
_libssh2_pem_parse(LIBSSH2_SESSION * session,
111
                   const char *headerbegin,
112
                   const char *headerend,
113
                   const unsigned char *passphrase,
114
                   FILE * fp, unsigned char **data, size_t *datalen)
115
0
{
116
0
    char line[LINE_SIZE];
117
0
    unsigned char iv[LINE_SIZE];
118
0
    char *b64data = NULL;
119
0
    size_t b64datalen = 0;
120
0
    int ret;
121
0
    const LIBSSH2_CRYPT_METHOD *method = NULL;
122
123
0
    do {
124
0
        *line = '\0';
125
126
0
        if(readline(line, LINE_SIZE, fp)) {
127
0
            return -1;
128
0
        }
129
0
    } while(strcmp(line, headerbegin) != 0);
130
131
0
    if(readline(line, LINE_SIZE, fp)) {
132
0
        return -1;
133
0
    }
134
135
0
    if(passphrase &&
136
0
            memcmp(line, crypt_annotation, strlen(crypt_annotation)) == 0) {
137
0
        const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
138
0
        int i;
139
140
0
        if(readline(line, LINE_SIZE, fp)) {
141
0
            ret = -1;
142
0
            goto out;
143
0
        }
144
145
0
        all_methods = libssh2_crypt_methods();
146
        /* !checksrc! disable EQUALSNULL 1 */
147
0
        while((cur_method = *all_methods++) != NULL) {
148
0
            if(*cur_method->pem_annotation &&
149
0
                    memcmp(line, cur_method->pem_annotation,
150
0
                           strlen(cur_method->pem_annotation)) == 0) {
151
0
                method = cur_method;
152
0
                memcpy(iv, line + strlen(method->pem_annotation) + 1,
153
0
                       2*method->iv_len);
154
0
            }
155
0
        }
156
157
        /* None of the available crypt methods were able to decrypt the key */
158
0
        if(!method)
159
0
            return -1;
160
161
        /* Decode IV from hex */
162
0
        for(i = 0; i < method->iv_len; ++i) {
163
0
            iv[i]  = (unsigned char)(hex_decode(iv[2*i]) << 4);
164
0
            iv[i] |= hex_decode(iv[2*i + 1]);
165
0
        }
166
167
        /* skip to the next line */
168
0
        if(readline(line, LINE_SIZE, fp)) {
169
0
            ret = -1;
170
0
            goto out;
171
0
        }
172
0
    }
173
174
0
    do {
175
0
        if(*line) {
176
0
            char *tmp;
177
0
            size_t linelen;
178
179
0
            linelen = strlen(line);
180
0
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
181
0
            if(!tmp) {
182
0
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
183
0
                               "Unable to allocate memory for PEM parsing");
184
0
                ret = -1;
185
0
                goto out;
186
0
            }
187
0
            memcpy(tmp + b64datalen, line, linelen);
188
0
            b64data = tmp;
189
0
            b64datalen += linelen;
190
0
        }
191
192
0
        *line = '\0';
193
194
0
        if(readline(line, LINE_SIZE, fp)) {
195
0
            ret = -1;
196
0
            goto out;
197
0
        }
198
0
    } while(strcmp(line, headerend) != 0);
199
200
0
    if(!b64data) {
201
0
        return -1;
202
0
    }
203
204
0
    if(_libssh2_base64_decode(session, (char **) data, datalen,
205
0
                              b64data, b64datalen)) {
206
0
        ret = -1;
207
0
        goto out;
208
0
    }
209
210
0
    if(method) {
211
#if LIBSSH2_MD5_PEM
212
        /* Set up decryption */
213
        int free_iv = 0, free_secret = 0, len_decrypted = 0, padding = 0;
214
        int blocksize = method->blocksize;
215
        void *abstract;
216
        unsigned char secret[2*MD5_DIGEST_LENGTH];
217
        libssh2_md5_ctx fingerprint_ctx;
218
219
        /* Perform key derivation (PBKDF1/MD5) */
220
        if(!libssh2_md5_init(&fingerprint_ctx) ||
221
           !libssh2_md5_update(fingerprint_ctx, passphrase,
222
                               strlen((const char *)passphrase)) ||
223
           !libssh2_md5_update(fingerprint_ctx, iv, 8) ||
224
           !libssh2_md5_final(fingerprint_ctx, secret)) {
225
            ret = -1;
226
            goto out;
227
        }
228
        if(method->secret_len > MD5_DIGEST_LENGTH) {
229
            if(!libssh2_md5_init(&fingerprint_ctx) ||
230
               !libssh2_md5_update(fingerprint_ctx,
231
                                   secret, MD5_DIGEST_LENGTH) ||
232
               !libssh2_md5_update(fingerprint_ctx,
233
                                   passphrase,
234
                                   strlen((const char *)passphrase)) ||
235
               !libssh2_md5_update(fingerprint_ctx, iv, 8) ||
236
               !libssh2_md5_final(fingerprint_ctx,
237
                                  secret + MD5_DIGEST_LENGTH)) {
238
                ret = -1;
239
                goto out;
240
            }
241
        }
242
243
        /* Initialize the decryption */
244
        if(method->init(session, method, iv, &free_iv, secret,
245
                         &free_secret, 0, &abstract)) {
246
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
247
            LIBSSH2_FREE(session, data);
248
            ret = -1;
249
            goto out;
250
        }
251
252
        if(free_secret) {
253
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
254
        }
255
256
        /* Do the actual decryption */
257
        if((*datalen % blocksize) != 0) {
258
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
259
            method->dtor(session, &abstract);
260
            _libssh2_explicit_zero(*data, *datalen);
261
            LIBSSH2_FREE(session, *data);
262
            ret = -1;
263
            goto out;
264
        }
265
266
        if(method->flags & LIBSSH2_CRYPT_FLAG_REQUIRES_FULL_PACKET) {
267
            if(method->crypt(session, 0, *data, *datalen, &abstract, 0)) {
268
                ret = LIBSSH2_ERROR_DECRYPT;
269
                _libssh2_explicit_zero((char *)secret, sizeof(secret));
270
                method->dtor(session, &abstract);
271
                _libssh2_explicit_zero(*data, *datalen);
272
                LIBSSH2_FREE(session, *data);
273
                goto out;
274
            }
275
        }
276
        else {
277
            while(len_decrypted <= (int)*datalen - blocksize) {
278
                if(method->crypt(session, 0, *data + len_decrypted, blocksize,
279
                                &abstract,
280
                                len_decrypted == 0 ? FIRST_BLOCK :
281
                                ((len_decrypted == (int)*datalen - blocksize) ?
282
                                 LAST_BLOCK : MIDDLE_BLOCK)
283
                                )) {
284
                    ret = LIBSSH2_ERROR_DECRYPT;
285
                    _libssh2_explicit_zero((char *)secret, sizeof(secret));
286
                    method->dtor(session, &abstract);
287
                    _libssh2_explicit_zero(*data, *datalen);
288
                    LIBSSH2_FREE(session, *data);
289
                    goto out;
290
                }
291
292
                len_decrypted += blocksize;
293
            }
294
        }
295
296
        /* Account for padding */
297
        padding = (*data)[*datalen - 1];
298
        memset(&(*data)[*datalen-padding], 0, padding);
299
        *datalen -= padding;
300
301
        /* Clean up */
302
        _libssh2_explicit_zero((char *)secret, sizeof(secret));
303
        method->dtor(session, &abstract);
304
#else
305
0
        ret = -1;
306
0
        goto out;
307
0
#endif
308
0
    }
309
310
0
    ret = 0;
311
0
out:
312
0
    if(b64data) {
313
0
        _libssh2_explicit_zero(b64data, b64datalen);
314
0
        LIBSSH2_FREE(session, b64data);
315
0
    }
316
0
    return ret;
317
0
}
318
319
int
320
_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
321
                          const char *headerbegin,
322
                          const char *headerend,
323
                          const char *filedata, size_t filedata_len,
324
                          unsigned char **data, size_t *datalen)
325
0
{
326
0
    char line[LINE_SIZE];
327
0
    char *b64data = NULL;
328
0
    size_t b64datalen = 0;
329
0
    size_t off = 0;
330
0
    int ret;
331
332
0
    do {
333
0
        *line = '\0';
334
335
0
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
336
0
            return -1;
337
0
        }
338
0
    } while(strcmp(line, headerbegin) != 0);
339
340
0
    *line = '\0';
341
342
0
    do {
343
0
        if(*line) {
344
0
            char *tmp;
345
0
            size_t linelen;
346
347
0
            linelen = strlen(line);
348
0
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
349
0
            if(!tmp) {
350
0
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
351
0
                               "Unable to allocate memory for PEM parsing");
352
0
                ret = -1;
353
0
                goto out;
354
0
            }
355
0
            memcpy(tmp + b64datalen, line, linelen);
356
0
            b64data = tmp;
357
0
            b64datalen += linelen;
358
0
        }
359
360
0
        *line = '\0';
361
362
0
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
363
0
            ret = -1;
364
0
            goto out;
365
0
        }
366
0
    } while(strcmp(line, headerend) != 0);
367
368
0
    if(!b64data) {
369
0
        return -1;
370
0
    }
371
372
0
    if(_libssh2_base64_decode(session, (char **) data, datalen,
373
0
                              b64data, b64datalen)) {
374
0
        ret = -1;
375
0
        goto out;
376
0
    }
377
378
0
    ret = 0;
379
0
out:
380
0
    if(b64data) {
381
0
        _libssh2_explicit_zero(b64data, b64datalen);
382
0
        LIBSSH2_FREE(session, b64data);
383
0
    }
384
0
    return ret;
385
0
}
386
387
/* OpenSSH formatted keys */
388
0
#define AUTH_MAGIC "openssh-key-v1"
389
0
#define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
390
0
#define OPENSSH_HEADER_END "-----END OPENSSH PRIVATE KEY-----"
391
392
static int
393
_libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,
394
                                const unsigned char *passphrase,
395
                                const char *b64data, size_t b64datalen,
396
                                struct string_buf **decrypted_buf)
397
0
{
398
0
    const LIBSSH2_CRYPT_METHOD *method = NULL;
399
0
    struct string_buf decoded, decrypted, kdf_buf;
400
0
    unsigned char *ciphername = NULL;
401
0
    unsigned char *kdfname = NULL;
402
0
    unsigned char *kdf = NULL;
403
0
    unsigned char *buf = NULL;
404
0
    unsigned char *salt = NULL;
405
0
    uint32_t nkeys, check1, check2;
406
0
    uint32_t rounds = 0;
407
0
    unsigned char *key = NULL;
408
0
    unsigned char *key_part = NULL;
409
0
    unsigned char *iv_part = NULL;
410
0
    unsigned char *f = NULL;
411
0
    size_t f_len = 0;
412
0
    int ret = 0, keylen = 0, ivlen = 0, total_len = 0;
413
0
    size_t kdf_len = 0, tmp_len = 0, salt_len = 0;
414
415
0
    if(decrypted_buf)
416
0
        *decrypted_buf = NULL;
417
418
    /* decode file */
419
0
    if(_libssh2_base64_decode(session, (char **)&f, &f_len,
420
0
                              b64data, b64datalen)) {
421
0
        ret = -1;
422
0
        goto out;
423
0
    }
424
425
    /* Parse the file */
426
0
    decoded.data = (unsigned char *)f;
427
0
    decoded.dataptr = (unsigned char *)f;
428
0
    decoded.len = f_len;
429
430
0
    if(decoded.len < strlen(AUTH_MAGIC)) {
431
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, "key too short");
432
0
        goto out;
433
0
    }
434
435
0
    if(strncmp((const char *) decoded.dataptr, AUTH_MAGIC,
436
0
               strlen(AUTH_MAGIC)) != 0) {
437
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
438
0
                             "key auth magic mismatch");
439
0
        goto out;
440
0
    }
441
442
0
    decoded.dataptr += strlen(AUTH_MAGIC) + 1;
443
444
0
    if(_libssh2_get_string(&decoded, &ciphername, &tmp_len) ||
445
0
       tmp_len == 0) {
446
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
447
0
                             "ciphername is missing");
448
0
        goto out;
449
0
    }
450
451
0
    if(_libssh2_get_string(&decoded, &kdfname, &tmp_len) ||
452
0
       tmp_len == 0) {
453
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
454
0
                             "kdfname is missing");
455
0
        goto out;
456
0
    }
457
458
0
    if(_libssh2_get_string(&decoded, &kdf, &kdf_len)) {
459
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
460
0
                             "kdf is missing");
461
0
        goto out;
462
0
    }
463
0
    else {
464
0
        kdf_buf.data = kdf;
465
0
        kdf_buf.dataptr = kdf;
466
0
        kdf_buf.len = kdf_len;
467
0
    }
468
469
0
    if((!passphrase || strlen((const char *)passphrase) == 0) &&
470
0
        strcmp((const char *)ciphername, "none") != 0) {
471
        /* passphrase required */
472
0
        ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
473
0
        goto out;
474
0
    }
475
476
0
    if(strcmp((const char *)kdfname, "none") != 0 &&
477
0
       strcmp((const char *)kdfname, "bcrypt") != 0) {
478
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
479
0
                             "unknown cipher");
480
0
        goto out;
481
0
    }
482
483
0
    if(!strcmp((const char *)kdfname, "none") &&
484
0
       strcmp((const char *)ciphername, "none") != 0) {
485
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
486
0
                             "invalid format");
487
0
        goto out;
488
0
    }
489
490
0
    if(_libssh2_get_u32(&decoded, &nkeys) != 0 || nkeys != 1) {
491
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
492
0
                             "Multiple keys are unsupported");
493
0
        goto out;
494
0
    }
495
496
    /* unencrypted public key */
497
498
0
    if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
499
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
500
0
                             "Invalid private key; "
501
0
                             "expect embedded public key");
502
0
        goto out;
503
0
    }
504
505
0
    if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
506
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
507
0
                             "Private key data not found");
508
0
        goto out;
509
0
    }
510
511
    /* decode encrypted private key */
512
0
    decrypted.data = decrypted.dataptr = buf;
513
0
    decrypted.len = tmp_len;
514
515
0
    if(ciphername && strcmp((const char *)ciphername, "none") != 0) {
516
0
        const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
517
518
0
        all_methods = libssh2_crypt_methods();
519
        /* !checksrc! disable EQUALSNULL 1 */
520
0
        while((cur_method = *all_methods++) != NULL) {
521
0
            if(*cur_method->name &&
522
0
                memcmp(ciphername, cur_method->name,
523
0
                       strlen(cur_method->name)) == 0) {
524
0
                    method = cur_method;
525
0
                }
526
0
        }
527
528
        /* None of the available crypt methods were able to decrypt the key */
529
530
0
        if(!method) {
531
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
532
0
                                 "No supported cipher found");
533
0
            goto out;
534
0
        }
535
0
    }
536
537
0
    if(method) {
538
0
        int free_iv = 0, free_secret = 0, len_decrypted = 0;
539
0
        int blocksize;
540
0
        void *abstract = NULL;
541
542
0
        keylen = method->secret_len;
543
0
        ivlen = method->iv_len;
544
0
        total_len = keylen + ivlen;
545
546
0
        key = LIBSSH2_CALLOC(session, total_len);
547
0
        if(!key) {
548
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
549
0
                                 "Could not alloc key");
550
0
            goto out;
551
0
        }
552
553
0
        if(strcmp((const char *)kdfname, "bcrypt") == 0 && passphrase) {
554
0
            if((_libssh2_get_string(&kdf_buf, &salt, &salt_len)) ||
555
0
                (_libssh2_get_u32(&kdf_buf, &rounds) != 0)) {
556
0
                ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
557
0
                                     "kdf contains unexpected values");
558
0
                goto out;
559
0
            }
560
561
0
            if(_libssh2_bcrypt_pbkdf((const char *)passphrase,
562
0
                                     strlen((const char *)passphrase),
563
0
                                     salt, salt_len, key,
564
0
                                     keylen + ivlen, rounds) < 0) {
565
0
                ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
566
0
                                     "invalid format");
567
0
                goto out;
568
0
            }
569
0
        }
570
0
        else {
571
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_KEYFILE_AUTH_FAILED,
572
0
                                 "bcrypted without passphrase");
573
0
            goto out;
574
0
        }
575
576
        /* Set up decryption */
577
0
        blocksize = method->blocksize;
578
579
0
        key_part = LIBSSH2_CALLOC(session, keylen);
580
0
        if(!key_part) {
581
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
582
0
                                 "Could not alloc key part");
583
0
            goto out;
584
0
        }
585
586
0
        iv_part = LIBSSH2_CALLOC(session, ivlen);
587
0
        if(!iv_part) {
588
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
589
0
                                 "Could not alloc iv part");
590
0
            goto out;
591
0
        }
592
593
0
        memcpy(key_part, key, keylen);
594
0
        memcpy(iv_part, key + keylen, ivlen);
595
596
        /* Initialize the decryption */
597
0
        if(method->init(session, method, iv_part, &free_iv, key_part,
598
0
                        &free_secret, 0, &abstract)) {
599
0
            ret = LIBSSH2_ERROR_DECRYPT;
600
0
            goto out;
601
0
        }
602
603
        /* Do the actual decryption */
604
0
        if((decrypted.len % blocksize) != 0) {
605
0
            method->dtor(session, &abstract);
606
0
            ret = LIBSSH2_ERROR_DECRYPT;
607
0
            goto out;
608
0
        }
609
610
0
        if(method->flags & LIBSSH2_CRYPT_FLAG_REQUIRES_FULL_PACKET) {
611
0
            if(method->crypt(session, 0, decrypted.data,
612
0
                             decrypted.len,
613
0
                             &abstract,
614
0
                             MIDDLE_BLOCK)) {
615
0
                ret = LIBSSH2_ERROR_DECRYPT;
616
0
                method->dtor(session, &abstract);
617
0
                goto out;
618
0
            }
619
0
        }
620
0
        else {
621
0
            while((size_t)len_decrypted <= decrypted.len - blocksize) {
622
                /* We always pass MIDDLE_BLOCK here because OpenSSH Key Files
623
                 * do not use AAD to authenticate the length.
624
                 * Furthermore, the authentication tag is appended after the
625
                 * encrypted key, and the length of the authentication tag is
626
                 * not included in the key length, so we check it after the
627
                 * loop.
628
                 */
629
0
                if(method->crypt(session, 0, decrypted.data + len_decrypted,
630
0
                                 blocksize,
631
0
                                 &abstract,
632
0
                                 MIDDLE_BLOCK)) {
633
0
                    ret = LIBSSH2_ERROR_DECRYPT;
634
0
                    method->dtor(session, &abstract);
635
0
                    goto out;
636
0
                }
637
638
0
                len_decrypted += blocksize;
639
0
            }
640
641
            /* No padding */
642
643
            /* for the AES GCM methods, the 16 byte authentication tag is
644
             * appended to the encrypted key */
645
0
            if(strcmp(method->name, "aes256-gcm@openssh.com") == 0 ||
646
0
               strcmp(method->name, "aes128-gcm@openssh.com") == 0) {
647
0
                if(!_libssh2_check_length(&decoded, 16)) {
648
0
                    ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
649
0
                                         "GCM auth tag missing");
650
0
                    method->dtor(session, &abstract);
651
0
                    goto out;
652
0
                }
653
0
                if(method->crypt(session, 0, decoded.dataptr, 16, &abstract,
654
0
                                 LAST_BLOCK)) {
655
0
                    ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
656
0
                                         "GCM auth tag invalid");
657
0
                    method->dtor(session, &abstract);
658
0
                    goto out;
659
0
                }
660
0
                decoded.dataptr += 16;
661
0
            }
662
0
        }
663
664
0
        method->dtor(session, &abstract);
665
0
    }
666
667
    /* Check random bytes match */
668
669
0
    if(_libssh2_get_u32(&decrypted, &check1) != 0 ||
670
0
       _libssh2_get_u32(&decrypted, &check2) != 0 ||
671
0
       check1 != check2) {
672
0
        _libssh2_error(session, LIBSSH2_ERROR_PROTO,
673
0
                       "Private key unpack failed (correct password?)");
674
0
        ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
675
0
        goto out;
676
0
    }
677
678
0
    if(decrypted_buf) {
679
        /* copy data to out-going buffer */
680
0
        struct string_buf *out_buf = _libssh2_string_buf_new(session);
681
0
        if(!out_buf) {
682
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
683
0
                                 "Unable to allocate memory for "
684
0
                                 "decrypted struct");
685
0
            goto out;
686
0
        }
687
688
0
        out_buf->data = LIBSSH2_CALLOC(session, decrypted.len);
689
0
        if(!out_buf->data) {
690
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
691
0
                                 "Unable to allocate memory for "
692
0
                                 "decrypted struct");
693
0
            _libssh2_string_buf_free(session, out_buf);
694
0
            goto out;
695
0
        }
696
0
        memcpy(out_buf->data, decrypted.data, decrypted.len);
697
0
        out_buf->dataptr = out_buf->data +
698
0
            (decrypted.dataptr - decrypted.data);
699
0
        out_buf->len = decrypted.len;
700
701
0
        *decrypted_buf = out_buf;
702
0
    }
703
704
0
out:
705
706
    /* Clean up */
707
0
    if(key) {
708
0
        _libssh2_explicit_zero(key, total_len);
709
0
        LIBSSH2_FREE(session, key);
710
0
    }
711
0
    if(key_part) {
712
0
        _libssh2_explicit_zero(key_part, keylen);
713
0
        LIBSSH2_FREE(session, key_part);
714
0
    }
715
0
    if(iv_part) {
716
0
        _libssh2_explicit_zero(iv_part, ivlen);
717
0
        LIBSSH2_FREE(session, iv_part);
718
0
    }
719
0
    if(f) {
720
0
        _libssh2_explicit_zero(f, f_len);
721
0
        LIBSSH2_FREE(session, f);
722
0
    }
723
724
0
    return ret;
725
0
}
726
727
int
728
_libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,
729
                           const unsigned char *passphrase,
730
                           FILE * fp, struct string_buf **decrypted_buf)
731
0
{
732
0
    char line[LINE_SIZE];
733
0
    char *b64data = NULL;
734
0
    size_t b64datalen = 0;
735
0
    int ret = 0;
736
737
    /* read file */
738
739
0
    do {
740
0
        *line = '\0';
741
742
0
        if(readline(line, LINE_SIZE, fp)) {
743
0
            return -1;
744
0
        }
745
0
    } while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
746
747
0
    if(readline(line, LINE_SIZE, fp)) {
748
0
        return -1;
749
0
    }
750
751
0
    do {
752
0
        if(*line) {
753
0
            char *tmp;
754
0
            size_t linelen;
755
756
0
            linelen = strlen(line);
757
0
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
758
0
            if(!tmp) {
759
0
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
760
0
                               "Unable to allocate memory for PEM parsing");
761
0
                ret = -1;
762
0
                goto out;
763
0
            }
764
0
            memcpy(tmp + b64datalen, line, linelen);
765
0
            b64data = tmp;
766
0
            b64datalen += linelen;
767
0
        }
768
769
0
        *line = '\0';
770
771
0
        if(readline(line, LINE_SIZE, fp)) {
772
0
            ret = -1;
773
0
            goto out;
774
0
        }
775
0
    } while(strcmp(line, OPENSSH_HEADER_END) != 0);
776
777
0
    if(!b64data) {
778
0
        return -1;
779
0
    }
780
781
0
    ret = _libssh2_openssh_pem_parse_data(session,
782
0
                                          passphrase,
783
0
                                          (const char *)b64data,
784
0
                                          b64datalen,
785
0
                                          decrypted_buf);
786
787
0
    if(b64data) {
788
0
        _libssh2_explicit_zero(b64data, b64datalen);
789
0
        LIBSSH2_FREE(session, b64data);
790
0
    }
791
792
0
out:
793
794
0
    return ret;
795
0
}
796
797
int
798
_libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,
799
                                  const unsigned char *passphrase,
800
                                  const char *filedata, size_t filedata_len,
801
                                  struct string_buf **decrypted_buf)
802
0
{
803
0
    char line[LINE_SIZE];
804
0
    char *b64data = NULL;
805
0
    size_t b64datalen = 0;
806
0
    size_t off = 0;
807
0
    int ret;
808
809
0
    if(!filedata || filedata_len <= 0)
810
0
        return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
811
0
                              "Error parsing PEM: filedata missing");
812
813
0
    do {
814
815
0
        *line = '\0';
816
817
0
        if(off >= filedata_len)
818
0
            return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
819
0
                                  "Error parsing PEM: "
820
0
                                  "OpenSSH header not found");
821
822
0
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
823
0
            return -1;
824
0
        }
825
0
    } while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
826
827
0
    *line = '\0';
828
829
0
    do {
830
0
        if(*line) {
831
0
            char *tmp;
832
0
            size_t linelen;
833
834
0
            linelen = strlen(line);
835
0
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
836
0
            if(!tmp) {
837
0
                ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
838
0
                                     "Unable to allocate memory for "
839
0
                                     "PEM parsing");
840
0
                goto out;
841
0
            }
842
0
            memcpy(tmp + b64datalen, line, linelen);
843
0
            b64data = tmp;
844
0
            b64datalen += linelen;
845
0
        }
846
847
0
        *line = '\0';
848
849
0
        if(off >= filedata_len) {
850
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
851
0
                                 "Error parsing PEM: offset out of bounds");
852
0
            goto out;
853
0
        }
854
855
0
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
856
0
            ret = -1;
857
0
            goto out;
858
0
        }
859
0
    } while(strcmp(line, OPENSSH_HEADER_END) != 0);
860
861
0
    if(!b64data)
862
0
        return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
863
0
                              "Error parsing PEM: base 64 data missing");
864
865
0
    ret = _libssh2_openssh_pem_parse_data(session, passphrase, b64data,
866
0
                                          b64datalen, decrypted_buf);
867
868
0
out:
869
0
    if(b64data) {
870
0
        _libssh2_explicit_zero(b64data, b64datalen);
871
0
        LIBSSH2_FREE(session, b64data);
872
0
    }
873
0
    return ret;
874
875
0
}
876
877
static int
878
read_asn1_length(const unsigned char *data,
879
                 size_t datalen, size_t *len)
880
0
{
881
0
    unsigned int lenlen;
882
0
    int nextpos;
883
884
0
    if(datalen < 1) {
885
0
        return -1;
886
0
    }
887
0
    *len = data[0];
888
889
0
    if(*len >= 0x80) {
890
0
        lenlen = *len & 0x7F;
891
0
        *len = data[1];
892
0
        if(1 + lenlen > datalen) {
893
0
            return -1;
894
0
        }
895
0
        if(lenlen > 1) {
896
0
            *len <<= 8;
897
0
            *len |= data[2];
898
0
        }
899
0
    }
900
0
    else {
901
0
        lenlen = 0;
902
0
    }
903
904
0
    nextpos = 1 + lenlen;
905
0
    if(lenlen > 2 || 1 + lenlen + *len > datalen) {
906
0
        return -1;
907
0
    }
908
909
0
    return nextpos;
910
0
}
911
912
int
913
_libssh2_pem_decode_sequence(unsigned char **data, size_t *datalen)
914
0
{
915
0
    size_t len;
916
0
    int lenlen;
917
918
0
    if(*datalen < 1) {
919
0
        return -1;
920
0
    }
921
922
0
    if((*data)[0] != '\x30') {
923
0
        return -1;
924
0
    }
925
926
0
    (*data)++;
927
0
    (*datalen)--;
928
929
0
    lenlen = read_asn1_length(*data, *datalen, &len);
930
0
    if(lenlen < 0 || lenlen + len != *datalen) {
931
0
        return -1;
932
0
    }
933
934
0
    *data += lenlen;
935
0
    *datalen -= lenlen;
936
937
0
    return 0;
938
0
}
939
940
int
941
_libssh2_pem_decode_integer(unsigned char **data, size_t *datalen,
942
                            unsigned char **i, unsigned int *ilen)
943
0
{
944
0
    size_t len;
945
0
    int lenlen;
946
947
0
    if(*datalen < 1) {
948
0
        return -1;
949
0
    }
950
951
0
    if((*data)[0] != '\x02') {
952
0
        return -1;
953
0
    }
954
955
0
    (*data)++;
956
0
    (*datalen)--;
957
958
0
    lenlen = read_asn1_length(*data, *datalen, &len);
959
0
    if(lenlen < 0 || lenlen + len > *datalen) {
960
0
        return -1;
961
0
    }
962
963
0
    *data += lenlen;
964
0
    *datalen -= lenlen;
965
966
0
    *i = *data;
967
0
    *ilen = (unsigned int)len;
968
969
0
    *data += len;
970
0
    *datalen -= len;
971
972
0
    return 0;
973
0
}