Branch data Line data Source code
1 : : /*
2 : : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License").
5 : : * You may not use this file except in compliance with the License.
6 : : * A copy of the License is located at
7 : : *
8 : : * http://aws.amazon.com/apache2.0
9 : : *
10 : : * or in the "license" file accompanying this file. This file is distributed
11 : : * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 : : * express or implied. See the License for the specific language governing
13 : : * permissions and limitations under the License.
14 : : */
15 : :
16 : : #include <openssl/evp.h>
17 : : #include <string.h>
18 : :
19 : : #include "error/s2n_errno.h"
20 : : #include "stuffer/s2n_stuffer.h"
21 : : #include "utils/s2n_safety.h"
22 : :
23 : : #define S2N_PEM_DELIMITER_CHAR '-'
24 : : #define S2N_PEM_DELIMITER_TOKEN "--"
25 : : #define S2N_PEM_DELIMITER_MIN_COUNT 2
26 : : #define S2N_PEM_DELIMITER_MAX_COUNT 64
27 : 6410 : #define S2N_PEM_BEGIN_TOKEN "BEGIN "
28 : 12976 : #define S2N_PEM_END_TOKEN "END "
29 : 553 : #define S2N_PEM_PKCS1_RSA_PRIVATE_KEY "RSA PRIVATE KEY"
30 : 501 : #define S2N_PEM_PKCS1_EC_PRIVATE_KEY "EC PRIVATE KEY"
31 : 349 : #define S2N_PEM_PKCS8_PRIVATE_KEY "PRIVATE KEY"
32 : 83 : #define S2N_PEM_DH_PARAMETERS "DH PARAMETERS"
33 : 501 : #define S2N_PEM_EC_PARAMETERS "EC PARAMETERS"
34 : 1824 : #define S2N_PEM_CERTIFICATE "CERTIFICATE"
35 : 10 : #define S2N_PEM_CRL "X509 CRL"
36 : :
37 : : static S2N_RESULT s2n_stuffer_pem_read_delimiter_chars(struct s2n_stuffer *pem)
38 : 8624 : {
39 [ - + ][ # # ]: 8624 : RESULT_ENSURE_REF(pem);
40 [ + - ][ + + ]: 8624 : RESULT_ENSURE(s2n_stuffer_data_available(pem) >= S2N_PEM_DELIMITER_MIN_COUNT,
41 : 7961 : S2N_ERR_INVALID_PEM);
42 : :
43 : : /* Skip any number of chars until a "--" is reached.
44 : : * We use "--" instead of "-" to account for dashes that appear in comments.
45 : : * We do not accept comments that contain "--".
46 : : */
47 [ + + ]: 7961 : RESULT_GUARD_POSIX(s2n_stuffer_skip_read_until(pem, S2N_PEM_DELIMITER_TOKEN));
48 [ - + ]: 7959 : RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(pem, strlen(S2N_PEM_DELIMITER_TOKEN)));
49 : :
50 : : /* Ensure between 2 and 64 '-' chars at start of line. */
51 [ - + ]: 7959 : RESULT_GUARD_POSIX(s2n_stuffer_skip_expected_char(pem, S2N_PEM_DELIMITER_CHAR,
52 : 7959 : S2N_PEM_DELIMITER_MIN_COUNT, S2N_PEM_DELIMITER_MAX_COUNT, NULL));
53 : :
54 : 7959 : return S2N_RESULT_OK;
55 : 7959 : }
56 : :
57 : : static int s2n_stuffer_pem_read_encapsulation_line(struct s2n_stuffer *pem, const char *encap_marker,
58 : : const char *keyword)
59 : 6415 : {
60 [ + + ]: 6415 : POSIX_GUARD_RESULT(s2n_stuffer_pem_read_delimiter_chars(pem));
61 : :
62 : : /* Ensure next string in stuffer is "BEGIN " or "END " */
63 [ + + ]: 6414 : POSIX_GUARD(s2n_stuffer_read_expected_str(pem, encap_marker));
64 : :
65 : : /* Ensure next string is stuffer is the keyword (Eg "CERTIFICATE", "PRIVATE KEY", etc) */
66 [ + + ]: 6399 : POSIX_GUARD(s2n_stuffer_read_expected_str(pem, keyword));
67 : :
68 : : /* Ensure between 2 and 64 '-' chars at end of line */
69 [ + + ]: 5195 : POSIX_GUARD(s2n_stuffer_skip_expected_char(pem, S2N_PEM_DELIMITER_CHAR, S2N_PEM_DELIMITER_MIN_COUNT,
70 : 5191 : S2N_PEM_DELIMITER_MAX_COUNT, NULL));
71 : :
72 : : /* Check for missing newline between dashes case: "-----END CERTIFICATE----------BEGIN CERTIFICATE-----" */
73 [ + + ]: 5191 : if (strncmp(encap_marker, S2N_PEM_END_TOKEN, strlen(S2N_PEM_END_TOKEN)) == 0
74 [ + + ]: 5191 : && s2n_stuffer_peek_check_for_str(pem, S2N_PEM_BEGIN_TOKEN) == S2N_SUCCESS) {
75 : : /* Rewind stuffer by 2 bytes before BEGIN, so that next read will find the dashes before the BEGIN */
76 [ - + ]: 7 : POSIX_GUARD(s2n_stuffer_rewind_read(pem, S2N_PEM_DELIMITER_MIN_COUNT));
77 : 7 : }
78 : :
79 : : /* Skip newlines and other whitespace that may be after the dashes */
80 : 5191 : return s2n_stuffer_skip_whitespace(pem, NULL);
81 : 5191 : }
82 : :
83 : : static int s2n_stuffer_pem_read_begin(struct s2n_stuffer *pem, const char *keyword)
84 : 3821 : {
85 : 3821 : return s2n_stuffer_pem_read_encapsulation_line(pem, S2N_PEM_BEGIN_TOKEN, keyword);
86 : 3821 : }
87 : :
88 : : static int s2n_stuffer_pem_read_end(struct s2n_stuffer *pem, const char *keyword)
89 : 2594 : {
90 : 2594 : return s2n_stuffer_pem_read_encapsulation_line(pem, S2N_PEM_END_TOKEN, keyword);
91 : 2594 : }
92 : :
93 : : static int s2n_stuffer_pem_read_contents(struct s2n_stuffer *pem, struct s2n_stuffer *asn1)
94 : 2602 : {
95 [ - + ][ # # ]: 2602 : s2n_stack_blob(base64__blob, 64, 64);
[ - + ]
96 : 2602 : struct s2n_stuffer base64_stuffer = { 0 };
97 [ - + ]: 2602 : POSIX_GUARD(s2n_stuffer_init(&base64_stuffer, &base64__blob));
98 : :
99 : 2768492 : while (1) {
100 : : /* We need a byte... */
101 [ + + ][ + - ]: 2768492 : POSIX_ENSURE(s2n_stuffer_data_available(pem) >= 1, S2N_ERR_STUFFER_OUT_OF_DATA);
102 : :
103 : : /* Peek to see if the next char is a dash, meaning end of pem_contents */
104 : 2768484 : uint8_t c = pem->blob.data[pem->read_cursor];
105 [ + + ]: 2768484 : if (c == '-') {
106 : 2594 : break;
107 : 2594 : }
108 : : /* Else, move read pointer forward by 1 byte since we will be consuming it. */
109 : 2765890 : pem->read_cursor += 1;
110 : :
111 : : /* Skip non-base64 characters */
112 [ + + ]: 2765890 : if (!s2n_is_base64_char(c)) {
113 : 42595 : continue;
114 : 42595 : }
115 : :
116 : : /* Flush base64_stuffer to asn1 stuffer if we're out of space, and reset base64_stuffer read/write pointers */
117 [ + + ]: 2723295 : if (s2n_stuffer_space_remaining(&base64_stuffer) == 0) {
118 [ - + ]: 41119 : POSIX_GUARD(s2n_stuffer_read_base64(&base64_stuffer, asn1));
119 [ - + ]: 41119 : POSIX_GUARD(s2n_stuffer_rewrite(&base64_stuffer));
120 : 41119 : }
121 : :
122 : : /* Copy next char to base64_stuffer */
123 [ - + ]: 2723295 : POSIX_GUARD(s2n_stuffer_write_bytes(&base64_stuffer, (uint8_t *) &c, 1));
124 : 2723295 : };
125 : :
126 : : /* Flush any remaining bytes to asn1 */
127 [ - + ]: 2594 : POSIX_GUARD(s2n_stuffer_read_base64(&base64_stuffer, asn1));
128 : :
129 : 2594 : return S2N_SUCCESS;
130 : 2594 : }
131 : :
132 : : static int s2n_stuffer_data_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1, const char *keyword)
133 : 3821 : {
134 [ - + ][ + - ]: 3821 : POSIX_PRECONDITION(s2n_stuffer_validate(pem));
135 [ - + ][ + - ]: 3821 : POSIX_PRECONDITION(s2n_stuffer_validate(asn1));
136 [ # # ][ - + ]: 3821 : POSIX_ENSURE_REF(keyword);
137 : :
138 [ + + ]: 3821 : POSIX_GUARD(s2n_stuffer_pem_read_begin(pem, keyword));
139 [ + + ]: 2602 : POSIX_GUARD(s2n_stuffer_pem_read_contents(pem, asn1));
140 [ + + ]: 2594 : POSIX_GUARD(s2n_stuffer_pem_read_end(pem, keyword));
141 : :
142 [ - + ][ + - ]: 2589 : POSIX_POSTCONDITION(s2n_stuffer_validate(pem));
143 [ - + ][ + - ]: 2589 : POSIX_POSTCONDITION(s2n_stuffer_validate(asn1));
144 : 2589 : return S2N_SUCCESS;
145 : 2589 : }
146 : :
147 : : int s2n_stuffer_private_key_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1, int *type_hint)
148 : 553 : {
149 [ - + ][ + - ]: 553 : POSIX_PRECONDITION(s2n_stuffer_validate(pem));
150 [ - + ][ + - ]: 553 : POSIX_PRECONDITION(s2n_stuffer_validate(asn1));
151 [ - + ][ # # ]: 553 : POSIX_ENSURE_REF(type_hint);
152 : :
153 [ + + ]: 553 : if (s2n_stuffer_data_from_pem(pem, asn1, S2N_PEM_PKCS1_RSA_PRIVATE_KEY) == S2N_SUCCESS) {
154 : 52 : *type_hint = EVP_PKEY_RSA;
155 : 52 : return S2N_SUCCESS;
156 : 52 : }
157 : :
158 [ - + ]: 501 : POSIX_GUARD(s2n_stuffer_reread(pem));
159 [ - + ]: 501 : POSIX_GUARD(s2n_stuffer_reread(asn1));
160 : :
161 : : /* By default, OpenSSL tools always generate both "EC PARAMETERS" and "EC PRIVATE
162 : : * KEY" PEM objects in the keyfile. Skip the first "EC PARAMETERS" object so that we're
163 : : * compatible with OpenSSL's default output, and since "EC PARAMETERS" is
164 : : * only needed for non-standard curves that aren't currently supported.
165 : : */
166 [ + + ]: 501 : if (s2n_stuffer_data_from_pem(pem, asn1, S2N_PEM_EC_PARAMETERS) != S2N_SUCCESS) {
167 [ - + ]: 350 : POSIX_GUARD(s2n_stuffer_reread(pem));
168 : 350 : }
169 [ - + ]: 501 : POSIX_GUARD(s2n_stuffer_wipe(asn1));
170 : :
171 [ + + ]: 501 : if (s2n_stuffer_data_from_pem(pem, asn1, S2N_PEM_PKCS1_EC_PRIVATE_KEY) == S2N_SUCCESS) {
172 : 152 : *type_hint = EVP_PKEY_EC;
173 : 152 : return S2N_SUCCESS;
174 : 152 : }
175 : :
176 : : /* If it does not match either format, try PKCS#8 */
177 [ - + ]: 349 : POSIX_GUARD(s2n_stuffer_reread(pem));
178 [ - + ]: 349 : POSIX_GUARD(s2n_stuffer_reread(asn1));
179 [ + + ]: 349 : if (s2n_stuffer_data_from_pem(pem, asn1, S2N_PEM_PKCS8_PRIVATE_KEY) == S2N_SUCCESS) {
180 : : /* This is the most generic format, and could represent keys other than RSA.
181 : : * We set RSA only as a default type hint.
182 : : */
183 : 347 : *type_hint = EVP_PKEY_RSA;
184 : 347 : return S2N_SUCCESS;
185 : 347 : }
186 : :
187 [ + - ]: 2 : POSIX_BAIL(S2N_ERR_INVALID_PEM);
188 : 2 : }
189 : :
190 : : /*
191 : : * We identify strings containing a PEM-encapsulated block by searching for the
192 : : * PEM delimiter chars.
193 : : *
194 : : * Although it isn't checked here, the delimiter should also imply the existence
195 : : * of the BEGIN keyword. This ensures that we don't ignore unexpected keywords
196 : : * like "END CERTIFICATE" instead of "BEGIN CERTIFICATE" or malformed keywords
197 : : * like "BEGAN CERTIFICATE" instead of "BEGIN CERTIFICATE".
198 : : */
199 : : bool s2n_stuffer_has_pem_encapsulated_block(struct s2n_stuffer *pem)
200 : 2209 : {
201 : : /* Operate on a copy of pem to avoid modifying pem */
202 : 2209 : struct s2n_stuffer pem_copy = *pem;
203 : 2209 : return s2n_result_is_ok(s2n_stuffer_pem_read_delimiter_chars(&pem_copy));
204 : 2209 : }
205 : :
206 : : int s2n_stuffer_certificate_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1)
207 : 1824 : {
208 : 1824 : return s2n_stuffer_data_from_pem(pem, asn1, S2N_PEM_CERTIFICATE);
209 : 1824 : }
210 : :
211 : : int s2n_stuffer_crl_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1)
212 : 10 : {
213 : 10 : return s2n_stuffer_data_from_pem(pem, asn1, S2N_PEM_CRL);
214 : 10 : }
215 : :
216 : : int s2n_stuffer_dhparams_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *pkcs3)
217 : 83 : {
218 : 83 : return s2n_stuffer_data_from_pem(pem, pkcs3, S2N_PEM_DH_PARAMETERS);
219 : 83 : }
|