xref: /reactos/dll/3rdparty/mbedtls/pem.c (revision 01e5cb0c)
1 /*
2  *  Privacy Enhanced Mail (PEM) decoding
3  *
4  *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
5  *  SPDX-License-Identifier: GPL-2.0
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, write to the Free Software Foundation, Inc.,
19  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  *  This file is part of mbed TLS (https://tls.mbed.org)
22  */
23 
24 #if !defined(MBEDTLS_CONFIG_FILE)
25 #include "mbedtls/config.h"
26 #else
27 #include MBEDTLS_CONFIG_FILE
28 #endif
29 
30 #if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C)
31 
32 #include "mbedtls/pem.h"
33 #include "mbedtls/base64.h"
34 #include "mbedtls/des.h"
35 #include "mbedtls/aes.h"
36 #include "mbedtls/md5.h"
37 #include "mbedtls/cipher.h"
38 
39 #include <string.h>
40 
41 #if defined(MBEDTLS_PLATFORM_C)
42 #include "mbedtls/platform.h"
43 #else
44 #include <stdlib.h>
45 #define mbedtls_calloc    calloc
46 #define mbedtls_free       free
47 #endif
48 
49 #if defined(MBEDTLS_PEM_PARSE_C)
50 /* Implementation that should never be optimized out by the compiler */
51 static void mbedtls_zeroize( void *v, size_t n ) {
52     volatile unsigned char *p = v; while( n-- ) *p++ = 0;
53 }
54 
55 void mbedtls_pem_init( mbedtls_pem_context *ctx )
56 {
57     memset( ctx, 0, sizeof( mbedtls_pem_context ) );
58 }
59 
60 #if defined(MBEDTLS_MD5_C) && defined(MBEDTLS_CIPHER_MODE_CBC) &&         \
61     ( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) )
62 /*
63  * Read a 16-byte hex string and convert it to binary
64  */
65 static int pem_get_iv( const unsigned char *s, unsigned char *iv,
66                        size_t iv_len )
67 {
68     size_t i, j, k;
69 
70     memset( iv, 0, iv_len );
71 
72     for( i = 0; i < iv_len * 2; i++, s++ )
73     {
74         if( *s >= '0' && *s <= '9' ) j = *s - '0'; else
75         if( *s >= 'A' && *s <= 'F' ) j = *s - '7'; else
76         if( *s >= 'a' && *s <= 'f' ) j = *s - 'W'; else
77             return( MBEDTLS_ERR_PEM_INVALID_ENC_IV );
78 
79         k = ( ( i & 1 ) != 0 ) ? j : j << 4;
80 
81         iv[i >> 1] = (unsigned char)( iv[i >> 1] | k );
82     }
83 
84     return( 0 );
85 }
86 
87 static int pem_pbkdf1( unsigned char *key, size_t keylen,
88                        unsigned char *iv,
89                        const unsigned char *pwd, size_t pwdlen )
90 {
91     mbedtls_md5_context md5_ctx;
92     unsigned char md5sum[16];
93     size_t use_len;
94     int ret;
95 
96     mbedtls_md5_init( &md5_ctx );
97 
98     /*
99      * key[ 0..15] = MD5(pwd || IV)
100      */
101     if( ( ret = mbedtls_md5_starts_ret( &md5_ctx ) ) != 0 )
102         goto exit;
103     if( ( ret = mbedtls_md5_update_ret( &md5_ctx, pwd, pwdlen ) ) != 0 )
104         goto exit;
105     if( ( ret = mbedtls_md5_update_ret( &md5_ctx, iv,  8 ) ) != 0 )
106         goto exit;
107     if( ( ret = mbedtls_md5_finish_ret( &md5_ctx, md5sum ) ) != 0 )
108         goto exit;
109 
110     if( keylen <= 16 )
111     {
112         memcpy( key, md5sum, keylen );
113         goto exit;
114     }
115 
116     memcpy( key, md5sum, 16 );
117 
118     /*
119      * key[16..23] = MD5(key[ 0..15] || pwd || IV])
120      */
121     if( ( ret = mbedtls_md5_starts_ret( &md5_ctx ) ) != 0 )
122         goto exit;
123     if( ( ret = mbedtls_md5_update_ret( &md5_ctx, md5sum, 16 ) ) != 0 )
124         goto exit;
125     if( ( ret = mbedtls_md5_update_ret( &md5_ctx, pwd, pwdlen ) ) != 0 )
126         goto exit;
127     if( ( ret = mbedtls_md5_update_ret( &md5_ctx, iv, 8 ) ) != 0 )
128         goto exit;
129     if( ( ret = mbedtls_md5_finish_ret( &md5_ctx, md5sum ) ) != 0 )
130         goto exit;
131 
132     use_len = 16;
133     if( keylen < 32 )
134         use_len = keylen - 16;
135 
136     memcpy( key + 16, md5sum, use_len );
137 
138 exit:
139     mbedtls_md5_free( &md5_ctx );
140     mbedtls_zeroize( md5sum, 16 );
141 
142     return( ret );
143 }
144 
145 #if defined(MBEDTLS_DES_C)
146 /*
147  * Decrypt with DES-CBC, using PBKDF1 for key derivation
148  */
149 static int pem_des_decrypt( unsigned char des_iv[8],
150                             unsigned char *buf, size_t buflen,
151                             const unsigned char *pwd, size_t pwdlen )
152 {
153     mbedtls_des_context des_ctx;
154     unsigned char des_key[8];
155     int ret;
156 
157     mbedtls_des_init( &des_ctx );
158 
159     if( ( ret = pem_pbkdf1( des_key, 8, des_iv, pwd, pwdlen ) ) != 0 )
160         goto exit;
161 
162     if( ( ret = mbedtls_des_setkey_dec( &des_ctx, des_key ) ) != 0 )
163         goto exit;
164     ret = mbedtls_des_crypt_cbc( &des_ctx, MBEDTLS_DES_DECRYPT, buflen,
165                      des_iv, buf, buf );
166 
167 exit:
168     mbedtls_des_free( &des_ctx );
169     mbedtls_zeroize( des_key, 8 );
170 
171     return( ret );
172 }
173 
174 /*
175  * Decrypt with 3DES-CBC, using PBKDF1 for key derivation
176  */
177 static int pem_des3_decrypt( unsigned char des3_iv[8],
178                              unsigned char *buf, size_t buflen,
179                              const unsigned char *pwd, size_t pwdlen )
180 {
181     mbedtls_des3_context des3_ctx;
182     unsigned char des3_key[24];
183     int ret;
184 
185     mbedtls_des3_init( &des3_ctx );
186 
187     if( ( ret = pem_pbkdf1( des3_key, 24, des3_iv, pwd, pwdlen ) ) != 0 )
188         goto exit;
189 
190     if( ( ret = mbedtls_des3_set3key_dec( &des3_ctx, des3_key ) ) != 0 )
191         goto exit;
192     ret = mbedtls_des3_crypt_cbc( &des3_ctx, MBEDTLS_DES_DECRYPT, buflen,
193                      des3_iv, buf, buf );
194 
195 exit:
196     mbedtls_des3_free( &des3_ctx );
197     mbedtls_zeroize( des3_key, 24 );
198 
199     return( ret );
200 }
201 #endif /* MBEDTLS_DES_C */
202 
203 #if defined(MBEDTLS_AES_C)
204 /*
205  * Decrypt with AES-XXX-CBC, using PBKDF1 for key derivation
206  */
207 static int pem_aes_decrypt( unsigned char aes_iv[16], unsigned int keylen,
208                             unsigned char *buf, size_t buflen,
209                             const unsigned char *pwd, size_t pwdlen )
210 {
211     mbedtls_aes_context aes_ctx;
212     unsigned char aes_key[32];
213     int ret;
214 
215     mbedtls_aes_init( &aes_ctx );
216 
217     if( ( ret = pem_pbkdf1( aes_key, keylen, aes_iv, pwd, pwdlen ) ) != 0 )
218         goto exit;
219 
220     if( ( ret = mbedtls_aes_setkey_dec( &aes_ctx, aes_key, keylen * 8 ) ) != 0 )
221         goto exit;
222     ret = mbedtls_aes_crypt_cbc( &aes_ctx, MBEDTLS_AES_DECRYPT, buflen,
223                      aes_iv, buf, buf );
224 
225 exit:
226     mbedtls_aes_free( &aes_ctx );
227     mbedtls_zeroize( aes_key, keylen );
228 
229     return( ret );
230 }
231 #endif /* MBEDTLS_AES_C */
232 
233 #endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC &&
234           ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */
235 
236 int mbedtls_pem_read_buffer( mbedtls_pem_context *ctx, const char *header, const char *footer,
237                      const unsigned char *data, const unsigned char *pwd,
238                      size_t pwdlen, size_t *use_len )
239 {
240     int ret, enc;
241     size_t len;
242     unsigned char *buf;
243     const unsigned char *s1, *s2, *end;
244 #if defined(MBEDTLS_MD5_C) && defined(MBEDTLS_CIPHER_MODE_CBC) &&         \
245     ( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) )
246     unsigned char pem_iv[16];
247     mbedtls_cipher_type_t enc_alg = MBEDTLS_CIPHER_NONE;
248 #else
249     ((void) pwd);
250     ((void) pwdlen);
251 #endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC &&
252           ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */
253 
254     if( ctx == NULL )
255         return( MBEDTLS_ERR_PEM_BAD_INPUT_DATA );
256 
257     s1 = (unsigned char *) strstr( (const char *) data, header );
258 
259     if( s1 == NULL )
260         return( MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT );
261 
262     s2 = (unsigned char *) strstr( (const char *) data, footer );
263 
264     if( s2 == NULL || s2 <= s1 )
265         return( MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT );
266 
267     s1 += strlen( header );
268     if( *s1 == ' '  ) s1++;
269     if( *s1 == '\r' ) s1++;
270     if( *s1 == '\n' ) s1++;
271     else return( MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT );
272 
273     end = s2;
274     end += strlen( footer );
275     if( *end == ' '  ) end++;
276     if( *end == '\r' ) end++;
277     if( *end == '\n' ) end++;
278     *use_len = end - data;
279 
280     enc = 0;
281 
282     if( s2 - s1 >= 22 && memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 )
283     {
284 #if defined(MBEDTLS_MD5_C) && defined(MBEDTLS_CIPHER_MODE_CBC) &&         \
285     ( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) )
286         enc++;
287 
288         s1 += 22;
289         if( *s1 == '\r' ) s1++;
290         if( *s1 == '\n' ) s1++;
291         else return( MBEDTLS_ERR_PEM_INVALID_DATA );
292 
293 
294 #if defined(MBEDTLS_DES_C)
295         if( s2 - s1 >= 23 && memcmp( s1, "DEK-Info: DES-EDE3-CBC,", 23 ) == 0 )
296         {
297             enc_alg = MBEDTLS_CIPHER_DES_EDE3_CBC;
298 
299             s1 += 23;
300             if( s2 - s1 < 16 || pem_get_iv( s1, pem_iv, 8 ) != 0 )
301                 return( MBEDTLS_ERR_PEM_INVALID_ENC_IV );
302 
303             s1 += 16;
304         }
305         else if( s2 - s1 >= 18 && memcmp( s1, "DEK-Info: DES-CBC,", 18 ) == 0 )
306         {
307             enc_alg = MBEDTLS_CIPHER_DES_CBC;
308 
309             s1 += 18;
310             if( s2 - s1 < 16 || pem_get_iv( s1, pem_iv, 8) != 0 )
311                 return( MBEDTLS_ERR_PEM_INVALID_ENC_IV );
312 
313             s1 += 16;
314         }
315 #endif /* MBEDTLS_DES_C */
316 
317 #if defined(MBEDTLS_AES_C)
318         if( s2 - s1 >= 14 && memcmp( s1, "DEK-Info: AES-", 14 ) == 0 )
319         {
320             if( s2 - s1 < 22 )
321                 return( MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG );
322             else if( memcmp( s1, "DEK-Info: AES-128-CBC,", 22 ) == 0 )
323                 enc_alg = MBEDTLS_CIPHER_AES_128_CBC;
324             else if( memcmp( s1, "DEK-Info: AES-192-CBC,", 22 ) == 0 )
325                 enc_alg = MBEDTLS_CIPHER_AES_192_CBC;
326             else if( memcmp( s1, "DEK-Info: AES-256-CBC,", 22 ) == 0 )
327                 enc_alg = MBEDTLS_CIPHER_AES_256_CBC;
328             else
329                 return( MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG );
330 
331             s1 += 22;
332             if( s2 - s1 < 32 || pem_get_iv( s1, pem_iv, 16 ) != 0 )
333                 return( MBEDTLS_ERR_PEM_INVALID_ENC_IV );
334 
335             s1 += 32;
336         }
337 #endif /* MBEDTLS_AES_C */
338 
339         if( enc_alg == MBEDTLS_CIPHER_NONE )
340             return( MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG );
341 
342         if( *s1 == '\r' ) s1++;
343         if( *s1 == '\n' ) s1++;
344         else return( MBEDTLS_ERR_PEM_INVALID_DATA );
345 #else
346         return( MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE );
347 #endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC &&
348           ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */
349     }
350 
351     if( s1 >= s2 )
352         return( MBEDTLS_ERR_PEM_INVALID_DATA );
353 
354     ret = mbedtls_base64_decode( NULL, 0, &len, s1, s2 - s1 );
355 
356     if( ret == MBEDTLS_ERR_BASE64_INVALID_CHARACTER )
357         return( MBEDTLS_ERR_PEM_INVALID_DATA + ret );
358 
359     if( ( buf = mbedtls_calloc( 1, len ) ) == NULL )
360         return( MBEDTLS_ERR_PEM_ALLOC_FAILED );
361 
362     if( ( ret = mbedtls_base64_decode( buf, len, &len, s1, s2 - s1 ) ) != 0 )
363     {
364         mbedtls_zeroize( buf, len );
365         mbedtls_free( buf );
366         return( MBEDTLS_ERR_PEM_INVALID_DATA + ret );
367     }
368 
369     if( enc != 0 )
370     {
371 #if defined(MBEDTLS_MD5_C) && defined(MBEDTLS_CIPHER_MODE_CBC) &&         \
372     ( defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C) )
373         if( pwd == NULL )
374         {
375             mbedtls_zeroize( buf, len );
376             mbedtls_free( buf );
377             return( MBEDTLS_ERR_PEM_PASSWORD_REQUIRED );
378         }
379 
380         ret = 0;
381 
382 #if defined(MBEDTLS_DES_C)
383         if( enc_alg == MBEDTLS_CIPHER_DES_EDE3_CBC )
384             ret = pem_des3_decrypt( pem_iv, buf, len, pwd, pwdlen );
385         else if( enc_alg == MBEDTLS_CIPHER_DES_CBC )
386             ret = pem_des_decrypt( pem_iv, buf, len, pwd, pwdlen );
387 #endif /* MBEDTLS_DES_C */
388 
389 #if defined(MBEDTLS_AES_C)
390         if( enc_alg == MBEDTLS_CIPHER_AES_128_CBC )
391             ret = pem_aes_decrypt( pem_iv, 16, buf, len, pwd, pwdlen );
392         else if( enc_alg == MBEDTLS_CIPHER_AES_192_CBC )
393             ret = pem_aes_decrypt( pem_iv, 24, buf, len, pwd, pwdlen );
394         else if( enc_alg == MBEDTLS_CIPHER_AES_256_CBC )
395             ret = pem_aes_decrypt( pem_iv, 32, buf, len, pwd, pwdlen );
396 #endif /* MBEDTLS_AES_C */
397 
398         if( ret != 0 )
399         {
400             mbedtls_free( buf );
401             return( ret );
402         }
403 
404         /*
405          * The result will be ASN.1 starting with a SEQUENCE tag, with 1 to 3
406          * length bytes (allow 4 to be sure) in all known use cases.
407          *
408          * Use that as heurisitic to try detecting password mismatchs.
409          */
410         if( len <= 2 || buf[0] != 0x30 || buf[1] > 0x83 )
411         {
412             mbedtls_zeroize( buf, len );
413             mbedtls_free( buf );
414             return( MBEDTLS_ERR_PEM_PASSWORD_MISMATCH );
415         }
416 #else
417         mbedtls_zeroize( buf, len );
418         mbedtls_free( buf );
419         return( MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE );
420 #endif /* MBEDTLS_MD5_C && MBEDTLS_CIPHER_MODE_CBC &&
421           ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */
422     }
423 
424     ctx->buf = buf;
425     ctx->buflen = len;
426 
427     return( 0 );
428 }
429 
430 void mbedtls_pem_free( mbedtls_pem_context *ctx )
431 {
432     if( ctx->buf != NULL )
433         mbedtls_zeroize( ctx->buf, ctx->buflen );
434     mbedtls_free( ctx->buf );
435     mbedtls_free( ctx->info );
436 
437     mbedtls_zeroize( ctx, sizeof( mbedtls_pem_context ) );
438 }
439 #endif /* MBEDTLS_PEM_PARSE_C */
440 
441 #if defined(MBEDTLS_PEM_WRITE_C)
442 int mbedtls_pem_write_buffer( const char *header, const char *footer,
443                       const unsigned char *der_data, size_t der_len,
444                       unsigned char *buf, size_t buf_len, size_t *olen )
445 {
446     int ret;
447     unsigned char *encode_buf = NULL, *c, *p = buf;
448     size_t len = 0, use_len, add_len = 0;
449 
450     mbedtls_base64_encode( NULL, 0, &use_len, der_data, der_len );
451     add_len = strlen( header ) + strlen( footer ) + ( use_len / 64 ) + 1;
452 
453     if( use_len + add_len > buf_len )
454     {
455         *olen = use_len + add_len;
456         return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
457     }
458 
459     if( use_len != 0 &&
460         ( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL ) )
461         return( MBEDTLS_ERR_PEM_ALLOC_FAILED );
462 
463     if( ( ret = mbedtls_base64_encode( encode_buf, use_len, &use_len, der_data,
464                                der_len ) ) != 0 )
465     {
466         mbedtls_free( encode_buf );
467         return( ret );
468     }
469 
470     memcpy( p, header, strlen( header ) );
471     p += strlen( header );
472     c = encode_buf;
473 
474     while( use_len )
475     {
476         len = ( use_len > 64 ) ? 64 : use_len;
477         memcpy( p, c, len );
478         use_len -= len;
479         p += len;
480         c += len;
481         *p++ = '\n';
482     }
483 
484     memcpy( p, footer, strlen( footer ) );
485     p += strlen( footer );
486 
487     *p++ = '\0';
488     *olen = p - buf;
489 
490     mbedtls_free( encode_buf );
491     return( 0 );
492 }
493 #endif /* MBEDTLS_PEM_WRITE_C */
494 #endif /* MBEDTLS_PEM_PARSE_C || MBEDTLS_PEM_WRITE_C */
495