Coverage Report

Created: 2023-11-19 06:28

/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
    }
130
0
    while(strcmp(line, headerbegin) != 0);
131
132
0
    if(readline(line, LINE_SIZE, fp)) {
133
0
        return -1;
134
0
    }
135
136
0
    if(passphrase &&
137
0
            memcmp(line, crypt_annotation, strlen(crypt_annotation)) == 0) {
138
0
        const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
139
0
        int i;
140
141
0
        if(readline(line, LINE_SIZE, fp)) {
142
0
            ret = -1;
143
0
            goto out;
144
0
        }
145
146
0
        all_methods = libssh2_crypt_methods();
147
        /* !checksrc! disable EQUALSNULL 1 */
148
0
        while((cur_method = *all_methods++) != NULL) {
149
0
            if(*cur_method->pem_annotation &&
150
0
                    memcmp(line, cur_method->pem_annotation,
151
0
                           strlen(cur_method->pem_annotation)) == 0) {
152
0
                method = cur_method;
153
0
                memcpy(iv, line + strlen(method->pem_annotation) + 1,
154
0
                       2*method->iv_len);
155
0
            }
156
0
        }
157
158
        /* None of the available crypt methods were able to decrypt the key */
159
0
        if(!method)
160
0
            return -1;
161
162
        /* Decode IV from hex */
163
0
        for(i = 0; i < method->iv_len; ++i) {
164
0
            iv[i]  = (unsigned char)(hex_decode(iv[2*i]) << 4);
165
0
            iv[i] |= hex_decode(iv[2*i + 1]);
166
0
        }
167
168
        /* skip to the next line */
169
0
        if(readline(line, LINE_SIZE, fp)) {
170
0
            ret = -1;
171
0
            goto out;
172
0
        }
173
0
    }
174
175
0
    do {
176
0
        if(*line) {
177
0
            char *tmp;
178
0
            size_t linelen;
179
180
0
            linelen = strlen(line);
181
0
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
182
0
            if(!tmp) {
183
0
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
184
0
                               "Unable to allocate memory for PEM parsing");
185
0
                ret = -1;
186
0
                goto out;
187
0
            }
188
0
            memcpy(tmp + b64datalen, line, linelen);
189
0
            b64data = tmp;
190
0
            b64datalen += linelen;
191
0
        }
192
193
0
        *line = '\0';
194
195
0
        if(readline(line, LINE_SIZE, fp)) {
196
0
            ret = -1;
197
0
            goto out;
198
0
        }
199
0
    } while(strcmp(line, headerend) != 0);
200
201
0
    if(!b64data) {
202
0
        return -1;
203
0
    }
204
205
0
    if(_libssh2_base64_decode(session, (char **) data, datalen,
206
0
                              b64data, b64datalen)) {
207
0
        ret = -1;
208
0
        goto out;
209
0
    }
