1/*
2 * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10#include <openssl/core_dispatch.h>
11#include <openssl/pem.h>
12#include <openssl/encoder.h>
13
14/*
15 * Selectors, named according to the ASN.1 names used throughout libcrypto.
16 *
17 * Note that these are not absolutely mandatory, they are rather a wishlist
18 * of sorts.  The provider implementations are free to make choices that
19 * make sense for them, based on these selectors.
20 * For example, the EC backend is likely to really just output the private
21 * key to a PKCS#8 structure, even thought PEM_SELECTION_PrivateKey specifies
22 * the public key as well.  This is fine, as long as the corresponding
23 * decoding operation can return an object that contains what libcrypto
24 * expects.
25 */
26# define PEM_SELECTION_PUBKEY           EVP_PKEY_PUBLIC_KEY
27# define PEM_SELECTION_PrivateKey       EVP_PKEY_KEYPAIR
28# define PEM_SELECTION_Parameters       EVP_PKEY_KEY_PARAMETERS
29
30/*
31 * Properties, named according to the ASN.1 names used throughout libcrypto.
32 */
33# define PEM_STRUCTURE_PUBKEY "SubjectPublicKeyInfo"
34# define PEM_STRUCTURE_PrivateKey "PrivateKeyInfo"
35# define PEM_STRUCTURE_Parameters "type-specific"
36
37# define PEM_STRUCTURE_RSAPrivateKey "type-specific"
38# define PEM_STRUCTURE_RSAPublicKey "type-specific"
39
40/* Alternative IMPLEMENT macros for provided encoders */
41
42# define IMPLEMENT_PEM_provided_write_body_vars(type, asn1, pq)         \
43    int ret = 0;                                                        \
44    OSSL_ENCODER_CTX *ctx =                                             \
45        OSSL_ENCODER_CTX_new_for_##type(x, PEM_SELECTION_##asn1,        \
46                                       "PEM", PEM_STRUCTURE_##asn1,     \
47                                       (pq));                           \
48                                                                        \
49    if (OSSL_ENCODER_CTX_get_num_encoders(ctx) == 0) {                  \
50        OSSL_ENCODER_CTX_free(ctx);                                     \
51        goto legacy;                                                    \
52    }
53# define IMPLEMENT_PEM_provided_write_body_pass()                       \
54    ret = 1;                                                            \
55    if (kstr == NULL && cb == NULL) {                                   \
56        if (u != NULL) {                                                \
57            kstr = u;                                                   \
58            klen = strlen(u);                                           \
59        } else {                                                        \
60            cb = PEM_def_callback;                                      \
61        }                                                               \
62    }                                                                   \
63    if (enc != NULL) {                                                  \
64        ret = 0;                                                        \
65        if (OSSL_ENCODER_CTX_set_cipher(ctx, EVP_CIPHER_get0_name(enc), \
66                                        NULL)) {                        \
67            ret = 1;                                                    \
68            if (kstr != NULL                                            \
69                && !OSSL_ENCODER_CTX_set_passphrase(ctx, kstr, klen))   \
70                ret = 0;                                                \
71            else if (cb != NULL                                         \
72                     && !OSSL_ENCODER_CTX_set_pem_password_cb(ctx,      \
73                                                              cb, u))   \
74                ret = 0;                                                \
75        }                                                               \
76    }                                                                   \
77    if (!ret) {                                                         \
78        OSSL_ENCODER_CTX_free(ctx);                                     \
79        return 0;                                                       \
80    }
81# define IMPLEMENT_PEM_provided_write_body_main(type, outtype)          \
82    ret = OSSL_ENCODER_to_##outtype(ctx, out);                          \
83    OSSL_ENCODER_CTX_free(ctx);                                         \
84    return ret
85# define IMPLEMENT_PEM_provided_write_body_fallback(str, asn1,          \
86                                                    writename)          \
87    legacy:                                                             \
88    return PEM_ASN1_##writename((i2d_of_void *)i2d_##asn1, str, out,    \
89                                x, NULL, NULL, 0, NULL, NULL)
90# define IMPLEMENT_PEM_provided_write_body_fallback_cb(str, asn1,       \
91                                                       writename)       \
92    legacy:                                                             \
93    return PEM_ASN1_##writename##((i2d_of_void *)i2d_##asn1, str, out,  \
94                                  x, enc, kstr, klen, cb, u)
95
96# define IMPLEMENT_PEM_provided_write_to(name, TYPE, type, str, asn1,   \
97                                         OUTTYPE, outtype, writename)   \
98    PEM_write_fnsig(name, TYPE, OUTTYPE, writename)                     \
99    {                                                                   \
100        IMPLEMENT_PEM_provided_write_body_vars(type, asn1, NULL);       \
101        IMPLEMENT_PEM_provided_write_body_main(type, outtype);          \
102        IMPLEMENT_PEM_provided_write_body_fallback(str, asn1,           \
103                                                   writename);          \
104    }                                                                   \
105    PEM_write_ex_fnsig(name, TYPE, OUTTYPE, writename)                  \
106    {                                                                   \
107        IMPLEMENT_PEM_provided_write_body_vars(type, asn1, propq);      \
108        IMPLEMENT_PEM_provided_write_body_main(type, outtype);          \
109        IMPLEMENT_PEM_provided_write_body_fallback(str, asn1,           \
110                                                   writename);          \
111    }
112
113
114# define IMPLEMENT_PEM_provided_write_cb_to(name, TYPE, type, str, asn1, \
115                                            OUTTYPE, outtype, writename) \
116    PEM_write_cb_fnsig(name, TYPE, OUTTYPE, writename)                  \
117    {                                                                   \
118        IMPLEMENT_PEM_provided_write_body_vars(type, asn1, NULL);       \
119        IMPLEMENT_PEM_provided_write_body_pass();                       \
120        IMPLEMENT_PEM_provided_write_body_main(type, outtype);          \
121        IMPLEMENT_PEM_provided_write_body_fallback_cb(str, asn1,        \
122                                                      writename);       \
123    }                                                                   \
124    PEM_write_ex_cb_fnsig(name, TYPE, OUTTYPE, writename)               \
125    {                                                                   \
126        IMPLEMENT_PEM_provided_write_body_vars(type, asn1, propq);      \
127        IMPLEMENT_PEM_provided_write_body_pass();                       \
128        IMPLEMENT_PEM_provided_write_body_main(type, outtype);          \
129        IMPLEMENT_PEM_provided_write_body_fallback(str, asn1,           \
130                                                   writename);          \
131    }
132
133# ifdef OPENSSL_NO_STDIO
134
135#  define IMPLEMENT_PEM_provided_write_fp(name, TYPE, type, str, asn1)
136#  define IMPLEMENT_PEM_provided_write_cb_fp(name, TYPE, type, str, asn1)
137
138# else
139
140#  define IMPLEMENT_PEM_provided_write_fp(name, TYPE, type, str, asn1)    \
141    IMPLEMENT_PEM_provided_write_to(name, TYPE, type, str, asn1, FILE, fp, write)
142#  define IMPLEMENT_PEM_provided_write_cb_fp(name, TYPE, type, str, asn1) \
143    IMPLEMENT_PEM_provided_write_cb_to(name, TYPE, type, str, asn1, FILE, fp, write)
144
145# endif
146
147# define IMPLEMENT_PEM_provided_write_bio(name, TYPE, type, str, asn1)    \
148    IMPLEMENT_PEM_provided_write_to(name, TYPE, type, str, asn1, BIO, bio, write_bio)
149# define IMPLEMENT_PEM_provided_write_cb_bio(name, TYPE, type, str, asn1) \
150    IMPLEMENT_PEM_provided_write_cb_to(name, TYPE, type, str, asn1, BIO, bio, write_bio)
151
152# define IMPLEMENT_PEM_provided_write(name, TYPE, type, str, asn1)        \
153    IMPLEMENT_PEM_provided_write_bio(name, TYPE, type, str, asn1)         \
154    IMPLEMENT_PEM_provided_write_fp(name, TYPE, type, str, asn1)
155
156# define IMPLEMENT_PEM_provided_write_cb(name, TYPE, type, str, asn1)     \
157    IMPLEMENT_PEM_provided_write_cb_bio(name, TYPE, type, str, asn1)      \
158    IMPLEMENT_PEM_provided_write_cb_fp(name, TYPE, type, str, asn1)
159
160# define IMPLEMENT_PEM_provided_rw(name, TYPE, type, str, asn1)           \
161    IMPLEMENT_PEM_read(name, TYPE, str, asn1)                             \
162    IMPLEMENT_PEM_provided_write(name, TYPE, type, str, asn1)
163
164# define IMPLEMENT_PEM_provided_rw_cb(name, TYPE, type, str, asn1)        \
165    IMPLEMENT_PEM_read(name, TYPE, str, asn1)                             \
166    IMPLEMENT_PEM_provided_write_cb(name, TYPE, type, str, asn1)
167
168