chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,21 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/pem.h>
#if defined(__cplusplus)
extern "C" {
#endif
// PEM utility functions
// PEM_proc_type appends a Proc-Type header to |buf|, determined by |type|.
void PEM_proc_type(char buf[PEM_BUFSIZE], int type);
// PEM_dek_info appends a DEK-Info header to |buf|, with an algorithm of |type|
// and a single parameter, specified by hex-encoding |len| bytes from |str|.
void PEM_dek_info(char buf[PEM_BUFSIZE], const char *type, size_t len,
char *str);
#if defined(__cplusplus)
} // extern C
#endif

View File

@@ -0,0 +1,178 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
// Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/dh.h>
#include <openssl/dsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "../fipsmodule/ec/internal.h"
static RSA *pkey_get_rsa(EVP_PKEY *key, RSA **rsa);
static DSA *pkey_get_dsa(EVP_PKEY *key, DSA **dsa);
static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey);
IMPLEMENT_PEM_rw(X509_REQ, X509_REQ, PEM_STRING_X509_REQ, X509_REQ)
IMPLEMENT_PEM_write(X509_REQ_NEW, X509_REQ, PEM_STRING_X509_REQ_OLD, X509_REQ)
IMPLEMENT_PEM_rw(X509_CRL, X509_CRL, PEM_STRING_X509_CRL, X509_CRL)
IMPLEMENT_PEM_rw(PKCS7, PKCS7, PEM_STRING_PKCS7, PKCS7)
// We treat RSA or DSA private keys as a special case. For private keys we
// read in an EVP_PKEY structure with PEM_read_bio_PrivateKey() and extract
// the relevant private key: this means can handle "traditional" and PKCS#8
// formats transparently.
static RSA *pkey_get_rsa(EVP_PKEY *key, RSA **rsa) {
RSA *rtmp;
if (!key) {
return NULL;
}
rtmp = EVP_PKEY_get1_RSA(key);
EVP_PKEY_free(key);
if (!rtmp) {
return NULL;
}
if (rsa) {
RSA_free(*rsa);
*rsa = rtmp;
}
return rtmp;
}
RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **rsa, pem_password_cb *cb,
void *u) {
EVP_PKEY *pktmp;
pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
return pkey_get_rsa(pktmp, rsa);
}
RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u) {
EVP_PKEY *pktmp;
pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
return pkey_get_rsa(pktmp, rsa);
}
IMPLEMENT_PEM_write_cb_const(RSAPrivateKey, RSA, PEM_STRING_RSA, RSAPrivateKey)
IMPLEMENT_PEM_rw_const(RSAPublicKey, RSA, PEM_STRING_RSA_PUBLIC, RSAPublicKey)
IMPLEMENT_PEM_rw(RSA_PUBKEY, RSA, PEM_STRING_PUBLIC, RSA_PUBKEY)
#ifndef OPENSSL_NO_DSA
static DSA *pkey_get_dsa(EVP_PKEY *key, DSA **dsa) {
DSA *dtmp;
if (!key) {
return NULL;
}
dtmp = EVP_PKEY_get1_DSA(key);
EVP_PKEY_free(key);
if (!dtmp) {
return NULL;
}
if (dsa) {
DSA_free(*dsa);
*dsa = dtmp;
}
return dtmp;
}
DSA *PEM_read_bio_DSAPrivateKey(BIO *bp, DSA **dsa, pem_password_cb *cb,
void *u) {
EVP_PKEY *pktmp;
pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
return pkey_get_dsa(pktmp, dsa); // will free pktmp
}
IMPLEMENT_PEM_write_cb_const(DSAPrivateKey, DSA, PEM_STRING_DSA, DSAPrivateKey)
IMPLEMENT_PEM_rw(DSA_PUBKEY, DSA, PEM_STRING_PUBLIC, DSA_PUBKEY)
DSA *PEM_read_DSAPrivateKey(FILE *fp, DSA **dsa, pem_password_cb *cb, void *u) {
EVP_PKEY *pktmp;
pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
return pkey_get_dsa(pktmp, dsa); // will free pktmp
}
IMPLEMENT_PEM_rw_const(DSAparams, DSA, PEM_STRING_DSAPARAMS, DSAparams)
#endif
static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey) {
EC_KEY *dtmp;
if (!key) {
return NULL;
}
dtmp = EVP_PKEY_get1_EC_KEY(key);
EVP_PKEY_free(key);
if (!dtmp) {
return NULL;
}
if (eckey) {
EC_KEY_free(*eckey);
*eckey = dtmp;
}
return dtmp;
}
EC_KEY *PEM_read_bio_ECPrivateKey(BIO *bp, EC_KEY **key, pem_password_cb *cb,
void *u) {
EVP_PKEY *pktmp;
pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
return pkey_get_eckey(pktmp, key); // will free pktmp
}
IMPLEMENT_PEM_write_cb(ECPrivateKey, EC_KEY, PEM_STRING_ECPRIVATEKEY,
ECPrivateKey)
IMPLEMENT_PEM_rw(EC_PUBKEY, EC_KEY, PEM_STRING_PUBLIC, EC_PUBKEY)
EC_KEY *PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb,
void *u) {
EVP_PKEY *pktmp;
pktmp = PEM_read_PrivateKey(fp, NULL, cb, u);
return pkey_get_eckey(pktmp, eckey); // will free pktmp
}
IMPLEMENT_PEM_rw_const(DHparams, DH, PEM_STRING_DHPARAMS, DHparams)
IMPLEMENT_PEM_rw(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
EC_GROUP *PEM_read_bio_ECPKParameters(BIO *bio, EC_GROUP **out_group,
pem_password_cb *cb, void *u) {
uint8_t *data = NULL;
long len;
if (!PEM_bytes_read_bio(&data, &len, NULL, PEM_STRING_ECPARAMETERS, bio, cb,
u)) {
return NULL;
}
const uint8_t *data_in = data;
EC_GROUP *ret = d2i_ECPKParameters(out_group, &data_in, len);
if (ret == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
}
OPENSSL_free(data);
return ret;
}
int PEM_write_bio_ECPKParameters(BIO *out, const EC_GROUP *group) {
int ret = 0;
unsigned char *data = NULL;
int buf_len = i2d_ECPKParameters(group, &data);
if(data == NULL || buf_len < 0) {
OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
goto err;
}
if (PEM_write_bio(out, PEM_STRING_ECPARAMETERS, NULL, data, buf_len) <= 0) {
goto err;
}
ret = 1;
err:
OPENSSL_free(data);
return ret;
}

View File

@@ -0,0 +1,331 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
// SPDX-License-Identifier: Apache-2.0
#include <openssl/pem.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <openssl/dsa.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "internal.h"
static X509_PKEY *X509_PKEY_new(void) {
return OPENSSL_zalloc(sizeof(X509_PKEY));
}
static void X509_PKEY_free(X509_PKEY *x) {
if (x == NULL) {
return;
}
EVP_PKEY_free(x->dec_pkey);
OPENSSL_free(x);
}
static X509_INFO *X509_INFO_new(void) {
return OPENSSL_zalloc(sizeof(X509_INFO));
}
void X509_INFO_free(X509_INFO *x) {
if (x == NULL) {
return;
}
X509_free(x->x509);
X509_CRL_free(x->crl);
X509_PKEY_free(x->x_pkey);
OPENSSL_free(x->enc_data);
OPENSSL_free(x);
}
STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,
pem_password_cb *cb, void *u) {
BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
if (b == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return 0;
}
STACK_OF(X509_INFO) *ret = PEM_X509_INFO_read_bio(b, sk, cb, u);
BIO_free(b);
return ret;
}
enum parse_result_t {
parse_ok,
parse_error,
parse_new_entry,
};
static enum parse_result_t parse_x509(X509_INFO *info, const uint8_t *data,
size_t len, int key_type) {
if (info->x509 != NULL) {
return parse_new_entry;
}
info->x509 = d2i_X509(NULL, &data, len);
return info->x509 != NULL ? parse_ok : parse_error;
}
static enum parse_result_t parse_x509_aux(X509_INFO *info, const uint8_t *data,
size_t len, int key_type) {
if (info->x509 != NULL) {
return parse_new_entry;
}
info->x509 = d2i_X509_AUX(NULL, &data, len);
return info->x509 != NULL ? parse_ok : parse_error;
}
static enum parse_result_t parse_crl(X509_INFO *info, const uint8_t *data,
size_t len, int key_type) {
if (info->crl != NULL) {
return parse_new_entry;
}
info->crl = d2i_X509_CRL(NULL, &data, len);
return info->crl != NULL ? parse_ok : parse_error;
}
static enum parse_result_t parse_key(X509_INFO *info, const uint8_t *data,
size_t len, int key_type) {
if (info->x_pkey != NULL) {
return parse_new_entry;
}
info->x_pkey = X509_PKEY_new();
if (info->x_pkey == NULL) {
return parse_error;
}
info->x_pkey->dec_pkey = d2i_PrivateKey(key_type, NULL, &data, len);
return info->x_pkey->dec_pkey != NULL ? parse_ok : parse_error;
}
STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk,
pem_password_cb *cb, void *u) {
X509_INFO *info = NULL;
char *name = NULL, *header = NULL;
unsigned char *data = NULL;
long len;
int ok = 0;
STACK_OF(X509_INFO) *ret = NULL;
if (sk == NULL) {
ret = sk_X509_INFO_new_null();
if (ret == NULL) {
return NULL;
}
} else {
ret = sk;
}
size_t orig_num = sk_X509_INFO_num(ret);
info = X509_INFO_new();
if (info == NULL) {
goto err;
}
for (;;) {
if (!PEM_read_bio(bp, &name, &header, &data, &len)) {
uint32_t error = ERR_peek_last_error();
if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
ERR_GET_REASON(error) == PEM_R_NO_START_LINE) {
ERR_clear_error();
break;
}
goto err;
}
enum parse_result_t (*parse_function)(X509_INFO *, const uint8_t *, size_t,
int) = NULL;
int key_type = EVP_PKEY_NONE;
if (strcmp(name, PEM_STRING_X509) == 0 ||
strcmp(name, PEM_STRING_X509_OLD) == 0) {
parse_function = parse_x509;
} else if (strcmp(name, PEM_STRING_X509_TRUSTED) == 0) {
parse_function = parse_x509_aux;
} else if (strcmp(name, PEM_STRING_X509_CRL) == 0) {
parse_function = parse_crl;
} else if (strcmp(name, PEM_STRING_RSA) == 0) {
parse_function = parse_key;
key_type = EVP_PKEY_RSA;
} else if (strcmp(name, PEM_STRING_DSA) == 0) {
parse_function = parse_key;
key_type = EVP_PKEY_DSA;
} else if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
parse_function = parse_key;
key_type = EVP_PKEY_EC;
}
// If a private key has a header, assume it is encrypted.
if (key_type != EVP_PKEY_NONE && strlen(header) > 10) {
if (info->x_pkey != NULL) {
if (!sk_X509_INFO_push(ret, info)) {
goto err;
}
info = X509_INFO_new();
if (info == NULL) {
goto err;
}
}
// Historically, raw entries pushed an empty key.
info->x_pkey = X509_PKEY_new();
if (info->x_pkey == NULL ||
!PEM_get_EVP_CIPHER_INFO(header, &info->enc_cipher)) {
goto err;
}
info->enc_data = (char *)data;
info->enc_len = (int)len;
data = NULL;
} else if (parse_function != NULL) {
EVP_CIPHER_INFO cipher;
if (!PEM_get_EVP_CIPHER_INFO(header, &cipher) ||
!PEM_do_header(&cipher, data, &len, cb, u)) {
goto err;
}
enum parse_result_t result = parse_function(info, data, len, key_type);
if (result == parse_new_entry) {
if (!sk_X509_INFO_push(ret, info)) {
goto err;
}
info = X509_INFO_new();
if (info == NULL) {
goto err;
}
result = parse_function(info, data, len, key_type);
}
if (result != parse_ok) {
OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
goto err;
}
}
OPENSSL_free(name);
OPENSSL_free(header);
OPENSSL_free(data);
name = NULL;
header = NULL;
data = NULL;
}
// Push the last entry on the stack if not empty.
if (info->x509 != NULL || info->crl != NULL || info->x_pkey != NULL ||
info->enc_data != NULL) {
if (!sk_X509_INFO_push(ret, info)) {
goto err;
}
info = NULL;
}
ok = 1;
err:
X509_INFO_free(info);
if (!ok) {
while (sk_X509_INFO_num(ret) > orig_num) {
X509_INFO_free(sk_X509_INFO_pop(ret));
}
if (ret != sk) {
sk_X509_INFO_free(ret);
}
ret = NULL;
}
OPENSSL_free(name);
OPENSSL_free(header);
OPENSSL_free(data);
return ret;
}
// A TJH addition
int PEM_X509_INFO_write_bio(BIO *bp, X509_INFO *xi, EVP_CIPHER *enc,
unsigned char *kstr, int klen, pem_password_cb *cb,
void *u) {
int i, ret = 0;
unsigned char *data = NULL;
const char *objstr = NULL;
char buf[PEM_BUFSIZE];
unsigned char *iv = NULL;
unsigned iv_len = 0;
if (enc != NULL) {
iv_len = EVP_CIPHER_iv_length(enc);
objstr = OBJ_nid2sn(EVP_CIPHER_nid(enc));
if (objstr == NULL) {
OPENSSL_PUT_ERROR(PEM, PEM_R_UNSUPPORTED_CIPHER);
goto err;
}
}
if (xi == NULL) {
goto err;
}
// now for the fun part ... if we have a private key then we have to be
// able to handle a not-yet-decrypted key being written out correctly ...
// if it is decrypted or it is non-encrypted then we use the base code
if (xi->x_pkey != NULL) {
if ((xi->enc_data != NULL) && (xi->enc_len > 0)) {
if (enc == NULL) {
OPENSSL_PUT_ERROR(PEM, PEM_R_CIPHER_IS_NULL);
goto err;
}
// copy from weirdo names into more normal things
iv = xi->enc_cipher.iv;
data = (unsigned char *)xi->enc_data;
i = xi->enc_len;
// we take the encryption data from the internal stuff rather
// than what the user has passed us ... as we have to match
// exactly for some strange reason
objstr = OBJ_nid2sn(EVP_CIPHER_nid(xi->enc_cipher.cipher));
if (objstr == NULL) {
OPENSSL_PUT_ERROR(PEM, PEM_R_UNSUPPORTED_CIPHER);
goto err;
}
// Check if there is enough space for the header:
// - strlen(objstr) for the cipher name
// - 23 bytes for fixed PEM header components from |PEM_proc_type|
// - 2*iv_len as IV bytes are encoded in hex (each byte needs two characters)
// - 13 bytes for DEK-Info header formatting
assert(strlen(objstr) + 23 + 2 * iv_len + 13 <= sizeof buf);
buf[0] = '\0';
PEM_proc_type(buf, PEM_TYPE_ENCRYPTED);
PEM_dek_info(buf, objstr, iv_len, (char *)iv);
// use the normal code to write things out
i = PEM_write_bio(bp, PEM_STRING_RSA, buf, data, i);
if (i <= 0) {
goto err;
}
} else if (xi->x_pkey->dec_pkey) {
// Add DSA/DH
// normal optionally encrypted stuff
if (PEM_write_bio_RSAPrivateKey(bp,
EVP_PKEY_get0_RSA(xi->x_pkey->dec_pkey),
enc, kstr, klen, cb, u) <= 0) {
goto err;
}
}
}
// if we have a certificate then write it out now
if ((xi->x509 != NULL) && (PEM_write_bio_X509(bp, xi->x509) <= 0)) {
goto err;
}
// we are ignoring anything else that is loaded into the X509_INFO
// structure for the moment ... as I don't need it so I'm not coding it
// here and Eric can do it when this makes it into the base library --tjh
ret = 1;
err:
OPENSSL_cleanse(buf, PEM_BUFSIZE);
return ret;
}