210
211
0
    if(method) {
212
0
#if LIBSSH2_MD5_PEM
213
        /* Set up decryption */
214
0
        int free_iv = 0, free_secret = 0, len_decrypted = 0, padding = 0;
215
0
        int blocksize = method->blocksize;
216
0
        void *abstract;
217
0
        unsigned char secret[2*MD5_DIGEST_LENGTH];
218
0
        libssh2_md5_ctx fingerprint_ctx;
219
220
        /* Perform key derivation (PBKDF1/MD5) */
221
0
        if(!libssh2_md5_init(&fingerprint_ctx)) {
222
0
            ret = -1;
223
0
            goto out;
224
0
        }
225
0
        libssh2_md5_update(fingerprint_ctx, passphrase,
226
0
                           strlen((char *)passphrase));
227
0
        libssh2_md5_update(fingerprint_ctx, iv, 8);
228
0
        libssh2_md5_final(fingerprint_ctx, secret);
229
0
        if(method->secret_len > MD5_DIGEST_LENGTH) {
230
0
            if(!libssh2_md5_init(&fingerprint_ctx)) {
231
0
                ret = -1;
232
0
                goto out;
233
0
            }
234
0
            libssh2_md5_update(fingerprint_ctx, secret, MD5_DIGEST_LENGTH);
235
0
            libssh2_md5_update(fingerprint_ctx, passphrase,
236
0
                               strlen((char *)passphrase));
237
0
            libssh2_md5_update(fingerprint_ctx, iv, 8);
238
0
            libssh2_md5_final(fingerprint_ctx, secret + MD5_DIGEST_LENGTH);
239
0
        }
240
241
        /* Initialize the decryption */
242
0
        if(method->init(session, method, iv, &free_iv, secret,
243
0
                         &free_secret, 0, &abstract)) {
244
0
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
245
0
            LIBSSH2_FREE(session, data);
246
0
            ret = -1;
247
0
            goto out;
248
0
        }
249
250
0
        if(free_secret) {
251
0
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
252
0
        }
253
254
        /* Do the actual decryption */
255
0
        if((*datalen % blocksize) != 0) {
256
0
            _libssh2_explicit_zero((char *)secret, sizeof(secret));
257
0
            method->dtor(session, &abstract);
258
0
            _libssh2_explicit_zero(*data, *datalen);
259
0
            LIBSSH2_FREE(session, *data);
260
0
            ret = -1;
261
0
            goto out;
262
0
        }
263
264
0
        while(len_decrypted <= (int)*datalen - blocksize) {
265
0
            if(method->crypt(session, *data + len_decrypted, blocksize,
266
0
                             &abstract,
267
0
                             len_decrypted == 0 ? FIRST_BLOCK :
268
0
                               ((len_decrypted == (int)*datalen - blocksize) ?
269
0
                              LAST_BLOCK : MIDDLE_BLOCK)
270
0
                             )) {
271
0
                ret = LIBSSH2_ERROR_DECRYPT;
272
0
                _libssh2_explicit_zero((char *)secret, sizeof(secret));
273
0
                method->dtor(session, &abstract);
274
0
                _libssh2_explicit_zero(*data, *datalen);
275
0
                LIBSSH2_FREE(session, *data);
276
0
                goto out;
277
0
            }
278
279
0
            len_decrypted += blocksize;
280
0
        }
281
282
        /* Account for padding */
283
0
        padding = (*data)[*datalen - 1];
284
0
        memset(&(*data)[*datalen-padding], 0, padding);
285
0
        *datalen -= padding;
286
287
        /* Clean up */
288
0
        _libssh2_explicit_zero((char *)secret, sizeof(secret));
289
0
        method->dtor(session, &abstract);
290
#else
291
        ret = -1;
292
        goto out;
293
#endif
294
0
    }
295
296
0
    ret = 0;
297
0
out:
298
0
    if(b64data) {
299
0
        _libssh2_explicit_zero(b64data, b64datalen);
300
0
        LIBSSH2_FREE(session, b64data);
301
0
    }
302
0
    return ret;
303
0
}
304
305
int
306
_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
307
                          const char *headerbegin,
308
                          const char *headerend,
309
                          const char *filedata, size_t filedata_len,
310
                          unsigned char **data, size_t *datalen)
311
0
{
312
0
    char line[LINE_SIZE];
313
0
    char *b64data = NULL;
314
0
    size_t b64datalen = 0;
315
0
    size_t off = 0;
316
0
    int ret;
317
318
0
    do {
319
0
        *line = '\0';
320
321
0
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
322
0
            return -1;
323
0
        }
324
0
    }
325
0
    while(strcmp(line, headerbegin) != 0);
326
327
0
    *line = '\0';
328
329
0
    do {
330
0
        if(*line) {
331
0
            char *tmp;
332
0
            size_t linelen;
333
334
0
            linelen = strlen(line);
335
0
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
336
0
            if(!tmp) {
337
0
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
338
0
                               "Unable to allocate memory for PEM parsing");
339
0
                ret = -1;
340
0
                goto out;
341
0
            }
342
0
            memcpy(tmp + b64datalen, line, linelen);
343
0
            b64data = tmp;
344
0
            b64datalen += linelen;
345
0
        }
346
347
0
        *line = '\0';
