Improve and document low-level PEM read routines
[openssl.git] / crypto / pem / pem_lib.c
index 4d4e8864968b45adb93a57c6a6d9be72b63b556b..42b46dc4d5f059b44116583b012f64490e7b6563 100644 (file)
@@ -1,64 +1,16 @@
-/* crypto/pem/pem_lib.c */
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
  *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
  */
 
 #include <stdio.h>
 #include <ctype.h>
-#include "cryptlib.h"
+#include <string.h>
+#include "internal/cryptlib.h"
 #include <openssl/buffer.h>
 #include <openssl/objects.h>
 #include <openssl/evp.h>
 #include <openssl/x509.h>
 #include <openssl/pem.h>
 #include <openssl/pkcs12.h>
-#include "asn1_locl.h"
-#ifndef OPENSSL_NO_DES
-# include <openssl/des.h>
-#endif
-#ifndef OPENSSL_NO_ENGINE
-# include <openssl/engine.h>
-#endif
-
-const char PEM_version[] = "PEM" OPENSSL_VERSION_PTEXT;
+#include "internal/asn1_int.h"
+#include <openssl/des.h>
+#include <openssl/engine.h>
 
 #define MIN_LENGTH      4
 
@@ -84,7 +30,7 @@ int pem_check_suffix(const char *pem_str, const char *suffix);
 
 int PEM_def_callback(char *buf, int num, int w, void *key)
 {
-#ifdef OPENSSL_NO_STDIO
+#if defined(OPENSSL_NO_STDIO) || defined(OPENSSL_NO_UI)
     /*
      * We should not ever call the default callback routine from windows.
      */
@@ -105,17 +51,23 @@ int PEM_def_callback(char *buf, int num, int w, void *key)
         prompt = "Enter PEM pass phrase:";
 
     for (;;) {
-        i = EVP_read_pw_string_min(buf, MIN_LENGTH, num, prompt, w);
+        /*
+         * We assume that w == 0 means decryption,
+         * while w == 1 means encryption
+         */
+        int min_len = w ? MIN_LENGTH : 0;
+
+        i = EVP_read_pw_string_min(buf, min_len, num, prompt, w);
         if (i != 0) {
             PEMerr(PEM_F_PEM_DEF_CALLBACK, PEM_R_PROBLEMS_GETTING_PASSWORD);
             memset(buf, 0, (unsigned int)num);
             return (-1);
         }
         j = strlen(buf);
-        if (j < MIN_LENGTH) {
+        if (min_len && j < min_len) {
             fprintf(stderr,
                     "phrase is too short, needs to be at least %d chars\n",
-                    MIN_LENGTH);
+                    min_len);
         } else
             break;
     }
@@ -136,9 +88,9 @@ void PEM_proc_type(char *buf, int type)
     else
         str = "BAD-TYPE";
 
-    BUF_strlcat(buf, "Proc-Type: 4,", PEM_BUFSIZE);
-    BUF_strlcat(buf, str, PEM_BUFSIZE);
-    BUF_strlcat(buf, "\n", PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, "Proc-Type: 4,", PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, str, PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, "\n", PEM_BUFSIZE);
 }
 
 void PEM_dek_info(char *buf, const char *type, int len, char *str)
@@ -147,9 +99,9 @@ void PEM_dek_info(char *buf, const char *type, int len, char *str)
     long i;
     int j;
 
-    BUF_strlcat(buf, "DEK-Info: ", PEM_BUFSIZE);
-    BUF_strlcat(buf, type, PEM_BUFSIZE);
-    BUF_strlcat(buf, ",", PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, "DEK-Info: ", PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, type, PEM_BUFSIZE);
+    OPENSSL_strlcat(buf, ",", PEM_BUFSIZE);
     j = strlen(buf);
     if (j + (len * 2) + 1 > PEM_BUFSIZE)
         return;
