Document low-level PEM functions Bug: 42290574 Change-Id: Ibaa45f269d9108574f310556e54435ad1615dbc0 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/97328 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/pem.h b/include/openssl/pem.h index b2b758f..991a147 100644 --- a/include/openssl/pem.h +++ b/include/openssl/pem.h
@@ -212,12 +212,16 @@ OPENSSL_EXPORT int PEM_write_X509_AUX(FILE *fp, const X509 *in); -// Not yet documented functions. +// Reading and writing raw PEM blocks. // -// TODO(crbug.com/42290574): Finish documenting and organizing this header. +// The functions in this section read and write PEM blocks without decoding +// their contents. +// PEM_BUFSIZE is an arbitrary buffer size used within the library. Some +// external callers depend on it being defined. #define PEM_BUFSIZE 1024 +// The following constants are a collection of known PEM types. #define PEM_STRING_X509_OLD "X509 CERTIFICATE" #define PEM_STRING_X509 "CERTIFICATE" #define PEM_STRING_X509_PAIR "CERTIFICATE PAIR" @@ -225,7 +229,6 @@ #define PEM_STRING_X509_REQ_OLD "NEW CERTIFICATE REQUEST" #define PEM_STRING_X509_REQ "CERTIFICATE REQUEST" #define PEM_STRING_X509_CRL "X509 CRL" -#define PEM_STRING_EVP_PKEY "ANY PRIVATE KEY" #define PEM_STRING_PUBLIC "PUBLIC KEY" #define PEM_STRING_RSA "RSA PRIVATE KEY" #define PEM_STRING_RSA_PUBLIC "RSA PUBLIC KEY" @@ -243,6 +246,129 @@ #define PEM_STRING_ECPRIVATEKEY "EC PRIVATE KEY" #define PEM_STRING_CMS "CMS" +// PEM_STRING_EVP_PKEY is not a PEM type, but is an implementation detail of +// `PEM_read_bio_PrivateKey`. +#define PEM_STRING_EVP_PKEY "ANY PRIVATE KEY" + +// PEM_read_bio reads from `bio` until the next PEM block. If one is found, it +// returns one and sets `*out_name`, `*out_header`, and `*out_data` to +// newly-allocated buffers containing the PEM type, the header block, and the +// decoded data, respectively. `*out_name` and `*out_header` are NUL-terminated +// C strings, while `*out_data` has `*out_len` bytes. The caller must release +// each of `*out_name`, `*out_header`, and `*out_data` with `OPENSSL_free` when +// done. +// +// If no PEM block is found, this function returns zero and pushes +// `PEM_R_NO_START_LINE` to the error queue. If one is found, but there is an +// error decoding it, it returns zero and pushes some other error to the error +// queue. +// +// This function does not decrypt encrypted PEM blocks and instead returns the +// header and (possibly encrypted) data unprocessed. See `PEM_bytes_read_bio` to +// decrypt blocks. +OPENSSL_EXPORT int PEM_read_bio(BIO *bio, char **out_name, char **out_header, + uint8_t **out_data, long *out_len); + +// PEM_read behaves like `PEM_read_bio` but reads from `fp`. +OPENSSL_EXPORT int PEM_read(FILE *fp, char **out_name, char **out_header, + uint8_t **out_data, long *out_len); + +// PEM_write_bio writes a PEM block to `bio`, containing `len` bytes from `data` +// as data. `name` and `hdr` are NUL-terminated C strings containing the PEM +// type and header block, respectively. This function returns zero on error and +// the number of bytes written on success. +OPENSSL_EXPORT int PEM_write_bio(BIO *bio, const char *name, const char *hdr, + const uint8_t *data, long len); + +// PEM_write behaves like `PEM_write_bio` but reads from `fp`. +OPENSSL_EXPORT int PEM_write(FILE *fp, const char *name, const char *hdr, + const uint8_t *data, long len); + +// PEM_bytes_read_bio reads from `bio` until it finds a PEM block whose name +// matches `expected_name`. If one is found, it sets `*out_name` and `*out_data` +// to newly-allocated buffers containing the PEM type and (possibly decrypted) +// PEM data. `*out_name` is a NUL-terminated C string, while `*out_data` has +// `*out_len` bytes. The caller must release `*out_name` and `*out_data` with +// `OPENSSL_free` when done. +// +// If the PEM block is encrypted, `cb` will be called to look up the password. +// See `pem_password_cb` for details. If `cb` is NULL, `PEM_def_callback` is +// used and `userdata` should be a NUL-terminated C string containing the +// password. Set both `cb` and `userdata` to NULL to only handle plaintext +// blocks. +// +// `expected_name` and `*out_name` may not necessarily be the same value, so +// callers must check `*out_name` before decoding `*out_data`. In addition to an +// exact match, the following values are also accepted: +// +// - If `expected_name` is "CERTIFICATE", the older "X509 CERTIFICATE" type is +// also accepted. +// +// - If `expected_name` is "CERTIFICATE REQUEST", the older "NEW CERTIFICATE +// REQUEST" type is also accepted. +// +// - If `expected_name` is "TRUSTED CERTIFICATE", "CERTIFICATE" and the older +// "X509 CERTIFICATE" are also accepted. +// +// - If `expected_name` is "PKCS7", "PKCS #7 SIGNED DATA" is also accepted. +// +// - If `expected_name` is "PKCS7", "CERTIFICATE" is also accepted. This is an +// exposed implementation detail of `PKCS7_get_PEM_certificates`, which works +// around a 2000-era mistake by some CAs. +// +// - If `expected_name` is "ANY PRIVATE KEY", the type "ANY PRIVATE KEY" is not +// accepted and, instead, the function accepts "PRIVATE KEY", "ENCRYPTED +// PRIVATE KEY", "RSA PRIVATE KEY", "EC PRIVATE KEY", and "DSA PRIVATE KEY". +// This is an exposed implementation detail of `PEM_read_bio_PrivateKey`. +// +// TODO(davidben): Can some of the older aliases and workarounds be removed now? +// +// If no PEM block is found, this function returns zero and pushes +// `PEM_R_NO_START_LINE` to the error queue. If one is found, but there is an +// error decoding it, it returns zero and pushes some other error to the error +// queue. +OPENSSL_EXPORT int PEM_bytes_read_bio(uint8_t **out_data, long *out_len, + char **out_name, + const char *expected_name, BIO *bio, + pem_password_cb *cb, void *userdata); + + +// Internal functions. +// +// The following functions are used to implement `PEM_read_bio_*` and +// `PEM_write_bio_*`. They should not be used outside the library. + +// PEM_ASN1_read_bio calls `PEM_bytes_read_bio` and then decodes the resulting +// data with `d2i`, which should behave as in `d2i_SAMPLE`. +OPENSSL_EXPORT void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, + BIO *bio, void **out, + pem_password_cb *cb, void *userdata); + +// PEM_ASN1_read behaves like `PEM_ASN1_read_bio` but reads from `fp`. +OPENSSL_EXPORT void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, + void **out, pem_password_cb *cb, + void *userdata); + +// PEM_ASN1_write_bio encodes `in` with `i2d`, which should behave as in +// `i2d_SAMPLE`. It then writes the result to `bio` as in +// `PEM_write_bio_SAMPLE`. +OPENSSL_EXPORT int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, + BIO *bio, const void *in, + const EVP_CIPHER *enc, + const uint8_t *pass, int pass_len, + pem_password_cb *cb, void *userdata); + +// PEM_ASN1_write behaves like `PEM_ASN1_write_bio` but reads from `fp`. +OPENSSL_EXPORT int PEM_ASN1_write(i2d_of_void *i2d, const char *name, FILE *fp, + const void *in, const EVP_CIPHER *enc, + const uint8_t *pass, int pass_len, + pem_password_cb *callback, void *userdata); + + +// Not yet documented functions. +// +// TODO(crbug.com/42290574): Finish documenting and organizing this header. + #define PEM_TYPE_ENCRYPTED 10 #define PEM_TYPE_MIC_ONLY 20 #define PEM_TYPE_MIC_CLEAR 30 @@ -294,48 +420,6 @@ DECLARE_PEM_read(name, type) \ DECLARE_PEM_write_cb(name, type) -// PEM_read_bio reads from `bp`, until the next PEM block. If one is found, it -// returns one and sets `*name`, `*header`, and `*data` to newly-allocated -// buffers containing the PEM type, the header block, and the decoded data, -// respectively. `*name` and `*header` are NUL-terminated C strings, while -// `*data` has `*len` bytes. The caller must release each of `*name`, `*header`, -// and `*data` with `OPENSSL_free` when done. If no PEM block is found, this -// function returns zero and pushes `PEM_R_NO_START_LINE` to the error queue. If -// one is found, but there is an error decoding it, it returns zero and pushes -// some other error to the error queue. -OPENSSL_EXPORT int PEM_read_bio(BIO *bp, char **name, char **header, - unsigned char **data, long *len); - -// PEM_write_bio writes a PEM block to `bp`, containing `len` bytes from `data` -// as data. `name` and `hdr` are NUL-terminated C strings containing the PEM -// type and header block, respectively. This function returns zero on error and -// the number of bytes written on success. -OPENSSL_EXPORT int PEM_write_bio(BIO *bp, const char *name, const char *hdr, - const unsigned char *data, long len); - -OPENSSL_EXPORT int PEM_bytes_read_bio(unsigned char **pdata, long *plen, - char **pnm, const char *expected_name, - BIO *bp, pem_password_cb *cb, void *u); -OPENSSL_EXPORT void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, - BIO *bp, void **x, pem_password_cb *cb, - void *u); -OPENSSL_EXPORT int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, - BIO *bp, const void *x, - const EVP_CIPHER *enc, - const unsigned char *pass, int pass_len, - pem_password_cb *cb, void *u); - -OPENSSL_EXPORT int PEM_read(FILE *fp, char **name, char **header, - unsigned char **data, long *len); -OPENSSL_EXPORT int PEM_write(FILE *fp, const char *name, const char *hdr, - const unsigned char *data, long len); -OPENSSL_EXPORT void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, - void **x, pem_password_cb *cb, void *u); -OPENSSL_EXPORT int PEM_ASN1_write(i2d_of_void *i2d, const char *name, FILE *fp, - const void *x, const EVP_CIPHER *enc, - const unsigned char *pass, int pass_len, - pem_password_cb *callback, void *u); - DECLARE_PEM_rw(X509_REQ, X509_REQ) DECLARE_PEM_write(X509_REQ_NEW, X509_REQ)