348
349
0
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
350
0
            ret = -1;
351
0
            goto out;
352
0
        }
353
0
    } while(strcmp(line, headerend) != 0);
354
355
0
    if(!b64data) {
356
0
        return -1;
357
0
    }
358
359
0
    if(_libssh2_base64_decode(session, (char **) data, datalen,
360
0
                              b64data, b64datalen)) {
361
0
        ret = -1;
362
0
        goto out;
363
0
    }
364
365
0
    ret = 0;
366
0
out:
367
0
    if(b64data) {
368
0
        _libssh2_explicit_zero(b64data, b64datalen);
369
0
        LIBSSH2_FREE(session, b64data);
370
0
    }
371
0
    return ret;
372
0
}
373
374
/* OpenSSH formatted keys */
375
0
#define AUTH_MAGIC "openssh-key-v1"
376
0
#define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
377
0
#define OPENSSH_HEADER_END "-----END OPENSSH PRIVATE KEY-----"
378
379
static int
380
_libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,
381
                                const unsigned char *passphrase,
382
                                const char *b64data, size_t b64datalen,
383
                                struct string_buf **decrypted_buf)
384
0
{
385
0
    const LIBSSH2_CRYPT_METHOD *method = NULL;
386
0
    struct string_buf decoded, decrypted, kdf_buf;
387
0
    unsigned char *ciphername = NULL;
388
0
    unsigned char *kdfname = NULL;
389
0
    unsigned char *kdf = NULL;
390
0
    unsigned char *buf = NULL;
391
0
    unsigned char *salt = NULL;
392
0
    uint32_t nkeys, check1, check2;
393
0
    uint32_t rounds = 0;
394
0
    unsigned char *key = NULL;
395
0
    unsigned char *key_part = NULL;
396
0
    unsigned char *iv_part = NULL;
397
0
    unsigned char *f = NULL;
398
0
    size_t f_len = 0;
399
0
    int ret = 0, keylen = 0, ivlen = 0, total_len = 0;
400
0
    size_t kdf_len = 0, tmp_len = 0, salt_len = 0;
401
402
0
    if(decrypted_buf)
403
0
        *decrypted_buf = NULL;
404
405
    /* decode file */
406
0
    if(_libssh2_base64_decode(session, (char **)&f, &f_len,
407
0
                              b64data, b64datalen)) {
408
0
        ret = -1;
409
0
        goto out;
410
0
    }
411
412
    /* Parse the file */
413
0
    decoded.data = (unsigned char *)f;
414
0
    decoded.dataptr = (unsigned char *)f;
415
0
    decoded.len = f_len;
416
417
0
    if(decoded.len < strlen(AUTH_MAGIC)) {
418
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, "key too short");
419
0
        goto out;
420
0
    }
421
422
0
    if(strncmp((char *) decoded.dataptr, AUTH_MAGIC,
423
0
               strlen(AUTH_MAGIC)) != 0) {
424
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
425
0
                             "key auth magic mismatch");
426
0
        goto out;
427
0
    }
428
429
0
    decoded.dataptr += strlen(AUTH_MAGIC) + 1;
430
431
0
    if(_libssh2_get_string(&decoded, &ciphername, &tmp_len) ||
432
0
       tmp_len == 0) {
433
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
434
0
                             "ciphername is missing");
435
0
        goto out;
436
0
    }
437
438
0
    if(_libssh2_get_string(&decoded, &kdfname, &tmp_len) ||
439
0
       tmp_len == 0) {
440
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
441
0
                             "kdfname is missing");
442
0
        goto out;
443
0
    }
444
445
0
    if(_libssh2_get_string(&decoded, &kdf, &kdf_len)) {
446
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
447
0
                             "kdf is missing");
448
0
        goto out;
449
0
    }
450
0
    else {
451
0
        kdf_buf.data = kdf;
452
0
        kdf_buf.dataptr = kdf;
453
0
        kdf_buf.len = kdf_len;
454
0
    }