@@ -182,17 +134,17 @@ void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
 static int check_pem(const char *nm, const char *name)
 {
     /* Normal matching nm and name */
-    if (!strcmp(nm, name))
+    if (strcmp(nm, name) == 0)
         return 1;
 
     /* Make PEM_STRING_EVP_PKEY match any private key */
 
-    if (!strcmp(name, PEM_STRING_EVP_PKEY)) {
+    if (strcmp(name, PEM_STRING_EVP_PKEY) == 0) {
         int slen;
         const EVP_PKEY_ASN1_METHOD *ameth;
-        if (!strcmp(nm, PEM_STRING_PKCS8))
+        if (strcmp(nm, PEM_STRING_PKCS8) == 0)
             return 1;
-        if (!strcmp(nm, PEM_STRING_PKCS8INF))
+        if (strcmp(nm, PEM_STRING_PKCS8INF) == 0)
             return 1;
         slen = pem_check_suffix(nm, "PRIVATE KEY");
         if (slen > 0) {
@@ -207,7 +159,7 @@ static int check_pem(const char *nm, const char *name)
         return 0;
     }
 
-    if (!strcmp(name, PEM_STRING_PARAMETERS)) {
+    if (strcmp(name, PEM_STRING_PARAMETERS) == 0) {
         int slen;
         const EVP_PKEY_ASN1_METHOD *ameth;
         slen = pem_check_suffix(nm, "PARAMETERS");
@@ -221,8 +173,7 @@ static int check_pem(const char *nm, const char *name)
                 else
                     r = 0;
 #ifndef OPENSSL_NO_ENGINE
-                if (e)
-                    ENGINE_finish(e);
+                ENGINE_finish(e);
 #endif
                 return r;
             }
@@ -230,41 +181,45 @@ static int check_pem(const char *nm, const char *name)
         return 0;
     }
     /* If reading DH parameters handle X9.42 DH format too */
-    if (!strcmp(nm, PEM_STRING_DHXPARAMS) &&
-        !strcmp(name, PEM_STRING_DHPARAMS))
+    if (strcmp(nm, PEM_STRING_DHXPARAMS) == 0
+        && strcmp(name, PEM_STRING_DHPARAMS) == 0)
         return 1;
 
     /* Permit older strings */
 
-    if (!strcmp(nm, PEM_STRING_X509_OLD) && !strcmp(name, PEM_STRING_X509))
+    if (strcmp(nm, PEM_STRING_X509_OLD) == 0
+        && strcmp(name, PEM_STRING_X509) == 0)
         return 1;
 
-    if (!strcmp(nm, PEM_STRING_X509_REQ_OLD) &&
-        !strcmp(name, PEM_STRING_X509_REQ))
+    if (strcmp(nm, PEM_STRING_X509_REQ_OLD) == 0
+        && strcmp(name, PEM_STRING_X509_REQ) == 0)
         return 1;
 
     /* Allow normal certs to be read as trusted certs */
-    if (!strcmp(nm, PEM_STRING_X509) &&
-        !strcmp(name, PEM_STRING_X509_TRUSTED))
+    if (strcmp(nm, PEM_STRING_X509) == 0
+        && strcmp(name, PEM_STRING_X509_TRUSTED) == 0)
         return 1;
 
-    if (!strcmp(nm, PEM_STRING_X509_OLD) &&
-        !strcmp(name, PEM_STRING_X509_TRUSTED))
+    if (strcmp(nm, PEM_STRING_X509_OLD) == 0
+        && strcmp(name, PEM_STRING_X509_TRUSTED) == 0)
         return 1;
 
     /* Some CAs use PKCS#7 with CERTIFICATE headers */
-    if (!strcmp(nm, PEM_STRING_X509) && !strcmp(name, PEM_STRING_PKCS7))
+    if (strcmp(nm, PEM_STRING_X509) == 0
+        && strcmp(name, PEM_STRING_PKCS7) == 0)
         return 1;
 
-    if (!strcmp(nm, PEM_STRING_PKCS7_SIGNED) &&
-        !strcmp(name, PEM_STRING_PKCS7))
+    if (strcmp(nm, PEM_STRING_PKCS7_SIGNED) == 0
+        && strcmp(name, PEM_STRING_PKCS7) == 0)
         return 1;
 
 #ifndef OPENSSL_NO_CMS
-    if (!strcmp(nm, PEM_STRING_X509) && !strcmp(name, PEM_STRING_CMS))
+    if (strcmp(nm, PEM_STRING_X509) == 0
+        && strcmp(name, PEM_STRING_CMS) == 0)
         return 1;
     /* Allow CMS to be read from PKCS#7 headers */
-    if (!strcmp(nm, PEM_STRING_PKCS7) && !strcmp(name, PEM_STRING_CMS))
+    if (strcmp(nm, PEM_STRING_PKCS7) == 0
+        && strcmp(name, PEM_STRING_CMS) == 0)
         return 1;
 #endif
 
@@ -338,8 +293,8 @@ int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
                        void *x, const EVP_CIPHER *enc, unsigned char *kstr,
                        int klen, pem_password_cb *callback, void *u)
 {
-    EVP_CIPHER_CTX ctx;
-    int dsize = 0, i, j, ret = 0;
+    EVP_CIPHER_CTX *ctx = NULL;
+    int dsize = 0, i = 0, j = 0, ret = 0;
     unsigned char *p, *data = NULL;
     const char *objstr = NULL;
     char buf[PEM_BUFSIZE];
@@ -348,7 +303,7 @@ int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
 
     if (enc != NULL) {
         objstr = OBJ_nid2sn(EVP_CIPHER_nid(enc));
-        if (objstr == NULL) {
+        if (objstr == NULL || EVP_CIPHER_iv_length(enc) == 0) {
             PEMerr(PEM_F_PEM_ASN1_WRITE_BIO, PEM_R_UNSUPPORTED_CIPHER);
             goto err;
         }
@@ -361,7 +316,7 @@ int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
     }
     /* dzise + 8 bytes are needed */
     /* actually it needs the cipher block size extra... */
-    data = (unsigned char *)OPENSSL_malloc((unsigned int)dsize + 20);
+    data = OPENSSL_malloc((unsigned int)dsize + 20);
     if (data == NULL) {
         PEMerr(PEM_F_PEM_ASN1_WRITE_BIO, ERR_R_MALLOC_FAILURE);
         goto err;
@@ -386,8 +341,8 @@ int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
             kstr = (unsigned char *)buf;
         }
         RAND_add(data, i, 0);   /* put in the RSA key. */
-        OPENSSL_assert(enc->iv_len <= (int)sizeof(iv));
-        if (RAND_pseudo_bytes(iv, enc->iv_len) < 0) /* Generate a salt */
+        OPENSSL_assert(EVP_CIPHER_iv_length(enc) <= (int)sizeof(iv));
+        if (RAND_bytes(iv, EVP_CIPHER_iv_length(enc)) <= 0) /* Generate a salt */
             goto err;
         /*
          * The 'iv' is used as the iv and as a salt.  It is NOT taken from
@@ -399,21 +354,20 @@ int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
         if (kstr == (unsigned char *)buf)
             OPENSSL_cleanse(buf, PEM_BUFSIZE);
 
-        OPENSSL_assert(strlen(objstr) + 23 + 2 * enc->iv_len + 13 <=
-                       sizeof buf);
+        OPENSSL_assert(strlen(objstr) + 23 + 2 * EVP_CIPHER_iv_length(enc) + 13
+                       <= sizeof buf);
 
         buf[0] = '\0';
         PEM_proc_type(buf, PEM_TYPE_ENCRYPTED);
-        PEM_dek_info(buf, objstr, enc->iv_len, (char *)iv);
+        PEM_dek_info(buf, objstr, EVP_CIPHER_iv_length(enc), (char *)iv);
         /* k=strlen(buf); */
 
-        EVP_CIPHER_CTX_init(&ctx);
         ret = 1;
-        if (!EVP_EncryptInit_ex(&ctx, enc, NULL, key, iv)
-            || !EVP_EncryptUpdate(&ctx, data, &j, data, i)
-            || !EVP_EncryptFinal_ex(&ctx, &(data[j]), &i))
+        if ((ctx = EVP_CIPHER_CTX_new()) == NULL
+            || !EVP_EncryptInit_ex(ctx, enc, NULL, key, iv)
+            || !EVP_EncryptUpdate(ctx, data, &j, data, i)
+            || !EVP_EncryptFinal_ex(ctx, &(data[j]), &i))
             ret = 0;
-        EVP_CIPHER_CTX_cleanup(&ctx);
         if (ret == 0)
             goto err;
         i += j;
@@ -427,127 +381,162 @@ int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
  err:
     OPENSSL_cleanse(key, sizeof(key));
     OPENSSL_cleanse(iv, sizeof(iv));
-    OPENSSL_cleanse((char *)&ctx, sizeof(ctx));
+    EVP_CIPHER_CTX_free(ctx);
     OPENSSL_cleanse(buf, PEM_BUFSIZE);
-    if (data != NULL) {
-        OPENSSL_cleanse(data, (unsigned int)dsize);
-        OPENSSL_free(data);
-    }
+    OPENSSL_clear_free(data, (unsigned int)dsize);
     return (ret);
 }
 
 int PEM_do_header(EVP_CIPHER_INFO *cipher, unsigned char *data, long *plen,
                   pem_password_cb *callback, void *u)
 {
-    int i = 0, j, o, klen;
-    long len;
-    EVP_CIPHER_CTX ctx;
+    int ok;
+    int keylen;
+    long len = *plen;
+    int ilen = (int) len;       /* EVP_DecryptUpdate etc. take int lengths */
+    EVP_CIPHER_CTX *ctx;
     unsigned char key[EVP_MAX_KEY_LENGTH];
     char buf[PEM_BUFSIZE];
 
-    len = *plen;
+#if LONG_MAX > INT_MAX
+    /* Check that we did not truncate the length */
+    if (len > INT_MAX) {
+        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_HEADER_TOO_LONG);
+        return 0;
+    }
+#endif
 
     if (cipher->cipher == NULL)
-        return (1);
+        return 1;
     if (callback == NULL)
-        klen = PEM_def_callback(buf, PEM_BUFSIZE, 0, u);
+        keylen = PEM_def_callback(buf, PEM_BUFSIZE, 0, u);
     else
-        klen = callback(buf, PEM_BUFSIZE, 0, u);
-    if (klen <= 0) {
+        keylen = callback(buf, PEM_BUFSIZE, 0, u);
+    if (keylen <= 0) {
         PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_BAD_PASSWORD_READ);
-        return (0);
+        return 0;
     }
 #ifdef CHARSET_EBCDIC
     /* Convert the pass phrase from EBCDIC */
-    ebcdic2ascii(buf, buf, klen);
+    ebcdic2ascii(buf, buf, keylen);
 #endif
 
     if (!EVP_BytesToKey(cipher->cipher, EVP_md5(), &(cipher->iv[0]),
-                        (unsigned char *)buf, klen, 1, key, NULL))
+                        (unsigned char *)buf, keylen, 1, key, NULL))
+        return 0;
+
+    ctx = EVP_CIPHER_CTX_new();
+    if (ctx == NULL)
         return 0;
 
-    j = (int)len;
-    EVP_CIPHER_CTX_init(&ctx);
-    o = EVP_DecryptInit_ex(&ctx, cipher->cipher, NULL, key, &(cipher->iv[0]));
-    if (o)
-        o = EVP_DecryptUpdate(&ctx, data, &i, data, j);
-    if (o)
-        o = EVP_DecryptFinal_ex(&ctx, &(data[i]), &j);
-    EVP_CIPHER_CTX_cleanup(&ctx);
+    ok = EVP_DecryptInit_ex(ctx, cipher->cipher, NULL, key, &(cipher->iv[0]));
+    if (ok)
+        ok = EVP_DecryptUpdate(ctx, data, &ilen, data, ilen);
+    if (ok) {
+        /* Squirrel away the length of data decrypted so far. */
+        *plen = ilen;
+        ok = EVP_DecryptFinal_ex(ctx, &(data[ilen]), &ilen);
+    }
+    if (ok)
+        *plen += ilen;
+    else
+        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_BAD_DECRYPT);
+
+    EVP_CIPHER_CTX_free(ctx);
     OPENSSL_cleanse((char *)buf, sizeof(buf));
     OPENSSL_cleanse((char *)key, sizeof(key));