View File

@@ -0,0 +1,773 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <openssl/base64.h>
#include <openssl/buf.h>
#include <openssl/des.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include "internal.h"
#include "../internal.h"
#include "../console/internal.h"
#include "../fipsmodule/evp/internal.h"
static int load_iv(char **fromp, unsigned char *to, size_t num);
static int check_pem(const char *nm, const char *name);
void PEM_proc_type(char buf[PEM_BUFSIZE], int type) {
const char *str;
if (type == PEM_TYPE_ENCRYPTED) {
str = "ENCRYPTED";
} else if (type == PEM_TYPE_MIC_CLEAR) {
str = "MIC-CLEAR";
} else if (type == PEM_TYPE_MIC_ONLY) {
str = "MIC-ONLY";
} else {
str = "BAD-TYPE";
}
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[PEM_BUFSIZE], const char *type, size_t len,
char *str) {
static const unsigned char map[17] = "0123456789ABCDEF";
OPENSSL_strlcat(buf, "DEK-Info: ", PEM_BUFSIZE);
OPENSSL_strlcat(buf, type, PEM_BUFSIZE);
OPENSSL_strlcat(buf, ",", PEM_BUFSIZE);
size_t buf_len = strlen(buf);
// We must write an additional |2 * len + 2| bytes after |buf_len|, including
// the trailing newline and NUL.
if (len > (PEM_BUFSIZE - buf_len - 2) / 2) {
return;
}
for (size_t i = 0; i < len; i++) {
buf[buf_len + i * 2] = map[(str[i] >> 4) & 0x0f];
buf[buf_len + i * 2 + 1] = map[(str[i]) & 0x0f];
}
buf[buf_len + len * 2] = '\n';
buf[buf_len + len * 2 + 1] = '\0';
}
void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
pem_password_cb *cb, void *u) {
BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
if (b == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return NULL;
}
void *ret = PEM_ASN1_read_bio(d2i, name, b, x, cb, u);
BIO_free(b);
return ret;
}
static int check_pem(const char *nm, const char *name) {
// Normal matching nm and name
if (!strcmp(nm, name)) {
return 1;
}
// Make PEM_STRING_EVP_PKEY match any private key
if (!strcmp(name, PEM_STRING_EVP_PKEY)) {
return !strcmp(nm, PEM_STRING_PKCS8) || !strcmp(nm, PEM_STRING_PKCS8INF) ||
!strcmp(nm, PEM_STRING_RSA) || !strcmp(nm, PEM_STRING_EC) ||
!strcmp(nm, PEM_STRING_DSA);
}
// These correspond with the PEM strings that have "PARAMETERS".
if (!strcmp(name, PEM_STRING_PARAMETERS)) {
return !strcmp(nm, PEM_STRING_ECPARAMETERS) ||
!strcmp(nm, PEM_STRING_DSAPARAMS) ||
!strcmp(nm, PEM_STRING_DHPARAMS);
}
// Permit older strings
if (!strcmp(nm, PEM_STRING_X509_OLD) && !strcmp(name, PEM_STRING_X509)) {
return 1;
}
if (!strcmp(nm, PEM_STRING_X509_REQ_OLD) &&
!strcmp(name, PEM_STRING_X509_REQ)) {
return 1;
}
// Allow normal certs to be read as trusted certs
if (!strcmp(nm, PEM_STRING_X509) && !strcmp(name, PEM_STRING_X509_TRUSTED)) {
return 1;
}
if (!strcmp(nm, PEM_STRING_X509_OLD) &&
!strcmp(name, PEM_STRING_X509_TRUSTED)) {
return 1;
}
// Some CAs use PKCS#7 with CERTIFICATE headers
if (!strcmp(nm, PEM_STRING_X509) && !strcmp(name, PEM_STRING_PKCS7)) {
return 1;
}
if (!strcmp(nm, PEM_STRING_PKCS7_SIGNED) && !strcmp(name, PEM_STRING_PKCS7)) {
return 1;
}
#ifndef OPENSSL_NO_CMS
if (!strcmp(nm, PEM_STRING_X509) && !strcmp(name, PEM_STRING_CMS)) {
return 1;
}
// Allow CMS to be read from PKCS#7 headers
if (!strcmp(nm, PEM_STRING_PKCS7) && !strcmp(name, PEM_STRING_CMS)) {
return 1;
}
#endif
return 0;
}
static const EVP_CIPHER *cipher_by_name(const char *name) {
// This is similar to the (deprecated) function |EVP_get_cipherbyname|. Note
// the PEM code assumes that ciphers have at least 8 bytes of IV, at most 20
// bytes of overhead and generally behave like CBC mode.
if (0 == strcmp(name, SN_des_cbc)) {
return EVP_des_cbc();
} else if (0 == strcmp(name, SN_des_ede3_cbc)) {
return EVP_des_ede3_cbc();
} else if (0 == strcmp(name, SN_aes_128_cbc)) {
return EVP_aes_128_cbc();
} else if (0 == strcmp(name, SN_aes_192_cbc)) {
return EVP_aes_192_cbc();
} else if (0 == strcmp(name, SN_aes_256_cbc)) {
return EVP_aes_256_cbc();
} else {
return NULL;
}
}
int PEM_bytes_read_bio(unsigned char **pdata, long *plen, char **pnm,
const char *name, BIO *bp, pem_password_cb *cb,
void *u) {
EVP_CIPHER_INFO cipher;
char *nm = NULL, *header = NULL;
unsigned char *data = NULL;
long len;
int ret = 0;
for (;;) {
if (!PEM_read_bio(bp, &nm, &header, &data, &len)) {
uint32_t error = ERR_peek_error();
if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
ERR_GET_REASON(error) == PEM_R_NO_START_LINE) {
ERR_add_error_data(2, "Expecting: ", name);
}
return 0;
}
if (check_pem(nm, name)) {
break;
}
OPENSSL_free(nm);
OPENSSL_free(header);
OPENSSL_free(data);
}
if (!PEM_get_EVP_CIPHER_INFO(header, &cipher)) {
goto err;
}
if (!PEM_do_header(&cipher, data, &len, cb, u)) {
goto err;
}
*pdata = data;
*plen = len;
if (pnm) {
*pnm = nm;
}
ret = 1;
err:
if (!ret || !pnm) {
OPENSSL_free(nm);
}
OPENSSL_free(header);
if (!ret) {
OPENSSL_free(data);
}
return ret;
}
int PEM_ASN1_write(i2d_of_void *i2d, const char *name, FILE *fp, void *x,
const EVP_CIPHER *enc, const unsigned char *pass,
int pass_len, pem_password_cb *callback, void *u) {
BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
if (b == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return 0;
}
int ret =
PEM_ASN1_write_bio(i2d, name, b, x, enc, pass, pass_len, callback, u);
BIO_free(b);
return ret;
}
int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp, void *x,
const EVP_CIPHER *enc, const unsigned char *pass,
int pass_len, pem_password_cb *callback, void *u) {
EVP_CIPHER_CTX ctx;
int i, j, ret = 0;
unsigned char *p, *data = NULL;
const char *objstr = NULL;
char buf[PEM_BUFSIZE];
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];
if (enc != NULL) {
objstr = OBJ_nid2sn(EVP_CIPHER_nid(enc));
if (objstr == NULL || cipher_by_name(objstr) == NULL ||
EVP_CIPHER_iv_length(enc) < 8) {
OPENSSL_PUT_ERROR(PEM, PEM_R_UNSUPPORTED_CIPHER);
goto err;
}
}
int dsize = i2d(x, NULL);
if (dsize < 0) {
OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
OPENSSL_cleanse(&dsize, sizeof(dsize));
goto err;
}
// dzise + 8 bytes are needed
// actually it needs the cipher block size extra...
data = (unsigned char *)OPENSSL_malloc((unsigned int)dsize + 20);
if (data == NULL) {
goto err;
}
p = data;
i = i2d(x, &p);
if (enc != NULL) {
const unsigned iv_len = EVP_CIPHER_iv_length(enc);
if (pass == NULL) {
if (!callback) {
callback = PEM_def_callback;
}
pass_len = (*callback)(buf, PEM_BUFSIZE, 1, u);
if (pass_len < 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_READ_KEY);
goto err;
}
pass = (const unsigned char *)buf;
}
assert(iv_len <= sizeof(iv));
AWSLC_ABORT_IF_NOT_ONE(RAND_bytes(iv, iv_len)); // Generate a salt
// The 'iv' is used as the iv and as a salt. It is NOT taken from
// the BytesToKey function
if (!EVP_BytesToKey(enc, EVP_md5(), iv, pass, pass_len, 1, key, NULL)) {
goto err;
}
if (pass == (const unsigned char *)buf) {
OPENSSL_cleanse(buf, PEM_BUFSIZE);
}
assert(strlen(objstr) + 23 + 2 * iv_len + 13 <= sizeof(buf));
buf[0] = '\0';
PEM_proc_type(buf, PEM_TYPE_ENCRYPTED);
PEM_dek_info(buf, objstr, iv_len, (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)) {
ret = 0;
} else {
i += j;
}
EVP_CIPHER_CTX_cleanup(&ctx);
if (ret == 0) {
goto err;
}
} else {
ret = 1;
buf[0] = '\0';
}
i = PEM_write_bio(bp, name, buf, data, i);
if (i <= 0) {
ret = 0;
}
err:
OPENSSL_cleanse(key, sizeof(key));
OPENSSL_cleanse(iv, sizeof(iv));
OPENSSL_cleanse((char *)&ctx, sizeof(ctx));
OPENSSL_cleanse(buf, PEM_BUFSIZE);
OPENSSL_free(data);
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, pass_len;
long len;
EVP_CIPHER_CTX ctx;
unsigned char key[EVP_MAX_KEY_LENGTH];
char buf[PEM_BUFSIZE];
len = *plen;
if (cipher->cipher == NULL) {
return 1;
}
if (!callback) {
callback = PEM_def_callback;
}
pass_len = callback(buf, PEM_BUFSIZE, 0, u);
if (pass_len < 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_BAD_PASSWORD_READ);
return 0;
}
if (!EVP_BytesToKey(cipher->cipher, EVP_md5(), &(cipher->iv[0]),
(unsigned char *)buf, pass_len, 1, key, 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);
OPENSSL_cleanse((char *)buf, sizeof(buf));
OPENSSL_cleanse((char *)key, sizeof(key));
if (!o) {
OPENSSL_PUT_ERROR(PEM, PEM_R_BAD_DECRYPT);
return 0;
}
j += i;
*plen = j;
return 1;
}
int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher) {
const EVP_CIPHER *enc = NULL;
char *p, c;
char **header_pp = &header;
cipher->cipher = NULL;
OPENSSL_memset(cipher->iv, 0, sizeof(cipher->iv));
if ((header == NULL) || (*header == '\0') || (*header == '\n')) {
return 1;
}
if (strncmp(header, "Proc-Type: ", 11) != 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_NOT_PROC_TYPE);
return 0;
}
header += 11;
if (*header != '4') {
return 0;
}
header++;
if (*header != ',') {
return 0;
}
header++;
if (strncmp(header, "ENCRYPTED", 9) != 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_NOT_ENCRYPTED);
return 0;
}
for (; (*header != '\n') && (*header != '\0'); header++) {
;
}
if (*header == '\0') {
OPENSSL_PUT_ERROR(PEM, PEM_R_SHORT_HEADER);
return 0;
}
header++;
if (strncmp(header, "DEK-Info: ", 10) != 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_NOT_DEK_INFO);
return 0;
}
header += 10;
p = header;
for (;;) {
c = *header;
if (!((c >= 'A' && c <= 'Z') || c == '-' ||
OPENSSL_isdigit(c))) {
break;
}
header++;
}
*header = '\0';
cipher->cipher = enc = cipher_by_name(p);
*header = c;
header++;
if (enc == NULL) {
OPENSSL_PUT_ERROR(PEM, PEM_R_UNSUPPORTED_ENCRYPTION);
return 0;
}
// The IV parameter must be at least 8 bytes long to be used as the salt in
// the KDF. (This should not happen given |cipher_by_name|.)
if (EVP_CIPHER_iv_length(enc) < 8) {
assert(0);
OPENSSL_PUT_ERROR(PEM, PEM_R_UNSUPPORTED_ENCRYPTION);
return 0;
}
if (!load_iv(header_pp, &(cipher->iv[0]), EVP_CIPHER_iv_length(enc))) {
return 0;
}
return 1;
}
static int load_iv(char **fromp, unsigned char *to, size_t num) {
uint8_t v;
char *from;
from = *fromp;
for (size_t i = 0; i < num; i++) {
to[i] = 0;
}
num *= 2;
for (size_t i = 0; i < num; i++) {
if (!OPENSSL_fromxdigit(&v, *from)) {
OPENSSL_PUT_ERROR(PEM, PEM_R_BAD_IV_CHARS);
return 0;
}
from++;
to[i / 2] |= v << (!(i & 1)) * 4;
}
*fromp = from;
return 1;
}
int PEM_write(FILE *fp, const char *name, const char *header,
const unsigned char *data, long len) {
BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
if (b == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return 0;
}
int ret = PEM_write_bio(b, name, header, data, len);
BIO_free(b);
return ret;
}
int PEM_write_bio(BIO *bp, const char *name, const char *header,
const unsigned char *data, long len) {
int nlen, n, i, j, outl;
unsigned char *buf = NULL;
EVP_ENCODE_CTX ctx;
int reason = ERR_R_BUF_LIB;
EVP_EncodeInit(&ctx);
nlen = strlen(name);
if ((BIO_write(bp, "-----BEGIN ", 11) != 11) ||
(BIO_write(bp, name, nlen) != nlen) ||
(BIO_write(bp, "-----\n", 6) != 6)) {
goto err;
}
i = (header != NULL) ? strlen(header) : 0;
if (i > 0) {
if ((BIO_write(bp, header, i) != i) || (BIO_write(bp, "\n", 1) != 1)) {
goto err;
}
}
buf = OPENSSL_malloc(PEM_BUFSIZE * 8);
if (buf == NULL) {
goto err;
}
i = j = 0;
while (len > 0) {
n = (int)((len > (PEM_BUFSIZE * 5)) ? (PEM_BUFSIZE * 5) : len);
if(!EVP_EncodeUpdate(&ctx, buf, &outl, &(data[j]), n)) {
goto err;
}
if ((outl) && (BIO_write(bp, (char *)buf, outl) != outl)) {
goto err;
}
i += outl;
len -= n;
j += n;
}
EVP_EncodeFinal(&ctx, buf, &outl);
if ((outl > 0) && (BIO_write(bp, (char *)buf, outl) != outl)) {
goto err;
}
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;
}
return i + outl;
err:
if (buf) {
OPENSSL_free(buf);
}
OPENSSL_PUT_ERROR(PEM, reason);
return 0;
}
int PEM_read(FILE *fp, char **name, char **header, unsigned char **data,
long *len) {
BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
if (b == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return 0;
}
int ret = PEM_read_bio(b, name, header, data, len);
BIO_free(b);
return ret;
}
int PEM_read_bio(BIO *bp, char **name, char **header, unsigned char **data,
long *len) {
EVP_ENCODE_CTX ctx;
int end = 0, i, k, bl = 0, hl = 0, nohead = 0;
char buf[256];
BUF_MEM *nameB;
BUF_MEM *headerB;
BUF_MEM *dataB, *tmpB;
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);
return 0;
}
buf[254] = '\0';
for (;;) {
i = BIO_gets(bp, buf, 254);
if (i <= 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_NO_START_LINE);
goto err;
}
while ((i >= 0) && (buf[i] <= ' ')) {
i--;
}
buf[++i] = '\n';
buf[++i] = '\0';
if (strncmp(buf, "-----BEGIN ", 11) == 0) {
i = strlen(&(buf[11]));
if (strncmp(&(buf[11 + i - 6]), "-----\n", 6) != 0) {
continue;
}
if (!BUF_MEM_grow(nameB, i + 9)) {
goto err;
}
OPENSSL_memcpy(nameB->data, &(buf[11]), i - 6);
nameB->data[i - 6] = '\0';
break;
}
}
hl = 0;
if (!BUF_MEM_grow(headerB, 256)) {
goto err;
}
headerB->data[0] = '\0';
for (;;) {
i = BIO_gets(bp, buf, 254);
if (i <= 0) {
break;
}
while ((i >= 0) && (buf[i] <= ' ')) {
i--;
}
buf[++i] = '\n';
buf[++i] = '\0';
if (buf[0] == '\n') {
break;
}
if (!BUF_MEM_grow(headerB, hl + i + 9)) {
goto err;
}
// To resolve following error:
// /home/runner/work/aws-lc-rs/aws-lc-rs/aws-lc-sys/aws-lc/crypto/pem/pem_lib.c:707:11: error: 'strncmp' of strings of length 1 and 9 and bound of 9 evaluates to nonzero [-Werror=string-compare]
// 707 | if (strncmp(buf, "-----END ", 9) == 0) {
// | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
if (CRYPTO_memcmp(buf, "-----END ", 9) == 0) {
nohead = 1;
break;
}
OPENSSL_memcpy(&(headerB->data[hl]), buf, i);
headerB->data[hl + i] = '\0';
hl += i;
}
bl = 0;
if (!BUF_MEM_grow(dataB, 1024)) {
goto err;
}
dataB->data[0] = '\0';
if (!nohead) {
for (;;) {
i = BIO_gets(bp, buf, 254);
if (i <= 0) {
break;
}
while ((i >= 0) && (buf[i] <= ' ')) {
i--;
}
buf[++i] = '\n';
buf[++i] = '\0';
if (i != 65) {
end = 1;
}
if (CRYPTO_memcmp(buf, "-----END ", 9) == 0) {
break;
}
if (i > 65) {
break;
}
if (!BUF_MEM_grow_clean(dataB, i + bl + 9)) {
goto err;
}
OPENSSL_memcpy(&(dataB->data[bl]), buf, i);
dataB->data[bl + i] = '\0';
bl += i;
if (end) {
buf[0] = '\0';
i = BIO_gets(bp, buf, 254);
if (i <= 0) {
break;
}
while ((i >= 0) && (buf[i] <= ' ')) {
i--;
}
buf[++i] = '\n';
buf[++i] = '\0';
break;
}
}
} else {
tmpB = headerB;
headerB = dataB;
dataB = tmpB;
bl = hl;
}
i = strlen(nameB->data);
if ((CRYPTO_memcmp(buf, "-----END ", 9) != 0) ||
(strncmp(nameB->data, &(buf[9]), i) != 0) ||
(strncmp(&(buf[9 + i]), "-----\n", 6) != 0)) {
OPENSSL_PUT_ERROR(PEM, PEM_R_BAD_END_LINE);
goto err;
}
EVP_DecodeInit(&ctx);
i = EVP_DecodeUpdate(&ctx, (unsigned char *)dataB->data, &bl,
(unsigned char *)dataB->data, bl);
if (i < 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_BAD_BASE64_DECODE);
goto err;
}
i = EVP_DecodeFinal(&ctx, (unsigned char *)&(dataB->data[bl]), &k);
if (i < 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_BAD_BASE64_DECODE);
goto err;
}
bl += k;
if (bl == 0) {
goto err;
}
*name = nameB->data;
*header = headerB->data;
*data = (unsigned char *)dataB->data;
*len = bl;
OPENSSL_free(nameB);
OPENSSL_free(headerB);
OPENSSL_free(dataB);
return 1;
err:
BUF_MEM_free(nameB);
BUF_MEM_free(headerB);
BUF_MEM_free(dataB);
return 0;
}
int PEM_def_callback(char *buf, int size, int rwflag, void *userdata) {
if (!buf || size <= 0) {
return 0;
}
// Proactively zeroize |buf|
OPENSSL_cleanse(buf, size);
if (userdata) {
size_t len = strlen((char *)userdata);
if (len >= (size_t)size) {
return 0;
}
OPENSSL_strlcpy(buf, userdata, (size_t)size);
return (int)len;
}
const char *prompt = EVP_get_pw_prompt();
if (prompt == NULL) {
prompt = "Enter PEM pass phrase:";
}
/*
* rwflag == 0 means decryption
* rwflag == 1 means encryption
*
* We assume that for encryption, we want a minimum length, while for
* decryption, we cannot know any minimum length, so we assume zero.
*/
int min_len = rwflag ? MIN_LENGTH : 0;
int ret = EVP_read_pw_string_min(buf, min_len, size, prompt, rwflag);
if (ret != 0) {
return 0;
}
return (int)OPENSSL_strnlen(buf, size);
}