455
456
0
    if((!passphrase || strlen((const char *)passphrase) == 0) &&
457
0
        strcmp((const char *)ciphername, "none") != 0) {
458
        /* passphrase required */
459
0
        ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
460
0
        goto out;
461
0
    }
462
463
0
    if(strcmp((const char *)kdfname, "none") != 0 &&
464
0
       strcmp((const char *)kdfname, "bcrypt") != 0) {
465
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
466
0
                             "unknown cipher");
467
0
        goto out;
468
0
    }
469
470
0
    if(!strcmp((const char *)kdfname, "none") &&
471
0
       strcmp((const char *)ciphername, "none") != 0) {
472
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
473
0
                             "invalid format");
474
0
        goto out;
475
0
    }
476
477
0
    if(_libssh2_get_u32(&decoded, &nkeys) != 0 || nkeys != 1) {
478
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
479
0
                             "Multiple keys are unsupported");
480
0
        goto out;
481
0
    }
482
483
    /* unencrypted public key */
484
485
0
    if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
486
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
487
0
                             "Invalid private key; "
488
0
                             "expect embedded public key");
489
0
        goto out;
490
0
    }
491
492
0
    if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
493
0
        ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
494
0
                             "Private key data not found");
495
0
        goto out;
496
0
    }
497
498
    /* decode encrypted private key */
499
0
    decrypted.data = decrypted.dataptr = buf;
500
0
    decrypted.len = tmp_len;
501
502
0
    if(ciphername && strcmp((const char *)ciphername, "none") != 0) {
503
0
        const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
504
505
0
        all_methods = libssh2_crypt_methods();
506
        /* !checksrc! disable EQUALSNULL 1 */
507
0
        while((cur_method = *all_methods++) != NULL) {
508
0
            if(*cur_method->name &&
509
0
                memcmp(ciphername, cur_method->name,
510
0
                       strlen(cur_method->name)) == 0) {
511
0
                    method = cur_method;
512
0
                }
513
0
        }
514
515
        /* None of the available crypt methods were able to decrypt the key */
516
517
0
        if(!method) {
518
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
519
0
                                 "No supported cipher found");
520
0
            goto out;
521
0
        }
522
0
    }
523
524
0
    if(method) {
525
0
        int free_iv = 0, free_secret = 0, len_decrypted = 0;
526
0
        int blocksize;
527
0
        void *abstract = NULL;
528
529
0
        keylen = method->secret_len;
530
0
        ivlen = method->iv_len;
531
0
        total_len = keylen + ivlen;
532
533
0
        key = LIBSSH2_CALLOC(session, total_len);
534
0
        if(!key) {
535
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
536
0
                                 "Could not alloc key");
537
0
            goto out;
538
0
        }
539
540
0
        if(strcmp((const char *)kdfname, "bcrypt") == 0 && passphrase) {
541
0
            if((_libssh2_get_string(&kdf_buf, &salt, &salt_len)) ||
542
0
                (_libssh2_get_u32(&kdf_buf, &rounds) != 0)) {
543
0
                ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
544
0
                                     "kdf contains unexpected values");
545
0
                LIBSSH2_FREE(session, key);
546
0
                goto out;
547
0
            }
548
549
0
            if(_libssh2_bcrypt_pbkdf((const char *)passphrase,
550
0
                                     strlen((const char *)passphrase),
551
0
                                     salt, salt_len, key,
552
0
                                     keylen + ivlen, rounds) < 0) {
553
0
                ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
554
0
                                     "invalid format");
555
0
                LIBSSH2_FREE(session, key);
556
0
                goto out;
557
0
            }
558
0
        }
559
0
        else {
560
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_KEYFILE_AUTH_FAILED,
561
0
                                 "bcrypted without passphrase");
562
0
            LIBSSH2_FREE(session, key);
563
0
            goto out;
564
0
        }
565
566
        /* Set up decryption */
567
0
        blocksize = method->blocksize;
568
569
0
        key_part = LIBSSH2_CALLOC(session, keylen);
570
0
        if(!key_part) {
571
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
572
0
                                 "Could not alloc key part");
573
0
            goto out;
574
0
        }