-    if (o)
-        j += i;
-    else {
-        PEMerr(PEM_F_PEM_DO_HEADER, PEM_R_BAD_DECRYPT);
-        return (0);
-    }
-    *plen = j;
-    return (1);
+    return ok;
 }
 
+/*
+ * This implements a very limited PEM header parser that does not support the
+ * full grammar of rfc1421.  In particular, folded headers are not supported,
+ * nor is additional whitespace.
+ *
+ * A robust implementation would make use of a library that turns the headers
+ * into a BIO from which one folded line is read at a time, and is then split
+ * into a header label and content.  We would then parse the content of the
+ * headers we care about.  This is overkill for just this limited use-case, but
+ * presumably we also parse rfc822-style headers for S/MIME, so a common
+ * abstraction might well be more generally useful.
+ */
 int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher)
 {
+    static const char ProcType[] = "Proc-Type:";
+    static const char ENCRYPTED[] = "ENCRYPTED";
+    static const char DEKInfo[] = "DEK-Info:";
     const EVP_CIPHER *enc = NULL;
-    char *p, c;
-    char **header_pp = &header;
+    int ivlen;
+    char *dekinfostart, c;
 
     cipher->cipher = NULL;
     if ((header == NULL) || (*header == '\0') || (*header == '\n'))
-        return (1);
-    if (strncmp(header, "Proc-Type: ", 11) != 0) {
+        return 1;
+
+    if (strncmp(header, ProcType, sizeof(ProcType)-1) != 0) {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_PROC_TYPE);
-        return (0);
+        return 0;
     }
-    header += 11;
-    if (*header != '4')
-        return (0);
-    header++;
-    if (*header != ',')
-        return (0);
-    header++;
-    if (strncmp(header, "ENCRYPTED", 9) != 0) {
+    header += sizeof(ProcType)-1;
+    header += strspn(header, " \t");
+
+    if (*header++ != '4' || *header++ != ',')
+        return 0;
+    header += strspn(header, " \t");
+
+    /* We expect "ENCRYPTED" followed by optional white-space + line break */
+    if (strncmp(header, ENCRYPTED, sizeof(ENCRYPTED)-1) != 0 ||
+        strspn(header+sizeof(ENCRYPTED)-1, " \t\r\n") == 0) {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_ENCRYPTED);
-        return (0);
+        return 0;
     }
-    for (; (*header != '\n') && (*header != '\0'); header++) ;
-    if (*header == '\0') {
+    header += sizeof(ENCRYPTED)-1;
+    header += strspn(header, " \t\r");
+    if (*header++ != '\n') {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_SHORT_HEADER);
-        return (0);
+        return 0;
     }
-    header++;
-    if (strncmp(header, "DEK-Info: ", 10) != 0) {
+
+    /*-
+     * https://tools.ietf.org/html/rfc1421#section-4.6.1.3
+     * We expect "DEK-Info: algo[,hex-parameters]"
+     */
+    if (strncmp(header, DEKInfo, sizeof(DEKInfo)-1) != 0) {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_NOT_DEK_INFO);
-        return (0);
+        return 0;
     }
-    header += 10;
+    header += sizeof(DEKInfo)-1;
+    header += strspn(header, " \t");
 
-    p = header;
-    for (;;) {
-        c = *header;
-#ifndef CHARSET_EBCDIC
-        if (!(((c >= 'A') && (c <= 'Z')) || (c == '-') ||
-              ((c >= '0') && (c <= '9'))))
-            break;
-#else
-        if (!(isupper(c) || (c == '-') || isdigit(c)))
-            break;
-#endif
-        header++;
-    }
+    /*
+     * DEK-INFO is a comma-separated combination of algorithm name and optional
+     * parameters.
+     */
+    dekinfostart = header;
+    header += strcspn(header, " \t,");
+    c = *header;
     *header = '\0';
-    cipher->cipher = enc = EVP_get_cipherbyname(p);
+    cipher->cipher = enc = EVP_get_cipherbyname(dekinfostart);
     *header = c;
-    header++;
+    header += strspn(header, " \t");
 
     if (enc == NULL) {
         PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_UNSUPPORTED_ENCRYPTION);
-        return (0);
+        return 0;
+    }
+    ivlen = EVP_CIPHER_iv_length(enc);
+    if (ivlen > 0 && *header++ != ',') {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_MISSING_DEK_IV);
+        return 0;
+    } else if (ivlen == 0 && *header == ',') {
+        PEMerr(PEM_F_PEM_GET_EVP_CIPHER_INFO, PEM_R_UNEXPECTED_DEK_IV);
+        return 0;
     }