View File

@@ -0,0 +1,34 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/pem.h>
#include <stdio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
// Handle 'other' PEMs: not private keys
void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x,
pem_password_cb *cb, void *u) {
const unsigned char *p = NULL;
unsigned char *data = NULL;
long len;
char *ret = NULL;
if (!PEM_bytes_read_bio(&data, &len, NULL, name, bp, cb, u)) {
return NULL;
}
p = data;
ret = d2i(x, &p, len);
if (ret == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
}
OPENSSL_free(data);
return ret;
}

View File

@@ -0,0 +1,196 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/pkcs8.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
static int do_pk8pkey(BIO *bp, const EVP_PKEY *x, int isder, int nid,
const EVP_CIPHER *enc, const char *pass, int pass_len,
pem_password_cb *cb, void *u);
static int do_pk8pkey_fp(FILE *bp, const EVP_PKEY *x, int isder, int nid,
const EVP_CIPHER *enc, const char *pass, int pass_len,
pem_password_cb *cb, void *u);
// These functions write a private key in PKCS#8 format: it is a "drop in"
// replacement for PEM_write_bio_PrivateKey() and friends. As usual if 'enc'
// is NULL then it uses the unencrypted private key form. The 'nid' versions
// uses PKCS#5 v1.5 PBE algorithms whereas the others use PKCS#5 v2.0.
int PEM_write_bio_PKCS8PrivateKey_nid(BIO *bp, const EVP_PKEY *x, int nid,
const char *pass, int pass_len,
pem_password_cb *cb, void *u) {
return do_pk8pkey(bp, x, 0, nid, NULL, pass, pass_len, cb, u);
}
int PEM_write_bio_PKCS8PrivateKey(BIO *bp, const EVP_PKEY *x,
const EVP_CIPHER *enc, const char *pass,
int pass_len, pem_password_cb *cb, void *u) {
return do_pk8pkey(bp, x, 0, -1, enc, pass, pass_len, cb, u);
}
int i2d_PKCS8PrivateKey_bio(BIO *bp, const EVP_PKEY *x, const EVP_CIPHER *enc,
const char *pass, int pass_len, pem_password_cb *cb,
void *u) {
return do_pk8pkey(bp, x, 1, -1, enc, pass, pass_len, cb, u);
}
int i2d_PKCS8PrivateKey_nid_bio(BIO *bp, const EVP_PKEY *x, int nid,
const char *pass, int pass_len,
pem_password_cb *cb, void *u) {
return do_pk8pkey(bp, x, 1, nid, NULL, pass, pass_len, cb, u);
}
static int do_pk8pkey(BIO *bp, const EVP_PKEY *x, int isder, int nid,
const EVP_CIPHER *enc, const char *pass, int pass_len,
pem_password_cb *cb, void *u) {
X509_SIG *p8;
PKCS8_PRIV_KEY_INFO *p8inf;
char buf[PEM_BUFSIZE];
int ret;
if (!(p8inf = EVP_PKEY2PKCS8(x))) {
OPENSSL_PUT_ERROR(PEM, PEM_R_ERROR_CONVERTING_PRIVATE_KEY);
return 0;
}
if (enc || (nid != -1)) {
if (!pass) {
if (!cb) {
cb = PEM_def_callback;
}
pass_len = cb(buf, PEM_BUFSIZE, 1, u);
if (pass_len < 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_READ_KEY);
PKCS8_PRIV_KEY_INFO_free(p8inf);
return 0;
}
pass = buf;
}
p8 = PKCS8_encrypt(nid, enc, pass, pass_len, NULL, 0, 0, p8inf);
if (pass == buf) {
OPENSSL_cleanse(buf, pass_len);
}
PKCS8_PRIV_KEY_INFO_free(p8inf);
if (isder) {
ret = i2d_PKCS8_bio(bp, p8);
} else {
ret = PEM_write_bio_PKCS8(bp, p8);
}
X509_SIG_free(p8);
return ret;
} else {
if (isder) {
ret = i2d_PKCS8_PRIV_KEY_INFO_bio(bp, p8inf);
} else {
ret = PEM_write_bio_PKCS8_PRIV_KEY_INFO(bp, p8inf);
}
PKCS8_PRIV_KEY_INFO_free(p8inf);
return ret;
}
}
EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY **x, pem_password_cb *cb,
void *u) {
PKCS8_PRIV_KEY_INFO *p8inf = NULL;
X509_SIG *p8 = NULL;
int pass_len;
EVP_PKEY *ret;
char psbuf[PEM_BUFSIZE];
p8 = d2i_PKCS8_bio(bp, NULL);
if (!p8) {
return NULL;
}
if (!cb) {
cb = PEM_def_callback;
}
pass_len = cb(psbuf, PEM_BUFSIZE, 0, u);
if (pass_len < 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_BAD_PASSWORD_READ);
X509_SIG_free(p8);
return NULL;
}
p8inf = PKCS8_decrypt(p8, psbuf, pass_len);
X509_SIG_free(p8);
OPENSSL_cleanse(psbuf, pass_len);
if (!p8inf) {
return NULL;
}
ret = EVP_PKCS82PKEY(p8inf);
PKCS8_PRIV_KEY_INFO_free(p8inf);
if (!ret) {
return NULL;
}
if (x) {
if (*x) {
EVP_PKEY_free(*x);
}
*x = ret;
}
return ret;
}
int i2d_PKCS8PrivateKey_fp(FILE *fp, const EVP_PKEY *x, const EVP_CIPHER *enc,
const char *pass, int pass_len, pem_password_cb *cb,
void *u) {
return do_pk8pkey_fp(fp, x, 1, -1, enc, pass, pass_len, cb, u);
}
int i2d_PKCS8PrivateKey_nid_fp(FILE *fp, const EVP_PKEY *x, int nid,
const char *pass, int pass_len,
pem_password_cb *cb, void *u) {
return do_pk8pkey_fp(fp, x, 1, nid, NULL, pass, pass_len, cb, u);
}
int PEM_write_PKCS8PrivateKey_nid(FILE *fp, const EVP_PKEY *x, int nid,
const char *pass, int pass_len,
pem_password_cb *cb, void *u) {
return do_pk8pkey_fp(fp, x, 0, nid, NULL, pass, pass_len, cb, u);
}
int PEM_write_PKCS8PrivateKey(FILE *fp, const EVP_PKEY *x,
const EVP_CIPHER *enc, const char *pass,
int pass_len, pem_password_cb *cb, void *u) {
return do_pk8pkey_fp(fp, x, 0, -1, enc, pass, pass_len, cb, u);
}
static int do_pk8pkey_fp(FILE *fp, const EVP_PKEY *x, int isder, int nid,
const EVP_CIPHER *enc, const char *pass, int pass_len,
pem_password_cb *cb, void *u) {
BIO *bp;
int ret;
if (!(bp = BIO_new_fp(fp, BIO_NOCLOSE))) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return 0;
}
ret = do_pk8pkey(bp, x, isder, nid, enc, pass, pass_len, cb, u);
BIO_free(bp);
return ret;
}
EVP_PKEY *d2i_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
void *u) {
BIO *bp;
EVP_PKEY *ret;
if (!(bp = BIO_new_fp(fp, BIO_NOCLOSE))) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return NULL;
}
ret = d2i_PKCS8PrivateKey_bio(bp, x, cb, u);
BIO_free(bp);
return ret;
}
IMPLEMENT_PEM_rw(PKCS8, X509_SIG, PEM_STRING_PKCS8, X509_SIG)
IMPLEMENT_PEM_rw(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO, PEM_STRING_PKCS8INF,
PKCS8_PRIV_KEY_INFO)