575
576
0
        iv_part = LIBSSH2_CALLOC(session, ivlen);
577
0
        if(!iv_part) {
578
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
579
0
                                 "Could not alloc iv part");
580
0
            goto out;
581
0
        }
582
583
0
        memcpy(key_part, key, keylen);
584
0
        memcpy(iv_part, key + keylen, ivlen);
585
586
        /* Initialize the decryption */
587
0
        if(method->init(session, method, iv_part, &free_iv, key_part,
588
0
                        &free_secret, 0, &abstract)) {
589
0
            ret = LIBSSH2_ERROR_DECRYPT;
590
0
            goto out;
591
0
        }
592
593
        /* Do the actual decryption */
594
0
        if((decrypted.len % blocksize) != 0) {
595
0
            method->dtor(session, &abstract);
596
0
            ret = LIBSSH2_ERROR_DECRYPT;
597
0
            goto out;
598
0
        }
599
600
0
        while((size_t)len_decrypted <= decrypted.len - blocksize) {
601
            /* We always pass MIDDLE_BLOCK here because OpenSSH Key Files
602
             * do not use AAD to authenticate the length.
603
             * Furthermore, the authentication tag is appended after the
604
             * encrypted key, and the length of the authentication tag is
605
             * not included in the key length, so we check it after the
606
             * loop.
607
             */
608
0
            if(method->crypt(session, decrypted.data + len_decrypted,
609
0
                             blocksize,
610
0
                             &abstract,
611
0
                             MIDDLE_BLOCK)) {
612
0
                ret = LIBSSH2_ERROR_DECRYPT;
613
0
                method->dtor(session, &abstract);
614
0
                goto out;
615
0
            }
616
617
0
            len_decrypted += blocksize;
618
0
        }
619
620
        /* No padding */
621
622
        /* for the AES GCM methods, the 16 byte authentication tag is
623
         * appended to the encrypted key */
624
0
        if(strcmp(method->name, "aes256-gcm@openssh.com") == 0 ||
625
0
           strcmp(method->name, "aes128-gcm@openssh.com") == 0) {
626
0
            if(!_libssh2_check_length(&decoded, 16)) {
627
0
                ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
628
0
                                     "GCM auth tag missing");
629
0
                method->dtor(session, &abstract);
630
0
                goto out;
631
0
            }
632
0
            if(method->crypt(session, decoded.dataptr, 16, &abstract,
633
0
                             LAST_BLOCK)) {
634
0
                ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
635
0
                                     "GCM auth tag invalid");
636
0
                method->dtor(session, &abstract);
637
0
                goto out;
638
0
            }
639
0
            decoded.dataptr += 16;
640
0
        }
641
642
0
        method->dtor(session, &abstract);
643
0
    }
644
645
    /* Check random bytes match */
646
647
0
    if(_libssh2_get_u32(&decrypted, &check1) != 0 ||
648
0
       _libssh2_get_u32(&decrypted, &check2) != 0 ||
649
0
       check1 != check2) {
650
0
        _libssh2_error(session, LIBSSH2_ERROR_PROTO,
651
0
                       "Private key unpack failed (correct password?)");
652
0
        ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
653
0
        goto out;
654
0
    }
655
656
0
    if(decrypted_buf) {
657
        /* copy data to out-going buffer */
658
0
        struct string_buf *out_buf = _libssh2_string_buf_new(session);
659
0
        if(!out_buf) {
660
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
661
0
                                 "Unable to allocate memory for "
662
0
                                 "decrypted struct");
663
0
            goto out;
664
0
        }
665
666
0
        out_buf->data = LIBSSH2_CALLOC(session, decrypted.len);
667
0
        if(!out_buf->data) {
668
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
669
0
                                 "Unable to allocate memory for "
670
0
                                 "decrypted struct");
671
0
            _libssh2_string_buf_free(session, out_buf);
672
0
            goto out;
673
0
        }
674
0
        memcpy(out_buf->data, decrypted.data, decrypted.len);
675
0
        out_buf->dataptr = out_buf->data +
