[openssl-commits] [openssl] master update
Viktor Dukhovni
viktor at openssl.org
Thu May 19 04:11:30 UTC 2016
The branch master has been updated
via a5a3722bc185b2baaaa183dcaafaf17b3d07a5fa (commit)
via 67787844f11fd7614bb26452fda1a1de3ed005ef (commit)
from 276fa9bda99d12666441277afa39f81ae374437d (commit)
- Log -----------------------------------------------------------------
commit a5a3722bc185b2baaaa183dcaafaf17b3d07a5fa
Author: Viktor Dukhovni <openssl-users at dukhovni.org>
Date: Sun Apr 24 19:50:45 2016 -0400
make update
Reviewed-by: Dr. Stephen Henson <steve at openssl.org>
commit 67787844f11fd7614bb26452fda1a1de3ed005ef
Author: Viktor Dukhovni <openssl-users at dukhovni.org>
Date: Sun Apr 24 19:48:50 2016 -0400
Improve and document low-level PEM read routines
PEM_read(), PEM_read_bio(), PEM_get_EVP_CIPHER_INFO() and
PEM_do_header().
Reviewed-by: Dr. Stephen Henson <steve at openssl.org>
-----------------------------------------------------------------------
Summary of changes:
crypto/pem/pem_err.c | 9 +--
crypto/pem/pem_lib.c | 157 ++++++++++++++++++++++++++++++------------------
doc/crypto/pem_read.pod | 90 +++++++++++++++++++++++++++
include/openssl/pem.h | 3 +
4 files changed, 196 insertions(+), 63 deletions(-)
create mode 100644 doc/crypto/pem_read.pod
diff --git a/crypto/pem/pem_err.c b/crypto/pem/pem_err.c
index 0d3c3e6..2282e06 100644
--- a/crypto/pem/pem_err.c
+++ b/crypto/pem/pem_err.c
@@ -82,23 +82,24 @@ static ERR_STRING_DATA PEM_str_reasons[] = {
"error converting private key"},
{ERR_REASON(PEM_R_EXPECTING_PRIVATE_KEY_BLOB),
"expecting private key blob"},
- {ERR_REASON(PEM_R_EXPECTING_PUBLIC_KEY_BLOB),
- "expecting public key blob"},
+ {ERR_REASON(PEM_R_EXPECTING_PUBLIC_KEY_BLOB), "expecting public key blob"},
+ {ERR_REASON(PEM_R_HEADER_TOO_LONG), "header too long"},
{ERR_REASON(PEM_R_INCONSISTENT_HEADER), "inconsistent header"},
{ERR_REASON(PEM_R_KEYBLOB_HEADER_PARSE_ERROR),
"keyblob header parse error"},
{ERR_REASON(PEM_R_KEYBLOB_TOO_SHORT), "keyblob too short"},
+ {ERR_REASON(PEM_R_MISSING_DEK_IV), "missing dek iv"},
{ERR_REASON(PEM_R_NOT_DEK_INFO), "not dek info"},
{ERR_REASON(PEM_R_NOT_ENCRYPTED), "not encrypted"},
{ERR_REASON(PEM_R_NOT_PROC_TYPE), "not proc type"},
{ERR_REASON(PEM_R_NO_START_LINE), "no start line"},
- {ERR_REASON(PEM_R_PROBLEMS_GETTING_PASSWORD),
- "problems getting password"},
+ {ERR_REASON(PEM_R_PROBLEMS_GETTING_PASSWORD), "problems getting password"},
{ERR_REASON(PEM_R_PUBLIC_KEY_NO_RSA), "public key no rsa"},
{ERR_REASON(PEM_R_PVK_DATA_TOO_SHORT), "pvk data too short"},
{ERR_REASON(PEM_R_PVK_TOO_SHORT), "pvk too short"},
{ERR_REASON(PEM_R_READ_KEY), "read key"},
{ERR_REASON(PEM_R_SHORT_HEADER), "short header"},
+ {ERR_REASON(PEM_R_UNEXPECTED_DEK_IV), "unexpected dek iv"},
{ERR_REASON(PEM_R_UNSUPPORTED_CIPHER), "unsupported cipher"},
{ERR_REASON(PEM_R_UNSUPPORTED_ENCRYPTION), "unsupported encryption"},
{ERR_REASON(PEM_R_UNSUPPORTED_KEY_COMPONENTS),
diff --git a/crypto/pem/pem_lib.c b/crypto/pem/pem_lib.c
index 5686554..42b46dc 100644
--- a/crypto/pem/pem_lib.c
+++ b/crypto/pem/pem_lib.c
@@ -9,6 +9,7 @@
#include <stdio.h>
#include <ctype.h>
+#include <string.h>
#include "internal/cryptlib.h"
#include <openssl/buffer.h>
#include <openssl/objects.h>
@@ -389,115 +390,153 @@ int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
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;
+ 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;
- j = (int)len;
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return 0;
- 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);
+
+ 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;
+ 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");
+ /*
+ * DEK-INFO is a comma-separated combination of algorithm name and optional
+ * parameters.
+ */
dekinfostart = 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++;
- }
+ header += strcspn(header, " \t,");
+ c = *header;
*header = '\0';
cipher->cipher = enc = EVP_get_cipherbyname(dekinfostart);
- *header++ = c;
+ *header = c;
+ 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, cipher->iv, EVP_CIPHER_iv_length(enc)))
- return (0);
+ return 0;
- return (1);
+ return 1;
}
static int load_iv(char **fromp, unsigned char *to, int num)
diff --git a/doc/crypto/pem_read.pod b/doc/crypto/pem_read.pod
new file mode 100644
index 0000000..b0ec0d8
--- /dev/null
+++ b/doc/crypto/pem_read.pod
@@ -0,0 +1,90 @@
+=pod
+
+=head1 NAME
+
+PEM_read, PEM_read_bio, PEM_do_header - low-level PEM routines
+
+=head1 SYNOPSIS
+
+ #include <openssl/pem.h>
+
+ int PEM_read(FILE *fp, char **name, char **header,
+ unsigned char **data, long *len);
+ int PEM_read_bio(BIO *bp, char **name, char **header,
+ unsigned char **data, long *len);
+ int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cinfo);
+ int PEM_do_header(EVP_CIPHER_INFO *cinfo, unsigned char *data, long *len,
+ pem_password_cb *cb, void *u);
+
+=head1 DESCRIPTION
+
+These functions read and decode PEM-encoded objects, returning the
+PEM type B<name>, any encapsulation B<header> and the decoded
+B<data> of length B<len>.
+
+PEM_read() reads from the stdio file handle B<fp>, while PEM_read_bio() reads
+from the BIO B<bio>.
+Both skip any non-PEM data that precedes the start of the next PEM object.
+When an object is successfuly retrieved, the type name from the "----BEGIN
+<type>-----" is returned via the B<name> argument, any encapsulation headers
+are returned in B<header> and the base64-decoded content and its length are
+returned via B<data> and B<len> respectively.
+The B<name>, B<header> and B<data> pointers are allocated via OPENSSL_malloc()
+and should be freed by the caller via OPENSSL_free() when no longer needed.
+
+PEM_get_EVP_CIPHER_INFO() can be used to determine the B<data> returned by
+PEM_read() or PEM_read_bio() is encrypted and to retrieve the associated cipher
+and IV.
+The caller passes a pointer to structure of type B<EVP_CIPHER_INFO> via the
+B<cinfo> argument and the B<header> returned via PEM_read() or PEM_read_bio().
+If the call is succesful 1 is retured and the cipher and IV are stored at the
+address pointed to by B<cinfo>.
+When the header is malformed, or not supported or when the cipher is unknown
+or some internal error happens 0 is returned.
+This function is deprecated, see B<NOTES> below.
+
+PEM_do_header() can then be used to decrypt the data if the header
+indicates encryption.
+The B<cinfo> argument is a pointer to the structure initialized by the previous
+call to PEM_get_EVP_CIPHER_INFO().
+The B<data> and B<len> arguments are those returned by the previous call to
+PEM_read() or PEM_read_bio().
+The B<cb> and B<u> arguments make it possible to override the default password
+prompt function as described in L<pem(3)>.
+On successful completion the B<data> is decrypted in place, and B<len> is
+updated to indicate the plaintext length.
+This function is deprecated, see B<NOTES> below.
+
+If the data is a priori known to not be encrypted, then neither PEM_do_header()
+nor PEM_get_EVP_CIPHER_INFO() need be called.
+
+The final B<data> buffer is typically an ASN.1 object which can be decoded with
+the B<d2i> function appropriate to the type B<name>.
+
+=head1 RETURN VALUES
+
+PEM_read() and PEM_read_bio() return 1 on success and 0 on failure, the latter
+includes the case when no more PEM objects remain in the input file.
+To distinguish end of file from more serious errors the caller must peek at the
+error stack and check for B<PEM_R_NO_START_LINE>, which indicates that no more
+PEM objects were found. See L<ERR_peek_last_error(3)>, L<ERR_GET_REASON(3)>.
+
+PEM_get_EVP_CIPHER_INFO() and PEM_do_header() return 1 on success, and 0 on
+failure.
+The B<data> is likely meaningless if these functions fail.
+
+=head1 NOTES
+
+The PEM_get_EVP_CIPHER_INFO() and PEM_do_header() functions are deprecated.
+This is because the underlying PEM encryption format is obsolete, and should
+be avoided.
+It uses an encryption format with an OpenSSL-specific key-derivation function,
+which employs MD5 with an iteration count of 1!
+Instead, private keys should be stored in PKCS#8 form, with a strong PKCS#5
+v2.0 PBE.
+See L<pkcs8(1)> and L<pem(3)> and L<d2i_PKCS8PrivateKey_bio(3)>.
+
+=head1 SEE ALSO
+
+L<pem(3)>, L<ERR_peek_last_error(3)>, L<ERR_GET_LIB(3)>, L<pkcs8(1)>,
+L<d2i_PKCS8PrivateKey_bio(3)>.
diff --git a/include/openssl/pem.h b/include/openssl/pem.h
index 2214bf1..74445ca 100644
--- a/include/openssl/pem.h
+++ b/include/openssl/pem.h
@@ -472,9 +472,11 @@ void ERR_load_PEM_strings(void);
# define PEM_R_ERROR_CONVERTING_PRIVATE_KEY 115
# define PEM_R_EXPECTING_PRIVATE_KEY_BLOB 119
# define PEM_R_EXPECTING_PUBLIC_KEY_BLOB 120
+# define PEM_R_HEADER_TOO_LONG 128
# define PEM_R_INCONSISTENT_HEADER 121
# define PEM_R_KEYBLOB_HEADER_PARSE_ERROR 122
# define PEM_R_KEYBLOB_TOO_SHORT 123
+# define PEM_R_MISSING_DEK_IV 129
# define PEM_R_NOT_DEK_INFO 105
# define PEM_R_NOT_ENCRYPTED 106
# define PEM_R_NOT_PROC_TYPE 107
@@ -485,6 +487,7 @@ void ERR_load_PEM_strings(void);
# define PEM_R_PVK_TOO_SHORT 125
# define PEM_R_READ_KEY 111
# define PEM_R_SHORT_HEADER 112
+# define PEM_R_UNEXPECTED_DEK_IV 130
# define PEM_R_UNSUPPORTED_CIPHER 113
# define PEM_R_UNSUPPORTED_ENCRYPTION 114
# define PEM_R_UNSUPPORTED_KEY_COMPONENTS 126
More information about the openssl-commits
mailing list