-    if (!load_iv(header_pp, &(cipher->iv[0]), enc->iv_len))
-        return (0);
 
-    return (1);
+    if (!load_iv(&header, cipher->iv, EVP_CIPHER_iv_length(enc)))
+        return 0;
+
+    return 1;
 }
 
 static int load_iv(char **fromp, unsigned char *to, int num)
@@ -560,13 +549,8 @@ static int load_iv(char **fromp, unsigned char *to, int num)
         to[i] = 0;
     num *= 2;
     for (i = 0; i < num; i++) {
-        if ((*from >= '0') && (*from <= '9'))
-            v = *from - '0';
-        else if ((*from >= 'A') && (*from <= 'F'))
-            v = *from - 'A' + 10;
-        else if ((*from >= 'a') && (*from <= 'f'))
-            v = *from - 'a' + 10;
-        else {
+        v = OPENSSL_hexchar2int(*from);
+        if (v < 0) {
             PEMerr(PEM_F_LOAD_IV, PEM_R_BAD_IV_CHARS);
             return (0);
         }
@@ -601,10 +585,15 @@ int PEM_write_bio(BIO *bp, const char *name, const char *header,
 {
     int nlen, n, i, j, outl;
     unsigned char *buf = NULL;
-    EVP_ENCODE_CTX ctx;
+    EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
     int reason = ERR_R_BUF_LIB;
 
-    EVP_EncodeInit(&ctx);
+    if (ctx == NULL) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+    }
+
+    EVP_EncodeInit(ctx);
     nlen = strlen(name);
 
     if ((BIO_write(bp, "-----BEGIN ", 11) != 11) ||
@@ -627,29 +616,26 @@ int PEM_write_bio(BIO *bp, const char *name, const char *header,
     i = j = 0;
     while (len > 0) {
         n = (int)((len > (PEM_BUFSIZE * 5)) ? (PEM_BUFSIZE * 5) : len);
-        EVP_EncodeUpdate(&ctx, buf, &outl, &(data[j]), n);
+        EVP_EncodeUpdate(ctx, buf, &outl, &(data[j]), n);
         if ((outl) && (BIO_write(bp, (char *)buf, outl) != outl))
             goto err;
         i += outl;
         len -= n;
         j += n;
     }
-    EVP_EncodeFinal(&ctx, buf, &outl);
+    EVP_EncodeFinal(ctx, buf, &outl);
     if ((outl > 0) && (BIO_write(bp, (char *)buf, outl) != outl))
         goto err;
-    OPENSSL_cleanse(buf, PEM_BUFSIZE * 8);
-    OPENSSL_free(buf);
-    buf = NULL;
     if ((BIO_write(bp, "-----END ", 9) != 9) ||
         (BIO_write(bp, name, nlen) != nlen) ||
         (BIO_write(bp, "-----\n", 6) != 6))
         goto err;
+    OPENSSL_clear_free(buf, PEM_BUFSIZE * 8);
+    EVP_ENCODE_CTX_free(ctx);
     return (i + outl);
  err:
-    if (buf) {
-        OPENSSL_cleanse(buf, PEM_BUFSIZE * 8);
-        OPENSSL_free(buf);
-    }
+    OPENSSL_clear_free(buf, PEM_BUFSIZE * 8);
+    EVP_ENCODE_CTX_free(ctx);
     PEMerr(PEM_F_PEM_WRITE_BIO, reason);
     return (0);
 }
@@ -675,22 +661,23 @@ int PEM_read(FILE *fp, char **name, char **header, unsigned char **data,
 int PEM_read_bio(BIO *bp, char **name, char **header, unsigned char **data,
                  long *len)
 {
-    EVP_ENCODE_CTX ctx;
+    EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
     int end = 0, i, k, bl = 0, hl = 0, nohead = 0;
     char buf[256];
     BUF_MEM *nameB;
     BUF_MEM *headerB;
     BUF_MEM *dataB, *tmpB;
 
+    if (ctx == NULL) {
+        PEMerr(PEM_F_PEM_READ_BIO, ERR_R_MALLOC_FAILURE);
+        return (0);
+    }
+
     nameB = BUF_MEM_new();
     headerB = BUF_MEM_new();
     dataB = BUF_MEM_new();
     if ((nameB == NULL) || (headerB == NULL) || (dataB == NULL)) {
-        BUF_MEM_free(nameB);
-        BUF_MEM_free(headerB);
-        BUF_MEM_free(dataB);
-        PEMerr(PEM_F_PEM_READ_BIO, ERR_R_MALLOC_FAILURE);
-        return (0);
+        goto err;
     }
 
     buf[254] = '\0';
@@ -810,15 +797,15 @@ int PEM_read_bio(BIO *bp, char **name, char **header, unsigned char **data,
         goto err;
     }
 
-    EVP_DecodeInit(&ctx);
-    i = EVP_DecodeUpdate(&ctx,
+    EVP_DecodeInit(ctx);
+    i = EVP_DecodeUpdate(ctx,
                          (unsigned char *)dataB->data, &bl,
                          (unsigned char *)dataB->data, bl);
     if (i < 0) {
         PEMerr(PEM_F_PEM_READ_BIO, PEM_R_BAD_BASE64_DECODE);
         goto err;
     }
-    i = EVP_DecodeFinal(&ctx, (unsigned char *)&(dataB->data[bl]), &k);
+    i = EVP_DecodeFinal(ctx, (unsigned char *)&(dataB->data[bl]), &k);
     if (i < 0) {
         PEMerr(PEM_F_PEM_READ_BIO, PEM_R_BAD_BASE64_DECODE);
         goto err;
@@ -834,11 +821,13 @@ int PEM_read_bio(BIO *bp, char **name, char **header, unsigned char **data,
     OPENSSL_free(nameB);
     OPENSSL_free(headerB);
     OPENSSL_free(dataB);
+    EVP_ENCODE_CTX_free(ctx);
     return (1);
  err:
     BUF_MEM_free(nameB);
     BUF_MEM_free(headerB);
     BUF_MEM_free(dataB);
+    EVP_ENCODE_CTX_free(ctx);
     return (0);
 }