676
0
            (decrypted.dataptr - decrypted.data);
677
0
        out_buf->len = decrypted.len;
678
679
0
        *decrypted_buf = out_buf;
680
0
    }
681
682
0
out:
683
684
    /* Clean up */
685
0
    if(key) {
686
0
        _libssh2_explicit_zero(key, total_len);
687
0
        LIBSSH2_FREE(session, key);
688
0
    }
689
0
    if(key_part) {
690
0
        _libssh2_explicit_zero(key_part, keylen);
691
0
        LIBSSH2_FREE(session, key_part);
692
0
    }
693
0
    if(iv_part) {
694
0
        _libssh2_explicit_zero(iv_part, ivlen);
695
0
        LIBSSH2_FREE(session, iv_part);
696
0
    }
697
0
    if(f) {
698
0
        _libssh2_explicit_zero(f, f_len);
699
0
        LIBSSH2_FREE(session, f);
700
0
    }
701
702
0
    return ret;
703
0
}
704
705
int
706
_libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,
707
                           const unsigned char *passphrase,
708
                           FILE * fp, struct string_buf **decrypted_buf)
709
0
{
710
0
    char line[LINE_SIZE];
711
0
    char *b64data = NULL;
712
0
    size_t b64datalen = 0;
713
0
    int ret = 0;
714
715
    /* read file */
716
717
0
    do {
718
0
        *line = '\0';
719
720
0
        if(readline(line, LINE_SIZE, fp)) {
721
0
            return -1;
722
0
        }
723
0
    }
724
0
    while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
725
726
0
    if(readline(line, LINE_SIZE, fp)) {
727
0
        return -1;
728
0
    }
729
730
0
    do {
731
0
        if(*line) {
732
0
            char *tmp;
733
0
            size_t linelen;
734
735
0
            linelen = strlen(line);
736
0
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
737
0
            if(!tmp) {
738
0
                _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
739
0
                               "Unable to allocate memory for PEM parsing");
740
0
                ret = -1;
741
0
                goto out;
742
0
            }
743
0
            memcpy(tmp + b64datalen, line, linelen);
744
0
            b64data = tmp;
745
0
            b64datalen += linelen;
746
0
        }
747
748
0
        *line = '\0';
749
750
0
        if(readline(line, LINE_SIZE, fp)) {
751
0
            ret = -1;
752
0
            goto out;
753
0
        }
754
0
    } while(strcmp(line, OPENSSH_HEADER_END) != 0);
755
756
0
    if(!b64data) {
757
0
        return -1;
758
0
    }
759
760
0
    ret = _libssh2_openssh_pem_parse_data(session,
761
0
                                          passphrase,
762
0
                                          (const char *)b64data,
763
0
                                          b64datalen,
764
0
                                          decrypted_buf);
765
766
0
    if(b64data) {
767
0
        _libssh2_explicit_zero(b64data, b64datalen);
768
0
        LIBSSH2_FREE(session, b64data);
769
0
    }
770
771
0
out:
772
773
0
    return ret;
774
0
}
775
776
int
777
_libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,
778
                                  const unsigned char *passphrase,
779
                                  const char *filedata, size_t filedata_len,
780
                                  struct string_buf **decrypted_buf)
781
0
{
782
0
    char line[LINE_SIZE];
783
0
    char *b64data = NULL;
784
0
    size_t b64datalen = 0;
785
0
    size_t off = 0;
786
0
    int ret;
787
788
0
    if(!filedata || filedata_len <= 0)
789
0
        return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
790
0
                              "Error parsing PEM: filedata missing");
791
792
0
    do {
793
794
0
        *line = '\0';
795
796
0
        if(off >= filedata_len)
797
0
            return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
798
0
                                  "Error parsing PEM: "
799
0
                                  "OpenSSH header not found");
800
801
0
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
802
0
            return -1;
803
0
        }
804
0
    }
805
0
    while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
806
807
0
    *line = '\0';