View File

@@ -0,0 +1,251 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/pem.h>
#include <stdio.h>
#include <string.h>
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/pkcs8.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include "../fipsmodule/evp/internal.h"
EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb,
void *u) {
char *nm = NULL;
const unsigned char *p = NULL;
unsigned char *data = NULL;
long len;
EVP_PKEY *ret = NULL;
if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_EVP_PKEY, bp, cb, u)) {
return NULL;
}
p = data;
if (strcmp(nm, PEM_STRING_PKCS8INF) == 0) {
PKCS8_PRIV_KEY_INFO *p8inf;
p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, len);
if (!p8inf) {
goto p8err;
}
ret = EVP_PKCS82PKEY(p8inf);
if (x) {
if (*x) {
EVP_PKEY_free((EVP_PKEY *)*x);
}
*x = ret;
}
PKCS8_PRIV_KEY_INFO_free(p8inf);
} else if (strcmp(nm, PEM_STRING_PKCS8) == 0) {
PKCS8_PRIV_KEY_INFO *p8inf;
X509_SIG *p8;
char psbuf[PEM_BUFSIZE];
p8 = d2i_X509_SIG(NULL, &p, len);
if (!p8) {
goto p8err;
}
if (!cb) {
cb = PEM_def_callback;
}
int pass_len = cb(psbuf, PEM_BUFSIZE, 0, u);
if (pass_len < 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_BAD_PASSWORD_READ);
X509_SIG_free(p8);
goto err;
}
p8inf = PKCS8_decrypt(p8, psbuf, pass_len);
X509_SIG_free(p8);
OPENSSL_cleanse(psbuf, pass_len);
if (!p8inf) {
goto p8err;
}
ret = EVP_PKCS82PKEY(p8inf);
if (x) {
if (*x) {
EVP_PKEY_free((EVP_PKEY *)*x);
}
*x = ret;
}
PKCS8_PRIV_KEY_INFO_free(p8inf);
} else if (strcmp(nm, PEM_STRING_RSA) == 0) {
// TODO(davidben): d2i_PrivateKey parses PKCS#8 along with the
// standalone format. This and the cases below probably should not
// accept PKCS#8.
ret = d2i_PrivateKey(EVP_PKEY_RSA, x, &p, len);
} else if (strcmp(nm, PEM_STRING_EC) == 0) {
ret = d2i_PrivateKey(EVP_PKEY_EC, x, &p, len);
} else if (strcmp(nm, PEM_STRING_DSA) == 0) {
ret = d2i_PrivateKey(EVP_PKEY_DSA, x, &p, len);
}
p8err:
if (ret == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
}
err:
OPENSSL_free(nm);
OPENSSL_free(data);
return ret;
}
int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
const unsigned char *pass, int pass_len,
pem_password_cb *cb, void *u) {
return PEM_write_bio_PKCS8PrivateKey(bp, x, enc, (const char *)pass, pass_len,
cb, u);
}
EVP_PKEY *PEM_read_bio_Parameters(BIO *bio, EVP_PKEY **pkey) {
if (bio == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
char *nm = NULL;
unsigned char *data = NULL;
long len;
if (!PEM_bytes_read_bio(&data, &len, &nm, PEM_STRING_PARAMETERS, bio, 0,
NULL)) {
return NULL;
}
const unsigned char *data_const = data;
// Implementing the ASN1 logic here allows us to decouple the pem logic for
// |EVP_PKEY|. These correspond to the historical |param_decode|
// |EVP_PKEY_ASN1_METHOD| hooks in OpenSSL.
EVP_PKEY *ret = EVP_PKEY_new();
if (ret == NULL) {
goto err;
}
if (strcmp(nm, PEM_STRING_ECPARAMETERS) == 0) {
EC_KEY *ec_key = d2i_ECParameters(NULL, &data_const, len);
if (ec_key == NULL || !EVP_PKEY_assign_EC_KEY(ret, ec_key)) {
OPENSSL_PUT_ERROR(EVP, ERR_R_EC_LIB);
EC_KEY_free(ec_key);
goto err;
}
} else if (strcmp(nm, PEM_STRING_DSAPARAMS) == 0) {
DSA *dsa = d2i_DSAparams(NULL, &data_const, len);
if (dsa == NULL || !EVP_PKEY_assign_DSA(ret, dsa)) {
OPENSSL_PUT_ERROR(EVP, ERR_R_DSA_LIB);
DSA_free(dsa);
goto err;
}
} else if (strcmp(nm, PEM_STRING_DHPARAMS) == 0) {
DH *dh = d2i_DHparams(NULL, &data_const, len);
if (dh == NULL || !EVP_PKEY_assign_DH(ret, dh)) {
OPENSSL_PUT_ERROR(EVP, ERR_R_DH_LIB);
DH_free(dh);
goto err;
}
} else {
goto err;
}
if (pkey != NULL) {
EVP_PKEY_free(*pkey);
*pkey = ret;
}
OPENSSL_free(nm);
OPENSSL_free(data);
return ret;
err:
EVP_PKEY_free(ret);
OPENSSL_free(nm);
OPENSSL_free(data);
return NULL;
}
static int i2d_ECParameters_void(const void *key, uint8_t **out) {
return i2d_ECParameters((EC_KEY *)key, out);
}
static int i2d_DSAparams_void(const void *key, uint8_t **out) {
return i2d_DSAparams((DSA *)key, out);
}
static int i2d_DHparams_void(const void *key, uint8_t **out) {
return i2d_DHparams((DH *)key, out);
}
int PEM_write_bio_Parameters(BIO *bio, EVP_PKEY *pkey) {
if (bio == NULL || pkey == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
// Implementing the ASN1 logic here allows us to decouple the pem logic for
// |EVP_PKEY|. These correspond to the historical |param_encode|
// |EVP_PKEY_ASN1_METHOD| hooks in OpenSSL.
char pem_str[80];
switch (pkey->type) {
case EVP_PKEY_EC:
BIO_snprintf(pem_str, 80, PEM_STRING_ECPARAMETERS);
return PEM_ASN1_write_bio(i2d_ECParameters_void, pem_str, bio,
pkey->pkey.ec, NULL, NULL, 0, 0, NULL);
case EVP_PKEY_DSA:
BIO_snprintf(pem_str, 80, PEM_STRING_DSAPARAMS);
return PEM_ASN1_write_bio(i2d_DSAparams_void, pem_str, bio,
pkey->pkey.dsa, NULL, NULL, 0, 0, NULL);
case EVP_PKEY_DH:
BIO_snprintf(pem_str, 80, PEM_STRING_DHPARAMS);
return PEM_ASN1_write_bio(i2d_DHparams_void, pem_str, bio, pkey->pkey.dh,
NULL, NULL, 0, 0, NULL);
default:
return 0;
}
}
static int i2d_PrivateKey_void(const void *key, uint8_t **out) {
return i2d_PrivateKey((const EVP_PKEY *)key, out);
}
int PEM_write_bio_PrivateKey_traditional(BIO *bp, EVP_PKEY *x,
const EVP_CIPHER *enc,
unsigned char *kstr, int klen,
pem_password_cb *cb, void *u) {
if (bp == NULL || x == NULL || x->ameth == NULL ||
x->ameth->pem_str == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
char pem_str[80];
BIO_snprintf(pem_str, sizeof(pem_str), "%s PRIVATE KEY", x->ameth->pem_str);
return PEM_ASN1_write_bio(i2d_PrivateKey_void, pem_str, bp, x, enc, kstr,
klen, cb, u);
}
EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
void *u) {
BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
if (b == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return NULL;
}
EVP_PKEY *ret = PEM_read_bio_PrivateKey(b, x, cb, u);
BIO_free(b);
return ret;
}
int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
const unsigned char *pass, int pass_len,
pem_password_cb *cb, void *u) {
BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
if (b == NULL) {
OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
return 0;
}
int ret = PEM_write_bio_PrivateKey(b, x, enc, pass, pass_len, cb, u);
BIO_free(b);
return ret;
}

View File

@@ -0,0 +1,516 @@
// Copyright (c) 2018, Google Inc.
// SPDX-License-Identifier: ISC
#include <openssl/pem.h>
#include <gtest/gtest.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/cipher.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include "../test/test_util.h"
const char* SECRET = "test";
static int pem_password_callback(char *buf, int size, int rwflag, void *userdata) {
char* data = (char *)userdata;
if(size <= 0) {
return 0;
}
return (int)BUF_strlcpy(buf, data, size);
}
// Test that implausible ciphers, notably an IV-less RC4, aren't allowed in PEM.
// This is a regression test for https://github.com/openssl/openssl/issues/6347,
// though our fix differs from upstream.
TEST(PEMTest, NoRC4) {
static const char kPEM[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"Proc-Type: 4,ENCRYPTED\n"
"DEK-Info: RC4 -\n"
"extra-info\n"
"router-signature\n"
"\n"
"Z1w=\n"
"-----END RSA PUBLIC KEY-----\n";
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kPEM, sizeof(kPEM) - 1));
ASSERT_TRUE(bio);
bssl::UniquePtr<RSA> rsa(PEM_read_bio_RSAPublicKey(
bio.get(), nullptr, nullptr, const_cast<char *>("password")));
EXPECT_FALSE(rsa);
EXPECT_TRUE(
ErrorEquals(ERR_get_error(), ERR_LIB_PEM, PEM_R_UNSUPPORTED_ENCRYPTION));
}
static void* d2i_ASN1_INTEGER_void(void ** out, const unsigned char **inp, long len) {
return d2i_ASN1_INTEGER((ASN1_INTEGER **)out, inp, len);
}
static int i2d_ASN1_INTEGER_void(const void *a, unsigned char **out) {
return i2d_ASN1_INTEGER((ASN1_INTEGER *)a, out);
}
TEST(PEMTest, WriteReadASN1IntegerPem) {
#if defined(OPENSSL_ANDROID)
// On Android, when running from an APK, |tmpfile| does not work. See
// b/36991167#comment8.
GTEST_SKIP();
#endif
// Numbers for testing
std::vector<long> nums = {
0x00000001L,
0x00000100L,
0x00010000L,
0x01000000L,
-2L};
for(long original_value: nums) {
// Create an ASN1_INTEGER with value
bssl::UniquePtr<ASN1_INTEGER> asn1_int(ASN1_INTEGER_new());
ASSERT_TRUE(asn1_int);
ASSERT_TRUE(ASN1_INTEGER_set(asn1_int.get(), original_value));
// Create buffer for writing
TempFILE pem_file = createTempFILE();
ASSERT_TRUE(pem_file);
// Write the ASN1_INTEGER to a PEM-formatted string
ASSERT_TRUE(PEM_ASN1_write(i2d_ASN1_INTEGER_void, "ASN1 INTEGER",
pem_file.get(), asn1_int.get(), nullptr, nullptr,
0, nullptr, nullptr));
rewind(pem_file.get());
// Read the ASN1_INTEGER back from the PEM-formatted string
bssl::UniquePtr<ASN1_INTEGER> read_integer((ASN1_INTEGER *)PEM_ASN1_read(
d2i_ASN1_INTEGER_void, "ASN1 INTEGER", pem_file.get(), nullptr,
nullptr, nullptr));
ASSERT_TRUE(read_integer);
// Check if the read ASN1_INTEGER has the same value as the original
long read_value = ASN1_INTEGER_get(read_integer.get());
ASSERT_EQ(original_value, read_value);
}
}
const char* kPemRsaPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"
"MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhz3vU103jx3wICCAAw\n"
"DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEA6vMhRLgHZuHFa+eiecYCgEgZDB\n"
"E8EOzjGQuu4D0TVAjOa3Peb9/MzQz3t09m5pvNBFKrEl96gefpZdni5qQk34ukj9\n"
"/kryXymP72m4Ch7vbmew08x5x9L69BpfsQLF1yyvAJVtEZ0a1Zqcn5veuoH2WtJT\n"
"ZTrZtc5Eb+tAjMVzRXPZ80cUwCbbpb9KHUX8spwtG10I1VxJ18X31FVpGORdr0A=\n"
"-----END ENCRYPTED PRIVATE KEY-----";
TEST(PEMTest, ReadPrivateKeyPem) {
bssl::UniquePtr<BIO> read_bio(BIO_new_mem_buf(kPemRsaPrivateKey, BUF_strnlen(kPemRsaPrivateKey, 2048)) );
ASSERT_TRUE(read_bio);
bssl::UniquePtr<EC_KEY> ec_key(PEM_read_bio_ECPrivateKey(read_bio.get(), nullptr, pem_password_callback, (void*)SECRET));
ASSERT_TRUE(ec_key);
const EC_GROUP* p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
ASSERT_EQ(p256, EC_KEY_get0_group(ec_key.get()));
}
TEST(PEMTest, WriteReadRSAPem) {
bssl::UniquePtr<RSA> rsa(RSA_new());
ASSERT_TRUE(rsa);
bssl::UniquePtr<BIGNUM> bn(BN_new());
ASSERT_TRUE(bn);
BN_set_u64(bn.get(), RSA_F4);
#if defined(BORINGSSL_FIPS)
ASSERT_TRUE(RSA_generate_key_fips(rsa.get(), 2048, nullptr));
#else
ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), 2048, bn.get(), nullptr));
#endif
bssl::UniquePtr<BIO> write_bio(BIO_new(BIO_s_mem()));
ASSERT_TRUE(write_bio);
const EVP_CIPHER* cipher = EVP_get_cipherbynid(NID_aes_256_cbc);
ASSERT_TRUE(cipher);
ASSERT_TRUE(PEM_write_bio_RSAPrivateKey(write_bio.get(), rsa.get(), cipher, (unsigned char*)SECRET, (int)BUF_strnlen(SECRET, 256), nullptr, nullptr));
const uint8_t* content;
size_t content_len;
BIO_mem_contents(write_bio.get(), &content, &content_len);
bssl::UniquePtr<BIO> read_bio(BIO_new_mem_buf(content, content_len) );
ASSERT_TRUE(read_bio);
bssl::UniquePtr<RSA> rsa_read(PEM_read_bio_RSAPrivateKey(read_bio.get(), nullptr, pem_password_callback, (void*)SECRET));
ASSERT_TRUE(rsa_read);
ASSERT_EQ(0, BN_cmp(RSA_get0_n(rsa.get()), RSA_get0_n(rsa_read.get())));
}
TEST(PEMTest, WriteReadECPem) {
bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new());
ASSERT_TRUE(ec_key);
bssl::UniquePtr<EC_GROUP> ec_group(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
ASSERT_TRUE(ec_group);
ASSERT_TRUE(EC_KEY_set_group(ec_key.get(), ec_group.get()));
#if defined(BORINGSSL_FIPS)
ASSERT_TRUE(EC_KEY_generate_key_fips(ec_key.get()));
#else
ASSERT_TRUE(EC_KEY_generate_key(ec_key.get()));
#endif
bssl::UniquePtr<BIO> write_bio(BIO_new(BIO_s_mem()));
ASSERT_TRUE(write_bio);
const EVP_CIPHER* cipher = EVP_get_cipherbynid(NID_aes_256_cbc);
ASSERT_TRUE(cipher);
ASSERT_TRUE(PEM_write_bio_ECPrivateKey(write_bio.get(), ec_key.get(), cipher, nullptr, 0, pem_password_callback, (void*)SECRET));
const uint8_t* content;
size_t content_len;
BIO_mem_contents(write_bio.get(), &content, &content_len);
bssl::UniquePtr<BIO> read_bio(BIO_new_mem_buf(content, content_len) );
ASSERT_TRUE(read_bio);
bssl::UniquePtr<EC_KEY> ec_key_read(PEM_read_bio_ECPrivateKey(read_bio.get(), nullptr, pem_password_callback, (void*)"test"));
ASSERT_TRUE(ec_key_read);
const BIGNUM* orig_priv_key = EC_KEY_get0_private_key(ec_key.get());
const BIGNUM* read_priv_key = EC_KEY_get0_private_key(ec_key_read.get());
ASSERT_EQ(0, BN_cmp(orig_priv_key, read_priv_key));
}
const char *kPemECPARAMETERS =
"-----BEGIN EC PARAMETERS-----\n"
"BgUrgQQAIw==\n"
"-----END EC PARAMETERS-----\n";
const char *kPemExplictECPARAMETERS =
"-----BEGIN EC PARAMETERS-----\n"
"MIH3AgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////"
"/////zBbBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6"
"k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9+"
"kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT+NC4v4af5uO5+tK"
"fA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////AAAAAP//////////vOb6racXnoTz"
"ucrC/GMlUQIBAQ==\n"
"-----END EC PARAMETERS-----\n";
TEST(PEMTest, WriteReadECPKPem) {
// Check named curve can be outputted to a PEM file.
bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(NID_secp521r1));
ASSERT_TRUE(group);
bssl::UniquePtr<BIO> write_bio(BIO_new(BIO_s_mem()));
ASSERT_TRUE(write_bio);
ASSERT_TRUE(PEM_write_bio_ECPKParameters(write_bio.get(), group.get()));
const uint8_t *content;
size_t content_len;
BIO_mem_contents(write_bio.get(), &content, &content_len);
EXPECT_EQ(Bytes(content, content_len), Bytes(kPemECPARAMETERS));
// Check named curve of a PEM file can be parsed.
bssl::UniquePtr<BIO> read_bio(
BIO_new_mem_buf(kPemECPARAMETERS, strlen(kPemECPARAMETERS)));
bssl::UniquePtr<EC_GROUP> read_group(
PEM_read_bio_ECPKParameters(read_bio.get(), nullptr, nullptr, nullptr));
ASSERT_TRUE(read_group);
ASSERT_EQ(EC_GROUP_cmp(EC_group_p521(), read_group.get(), nullptr), 0);
// Make an arbitrary curve which is identical to P-256.
static const uint8_t kP[] = {
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
static const uint8_t kA[] = {
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
};
static const uint8_t kB[] = {
0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd,
0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b,
};
bssl::UniquePtr<BIGNUM> p(BN_bin2bn(kP, sizeof(kP), nullptr)),
a(BN_bin2bn(kA, sizeof(kA), nullptr)),
b(BN_bin2bn(kB, sizeof(kB), nullptr));
ASSERT_TRUE(p && a && b);
// Writing custom curves, even if the parameters are identical to a named
// curve, will result in an error
bssl::UniquePtr<EC_GROUP> custom_group(
EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), nullptr));
write_bio.reset(BIO_new(BIO_s_mem()));
ASSERT_TRUE(write_bio);
EXPECT_FALSE(
PEM_write_bio_ECPKParameters(write_bio.get(), custom_group.get()));
// Check that explicitly-encoded versions of namedCurves can be correctly
// parsed from a PEM file.
read_bio.reset(BIO_new_mem_buf(
kPemExplictECPARAMETERS, strlen(kPemExplictECPARAMETERS)));
read_group.reset(
PEM_read_bio_ECPKParameters(read_bio.get(), nullptr, nullptr, nullptr));
ASSERT_TRUE(read_group);
ASSERT_EQ(EC_GROUP_cmp(EC_group_p256(), read_group.get(), nullptr), 0);
}
TEST(ParametersTest, PEMReadwrite) {
// Test |PEM_read/write_bio_Parameters| with |EC_KEY|.
bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new());
ASSERT_TRUE(ec_key);
bssl::UniquePtr<EC_GROUP> ec_group(EC_GROUP_new_by_curve_name(NID_secp384r1));
ASSERT_TRUE(ec_group);
ASSERT_TRUE(EC_KEY_set_group(ec_key.get(), ec_group.get()));
ASSERT_TRUE(EC_KEY_generate_key(ec_key.get()));
bssl::UniquePtr<BIO> write_bio(BIO_new(BIO_s_mem()));
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()));
EXPECT_TRUE(PEM_write_bio_Parameters(write_bio.get(), pkey.get()));
const uint8_t *content;
size_t content_len;
BIO_mem_contents(write_bio.get(), &content, &content_len);
bssl::UniquePtr<BIO> read_bio(BIO_new_mem_buf(content, content_len));
ASSERT_TRUE(read_bio);
bssl::UniquePtr<EVP_PKEY> pkey_read(
PEM_read_bio_Parameters(read_bio.get(), nullptr));
ASSERT_TRUE(pkey_read);
EC_KEY *pkey_eckey = EVP_PKEY_get0_EC_KEY(pkey.get());
ASSERT_TRUE(pkey_eckey);
const EC_GROUP *orig_params = EC_KEY_get0_group(pkey_eckey);
ASSERT_TRUE(orig_params);
const EC_GROUP *read_params = EC_KEY_get0_group(pkey_eckey);
ASSERT_TRUE(read_params);
ASSERT_EQ(0, EC_GROUP_cmp(orig_params, read_params, nullptr));
// Test |PEM_read/write_bio_Parameters| with |DH|.
bssl::UniquePtr<BIGNUM> p(BN_get_rfc3526_prime_1536(nullptr));
ASSERT_TRUE(p);
bssl::UniquePtr<BIGNUM> g(BN_new());
ASSERT_TRUE(g);
ASSERT_TRUE(BN_set_u64(g.get(), 2));
bssl::UniquePtr<DH> dh(DH_new());
ASSERT_TRUE(dh);
ASSERT_TRUE(DH_set0_pqg(dh.get(), p.release(), nullptr, g.release()));
write_bio.reset(BIO_new(BIO_s_mem()));
pkey.reset(EVP_PKEY_new());
ASSERT_TRUE(EVP_PKEY_set1_DH(pkey.get(), dh.get()));
EXPECT_TRUE(PEM_write_bio_Parameters(write_bio.get(), pkey.get()));
BIO_mem_contents(write_bio.get(), &content, &content_len);
read_bio.reset(BIO_new_mem_buf(content, content_len));
ASSERT_TRUE(read_bio);
pkey_read.reset(PEM_read_bio_Parameters(read_bio.get(), nullptr));
ASSERT_TRUE(pkey_read);
DH *pkey_dh = EVP_PKEY_get0_DH(pkey.get());
ASSERT_TRUE(pkey_dh);
EXPECT_EQ(0, BN_cmp(DH_get0_p(pkey_dh), DH_get0_p(dh.get())));
EXPECT_EQ(0, BN_cmp(DH_get0_g(pkey_dh), DH_get0_g(dh.get())));
// Test |PEM_read/write_bio_Parameters| with |DSA|.
bssl::UniquePtr<DSA> dsa(DSA_new());
ASSERT_TRUE(dsa);
uint8_t seed[20];
ASSERT_TRUE(RAND_bytes(seed, sizeof(seed)));
ASSERT_TRUE(DSA_generate_parameters_ex(dsa.get(), 512, seed, sizeof(seed),
nullptr, nullptr, nullptr));
ASSERT_TRUE(DSA_generate_key(dsa.get()));
write_bio.reset(BIO_new(BIO_s_mem()));
pkey.reset(EVP_PKEY_new());
ASSERT_TRUE(EVP_PKEY_set1_DSA(pkey.get(), dsa.get()));
EXPECT_TRUE(PEM_write_bio_Parameters(write_bio.get(), pkey.get()));
BIO_mem_contents(write_bio.get(), &content, &content_len);
read_bio.reset(BIO_new_mem_buf(content, content_len));
ASSERT_TRUE(read_bio);
pkey_read.reset(PEM_read_bio_Parameters(read_bio.get(), nullptr));
ASSERT_TRUE(pkey_read);
DSA *pkey_dsa = EVP_PKEY_get0_DSA(pkey.get());
EXPECT_EQ(0, BN_cmp(DSA_get0_p(pkey_dsa), DSA_get0_p(dsa.get())));
EXPECT_EQ(0, BN_cmp(DSA_get0_g(pkey_dsa), DSA_get0_g(dsa.get())));
}
const char *kRubyPemDHPARAMETERS =
"-----BEGIN DH PARAMETERS-----\n"
"MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY"
"JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab"
"VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6"
"YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3"
"1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD"
"7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==\n"
"-----END DH PARAMETERS-----\n";
TEST(ParametersTest, RubyDHFile) {
bssl::UniquePtr<BIO> read_bio(
BIO_new_mem_buf(kRubyPemDHPARAMETERS, strlen(kRubyPemDHPARAMETERS)));
ASSERT_TRUE(read_bio);
bssl::UniquePtr<EVP_PKEY> pkey_read(
PEM_read_bio_Parameters(read_bio.get(), nullptr));
ASSERT_TRUE(pkey_read);
bssl::UniquePtr<DH> dh(EVP_PKEY_get1_DH(pkey_read.get()));
EXPECT_TRUE(dh);
EXPECT_EQ(DH_num_bits(dh.get()), 2048u);
}
TEST(PEMTest, WriteReadTraditionalPem) {
// Test |PEM_write_bio_PrivateKey_traditional| with |EC_KEY|.
bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new());
ASSERT_TRUE(ec_key);
bssl::UniquePtr<EC_GROUP> ec_group(EC_GROUP_new_by_curve_name(NID_secp256k1));
ASSERT_TRUE(ec_group);
ASSERT_TRUE(EC_KEY_set_group(ec_key.get(), ec_group.get()));
ASSERT_TRUE(EC_KEY_generate_key(ec_key.get()));
bssl::UniquePtr<BIO> write_bio(BIO_new(BIO_s_mem()));
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()));
EXPECT_TRUE(PEM_write_bio_PrivateKey_traditional(
write_bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr));
const uint8_t *content;
size_t content_len;
BIO_mem_contents(write_bio.get(), &content, &content_len);
bssl::UniquePtr<BIO> read_bio(BIO_new_mem_buf(content, content_len));
ASSERT_TRUE(read_bio);
bssl::UniquePtr<EVP_PKEY> pkey_read(
PEM_read_bio_PrivateKey(read_bio.get(), nullptr, nullptr, nullptr));
ASSERT_TRUE(pkey_read);
EC_KEY *pkey_eckey = EVP_PKEY_get0_EC_KEY(pkey.get());
ASSERT_TRUE(pkey_eckey);
const BIGNUM *orig_priv_key = EC_KEY_get0_private_key(ec_key.get());
const BIGNUM *read_priv_key = EC_KEY_get0_private_key(pkey_eckey);
ASSERT_EQ(0, BN_cmp(orig_priv_key, read_priv_key));
// Test |PEM_write_bio_PrivateKey_traditional| with |RSA|.
bssl::UniquePtr<BIGNUM> e(BN_new());
ASSERT_TRUE(e);
ASSERT_TRUE(BN_set_word(e.get(), RSA_F4));
bssl::UniquePtr<RSA> rsa(RSA_new());
ASSERT_TRUE(rsa);
ASSERT_TRUE(RSA_generate_key_ex(rsa.get(), 1024, e.get(), nullptr));
write_bio.reset(BIO_new(BIO_s_mem()));
pkey.reset(EVP_PKEY_new());
ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()));
EXPECT_TRUE(PEM_write_bio_PrivateKey_traditional(
write_bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr));
BIO_mem_contents(write_bio.get(), &content, &content_len);
read_bio.reset(BIO_new_mem_buf(content, content_len));
ASSERT_TRUE(read_bio);
pkey_read.reset(
PEM_read_bio_PrivateKey(read_bio.get(), nullptr, nullptr, nullptr));
ASSERT_TRUE(pkey_read);
RSA *pkey_rsa = EVP_PKEY_get0_RSA(pkey.get());
ASSERT_TRUE(pkey_rsa);
EXPECT_EQ(0, BN_cmp(RSA_get0_d(pkey_rsa), RSA_get0_d(rsa.get())));
EXPECT_EQ(0, BN_cmp(RSA_get0_d(pkey_rsa), RSA_get0_d(rsa.get())));
// Test |PEM_write_bio_PrivateKey_traditional| with |DSA|.
bssl::UniquePtr<DSA> dsa(DSA_new());
ASSERT_TRUE(dsa);
uint8_t seed[20];
ASSERT_TRUE(RAND_bytes(seed, sizeof(seed)));
ASSERT_TRUE(DSA_generate_parameters_ex(dsa.get(), 512, seed, sizeof(seed),
nullptr, nullptr, nullptr));
ASSERT_TRUE(DSA_generate_key(dsa.get()));
write_bio.reset(BIO_new(BIO_s_mem()));
pkey.reset(EVP_PKEY_new());
ASSERT_TRUE(EVP_PKEY_set1_DSA(pkey.get(), dsa.get()));
EXPECT_TRUE(PEM_write_bio_PrivateKey_traditional(
write_bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr));
BIO_mem_contents(write_bio.get(), &content, &content_len);
read_bio.reset(BIO_new_mem_buf(content, content_len));
ASSERT_TRUE(read_bio);
pkey_read.reset(
PEM_read_bio_PrivateKey(read_bio.get(), nullptr, nullptr, nullptr));
ASSERT_TRUE(pkey_read);
DSA *pkey_dsa = EVP_PKEY_get0_DSA(pkey.get());
EXPECT_EQ(0, BN_cmp(DSA_get0_priv_key(pkey_dsa), DSA_get0_priv_key(dsa.get())));
// Test |PEM_write_bio_PrivateKey_traditional| with |DH|. This should fail,
// since it's not supported by the API.
bssl::UniquePtr<BIGNUM> p(BN_get_rfc3526_prime_1536(nullptr));
ASSERT_TRUE(p);
bssl::UniquePtr<BIGNUM> g(BN_new());
ASSERT_TRUE(g);
ASSERT_TRUE(BN_set_u64(g.get(), 2));
bssl::UniquePtr<DH> dh(DH_new());
ASSERT_TRUE(dh);
ASSERT_TRUE(DH_set0_pqg(dh.get(), p.release(), nullptr, g.release()));
write_bio.reset(BIO_new(BIO_s_mem()));
pkey.reset(EVP_PKEY_new());
ASSERT_TRUE(EVP_PKEY_set1_DH(pkey.get(), dh.get()));
EXPECT_FALSE(PEM_write_bio_PrivateKey_traditional(
write_bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr));
}
TEST(PEMTest, WriteReadECPemEmptyPassword) {
bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new());
ASSERT_TRUE(ec_key);
bssl::UniquePtr<EC_GROUP> ec_group(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
ASSERT_TRUE(ec_group);
ASSERT_TRUE(EC_KEY_set_group(ec_key.get(), ec_group.get()));
#if defined(BORINGSSL_FIPS)
ASSERT_TRUE(EC_KEY_generate_key_fips(ec_key.get()));
#else
ASSERT_TRUE(EC_KEY_generate_key(ec_key.get()));
#endif
bssl::UniquePtr<BIO> write_bio(BIO_new(BIO_s_mem()));
ASSERT_TRUE(write_bio);
const EVP_CIPHER* cipher = EVP_get_cipherbynid(NID_aes_256_cbc);
ASSERT_TRUE(cipher);
ASSERT_TRUE(PEM_write_bio_ECPrivateKey(write_bio.get(), ec_key.get(), cipher, nullptr, 0, pem_password_callback, (void*)""));
const uint8_t* content = nullptr;
size_t content_len = 0;
BIO_mem_contents(write_bio.get(), &content, &content_len);
bssl::UniquePtr<BIO> read_bio(BIO_new_mem_buf(content, content_len) );
ASSERT_TRUE(read_bio);
bssl::UniquePtr<EC_KEY> ec_key_read(PEM_read_bio_ECPrivateKey(read_bio.get(), nullptr, pem_password_callback, (void*)""));
ASSERT_TRUE(ec_key_read);
const BIGNUM* orig_priv_key = EC_KEY_get0_private_key(ec_key.get());
const BIGNUM* read_priv_key = EC_KEY_get0_private_key(ec_key_read.get());
ASSERT_EQ(0, BN_cmp(orig_priv_key, read_priv_key));
}
TEST(PEMTest, WriteReadPKCS8DerEmptyPassword) {
bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
ASSERT_TRUE(ec_key);
ASSERT_TRUE(EC_KEY_generate_key(ec_key.get()));
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
ASSERT_TRUE(pkey);
ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()));
bssl::UniquePtr<BIO> write_bio(BIO_new(BIO_s_mem()));
ASSERT_TRUE(write_bio);
ASSERT_TRUE(i2d_PKCS8PrivateKey_bio(write_bio.get(), pkey.get(), EVP_aes_256_cbc(), nullptr, 0, pem_password_callback, (void*)""));
const uint8_t* content = nullptr;
size_t content_len = 0;
BIO_mem_contents(write_bio.get(), &content, &content_len);
bssl::UniquePtr<BIO> read_bio(BIO_new_mem_buf(content, content_len) );
ASSERT_TRUE(read_bio);
bssl::UniquePtr<EVP_PKEY> pkey_read(d2i_PKCS8PrivateKey_bio(read_bio.get(), nullptr, pem_password_callback, (void*)""));
ASSERT_TRUE(pkey_read);
const EC_KEY* read_ec = EVP_PKEY_get0_EC_KEY(pkey_read.get());
ASSERT_TRUE(read_ec);
ASSERT_EQ(0, BN_cmp(EC_KEY_get0_private_key(ec_key.get()), EC_KEY_get0_private_key(read_ec)));
}

View File

@@ -0,0 +1,12 @@
// Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project 2001.
// Copyright (c) 2001 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
IMPLEMENT_PEM_rw(X509, X509, PEM_STRING_X509, X509)

View File

@@ -0,0 +1,12 @@
// Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project 2001.
// Copyright (c) 2001 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
IMPLEMENT_PEM_rw(X509_AUX, X509, PEM_STRING_X509_TRUSTED, X509_AUX)