808
809
0
    do {
810
0
        if(*line) {
811
0
            char *tmp;
812
0
            size_t linelen;
813
814
0
            linelen = strlen(line);
815
0
            tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
816
0
            if(!tmp) {
817
0
                ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
818
0
                                     "Unable to allocate memory for "
819
0
                                     "PEM parsing");
820
0
                goto out;
821
0
            }
822
0
            memcpy(tmp + b64datalen, line, linelen);
823
0
            b64data = tmp;
824
0
            b64datalen += linelen;
825
0
        }
826
827
0
        *line = '\0';
828
829
0
        if(off >= filedata_len) {
830
0
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
831
0
                                 "Error parsing PEM: offset out of bounds");
832
0
            goto out;
833
0
        }
834
835
0
        if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
836
0
            ret = -1;
837
0
            goto out;
838
0
        }
839
0
    } while(strcmp(line, OPENSSH_HEADER_END) != 0);
840
841
0
    if(!b64data)
842
0
        return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
843
0
                              "Error parsing PEM: base 64 data missing");
844
845
0
    ret = _libssh2_openssh_pem_parse_data(session, passphrase, b64data,
846
0
                                          b64datalen, decrypted_buf);
847
848
0
out:
849
0
    if(b64data) {
850
0
        _libssh2_explicit_zero(b64data, b64datalen);
851
0
        LIBSSH2_FREE(session, b64data);
852
0
    }
853
0
    return ret;
854
855
0
}
856
857
static int
858
read_asn1_length(const unsigned char *data,
859
                 size_t datalen, size_t *len)
860
0
{
861
0
    unsigned int lenlen;
862
0
    int nextpos;
863
864
0
    if(datalen < 1) {
865
0
        return -1;
866
0
    }
867
0
    *len = data[0];
868
869
0
    if(*len >= 0x80) {
870
0
        lenlen = *len & 0x7F;
871
0
        *len = data[1];
872
0
        if(1 + lenlen > datalen) {
873
0
            return -1;
874
0
        }
875
0
        if(lenlen > 1) {
876
0
            *len <<= 8;
877
0
            *len |= data[2];
878
0
        }
879
0
    }
880
0
    else {
881
0
        lenlen = 0;
882
0
    }
883
884
0
    nextpos = 1 + lenlen;
885
0
    if(lenlen > 2 || 1 + lenlen + *len > datalen) {
886
0
        return -1;
887
0
    }
888
889
0
    return nextpos;
890
0
}
891
892
int
893
_libssh2_pem_decode_sequence(unsigned char **data, size_t *datalen)
894
0
{
895
0
    size_t len;
896
0
    int lenlen;
897
898
0
    if(*datalen < 1) {
899
0
        return -1;
900
0
    }
901
902
0
    if((*data)[0] != '\x30') {
903
0
        return -1;
904
0
    }
905
906
0
    (*data)++;
907
0
    (*datalen)--;
908
909
0
    lenlen = read_asn1_length(*data, *datalen, &len);
910
0
    if(lenlen < 0 || lenlen + len != *datalen) {
911
0
        return -1;
912
0
    }
913
914
0
    *data += lenlen;
915
0
    *datalen -= lenlen;
916
917
0
    return 0;
918
0
}
919
920
int
921
_libssh2_pem_decode_integer(unsigned char **data, size_t *datalen,
922
                            unsigned char **i, unsigned int *ilen)
923
0
{
924
0
    size_t len;
925
0
    int lenlen;
926
927
0
    if(*datalen < 1) {
928
0
        return -1;
929
0
    }
930
931
0
    if((*data)[0] != '\x02') {
932
0
        return -1;
933
0
    }
934
935
0
    (*data)++;
936
0
    (*datalen)--;
937
938
0
    lenlen = read_asn1_length(*data, *datalen, &len);
939
0
    if(lenlen < 0 || lenlen + len > *datalen) {
940
0
        return -1;
941
0
    }
942
943
0
    *data += lenlen;
944
0
    *datalen -= lenlen;
945
946
0
    *i = *data;
947
0
    *ilen = (unsigned int)len;
948
949
0
    *data += len;
950
0
    *datalen -= len;
951
952
0
    return 0;
953
0
}