1/* BEGIN_HEADER */
2#include "pk_internal.h"
3#include "mbedtls/pem.h"
4#include "mbedtls/oid.h"
5#include "mbedtls/base64.h"
6#include "psa/crypto_sizes.h"
7
8typedef enum {
9    TEST_PEM,
10    TEST_DER
11} pkwrite_file_format_t;
12
13/* Helper function for removing "\r" chars from a buffer. */
14static void fix_new_lines(unsigned char *in_str, size_t *len)
15{
16    size_t chars_left;
17    unsigned int i;
18
19    for (i = 0; (i < *len) && (*len > 0); i++) {
20        if (in_str[i] == '\r') {
21            if (i < (*len - 1)) {
22                chars_left = *len - i - 1;
23                memmove(&in_str[i], &in_str[i+1], chars_left);
24            } else {
25                in_str[i] = '\0';
26            }
27            *len = *len - 1;
28        }
29    }
30}
31
32static int pk_write_any_key(mbedtls_pk_context *pk, unsigned char **p,
33                            size_t *buf_len, int is_public_key, int is_der)
34{
35    int ret = 0;
36
37    if (is_der) {
38        if (is_public_key) {
39            ret = mbedtls_pk_write_pubkey_der(pk, *p, *buf_len);
40        } else {
41            ret = mbedtls_pk_write_key_der(pk, *p, *buf_len);
42        }
43        if (ret <= 0) {
44            return ret;
45        }
46
47        *p = *p + *buf_len - ret;
48        *buf_len = ret;
49    } else {
50#if defined(MBEDTLS_PEM_WRITE_C)
51        if (is_public_key) {
52            ret = mbedtls_pk_write_pubkey_pem(pk, *p, *buf_len);
53        } else {
54            ret = mbedtls_pk_write_key_pem(pk, *p, *buf_len);
55        }
56        if (ret != 0) {
57            return ret;
58        }
59
60        *buf_len = strlen((char *) *p) + 1; /* +1 takes the string terminator into account */
61#else
62        return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
63#endif
64    }
65
66    return 0;
67}
68
69static void pk_write_check_common(char *key_file, int is_public_key, int is_der)
70{
71    mbedtls_pk_context key;
72    mbedtls_pk_init(&key);
73    unsigned char *buf = NULL;
74    unsigned char *check_buf = NULL;
75    unsigned char *start_buf;
76    size_t buf_len, check_buf_len;
77    int expected_result;
78#if defined(MBEDTLS_USE_PSA_CRYPTO)
79    mbedtls_svc_key_id_t opaque_id = MBEDTLS_SVC_KEY_ID_INIT;
80    psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
81#endif /* MBEDTLS_USE_PSA_CRYPTO */
82
83    USE_PSA_INIT();
84
85    /* Note: if mbedtls_pk_load_file() successfully reads the file, then
86       it also allocates check_buf, which should be freed on exit */
87    TEST_EQUAL(mbedtls_pk_load_file(key_file, &check_buf, &check_buf_len), 0);
88    TEST_ASSERT(check_buf_len > 0);
89
90    /* Windows' line ending is different from the Linux's one ("\r\n" vs "\n").
91     * Git treats PEM files as text, so when on Windows, it replaces new lines
92     * with "\r\n" on checkout.
93     * Unfortunately mbedtls_pk_load_file() loads files in binary format,
94     * while mbedtls_pk_write_pubkey_pem() goes through the I/O layer which
95     * uses "\n" for newlines in both Windows and Linux.
96     * Here we remove the extra "\r" so that "buf" and "check_buf" can be
97     * easily compared later. */
98    if (!is_der) {
99        fix_new_lines(check_buf, &check_buf_len);
100    }
101    TEST_ASSERT(check_buf_len > 0);
102
103    TEST_CALLOC(buf, check_buf_len);
104
105    if (is_public_key) {
106        TEST_EQUAL(mbedtls_pk_parse_public_keyfile(&key, key_file), 0);
107    } else {
108        TEST_EQUAL(mbedtls_pk_parse_keyfile(&key, key_file, NULL,
109                                            mbedtls_test_rnd_std_rand, NULL), 0);
110    }
111
112    start_buf = buf;
113    buf_len = check_buf_len;
114    if (is_der) {
115        expected_result = MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
116    } else {
117        expected_result = MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL;
118    }
119    /* Intentionally pass a wrong size for the provided output buffer and check
120     * that the writing functions fails as expected. */
121    for (size_t i = 1; i < buf_len; i++) {
122        TEST_EQUAL(pk_write_any_key(&key, &start_buf, &i, is_public_key,
123                                    is_der), expected_result);
124    }
125    TEST_EQUAL(pk_write_any_key(&key, &start_buf, &buf_len, is_public_key,
126                                is_der), 0);
127
128    TEST_MEMORY_COMPARE(start_buf, buf_len, check_buf, check_buf_len);
129
130#if defined(MBEDTLS_USE_PSA_CRYPTO)
131    /* Verify that pk_write works also for opaque private keys */
132    if (!is_public_key) {
133        memset(buf, 0, check_buf_len);
134        /* Turn the key PK context into an opaque one.
135         * Note: set some practical usage for the key to make get_psa_attributes() happy. */
136        TEST_EQUAL(mbedtls_pk_get_psa_attributes(&key, PSA_KEY_USAGE_SIGN_MESSAGE, &key_attr), 0);
137        TEST_EQUAL(mbedtls_pk_import_into_psa(&key, &key_attr, &opaque_id), 0);
138        mbedtls_pk_free(&key);
139        mbedtls_pk_init(&key);
140        TEST_EQUAL(mbedtls_pk_setup_opaque(&key, opaque_id), 0);
141        start_buf = buf;
142        buf_len = check_buf_len;
143        /* Intentionally pass a wrong size for the provided output buffer and check
144         * that the writing functions fails as expected. */
145        for (size_t i = 1; i < buf_len; i++) {
146            TEST_EQUAL(pk_write_any_key(&key, &start_buf, &i, is_public_key,
147                                        is_der), expected_result);
148        }
149        TEST_EQUAL(pk_write_any_key(&key, &start_buf, &buf_len, is_public_key,
150                                    is_der), 0);
151
152        TEST_MEMORY_COMPARE(start_buf, buf_len, check_buf, check_buf_len);
153    }
154#endif /* MBEDTLS_USE_PSA_CRYPTO */
155
156exit:
157#if defined(MBEDTLS_USE_PSA_CRYPTO)
158    psa_destroy_key(opaque_id);
159#endif /* MBEDTLS_USE_PSA_CRYPTO */
160    mbedtls_free(buf);
161    mbedtls_free(check_buf);
162    mbedtls_pk_free(&key);
163    USE_PSA_DONE();
164}
165/* END_HEADER */
166
167/* BEGIN_DEPENDENCIES
168 * depends_on:MBEDTLS_PK_PARSE_C:MBEDTLS_PK_WRITE_C:MBEDTLS_FS_IO
169 * END_DEPENDENCIES
170 */
171
172/* BEGIN_CASE */
173void pk_write_pubkey_check(char *key_file, int is_der)
174{
175    pk_write_check_common(key_file, 1, is_der);
176    goto exit; /* make the compiler happy */
177}
178/* END_CASE */
179
180/* BEGIN_CASE */
181void pk_write_key_check(char *key_file, int is_der)
182{
183    pk_write_check_common(key_file, 0, is_der);
184    goto exit; /* make the compiler happy */
185}
186/* END_CASE */
187
188/* BEGIN_CASE */
189void pk_write_public_from_private(char *priv_key_file, char *pub_key_file)
190{
191    mbedtls_pk_context priv_key;
192    uint8_t *derived_key_raw = NULL;
193    size_t derived_key_len = 0;
194    uint8_t *pub_key_raw = NULL;
195    size_t pub_key_len = 0;
196#if defined(MBEDTLS_USE_PSA_CRYPTO)
197    mbedtls_svc_key_id_t opaque_key_id = MBEDTLS_SVC_KEY_ID_INIT;
198    psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
199#endif /* MBEDTLS_USE_PSA_CRYPTO */
200
201    mbedtls_pk_init(&priv_key);
202    USE_PSA_INIT();
203
204    TEST_EQUAL(mbedtls_pk_parse_keyfile(&priv_key, priv_key_file, NULL,
205                                        mbedtls_test_rnd_std_rand, NULL), 0);
206    TEST_EQUAL(mbedtls_pk_load_file(pub_key_file, &pub_key_raw,
207                                    &pub_key_len), 0);
208
209    derived_key_len = pub_key_len;
210    TEST_CALLOC(derived_key_raw, derived_key_len);
211
212    TEST_EQUAL(mbedtls_pk_write_pubkey_der(&priv_key, derived_key_raw,
213                                           derived_key_len), pub_key_len);
214
215    TEST_MEMORY_COMPARE(derived_key_raw, derived_key_len,
216                        pub_key_raw, pub_key_len);
217
218#if defined(MBEDTLS_USE_PSA_CRYPTO)
219    mbedtls_platform_zeroize(derived_key_raw, derived_key_len);
220
221    /* Turn the priv_key PK context into an opaque one. */
222    TEST_EQUAL(mbedtls_pk_get_psa_attributes(&priv_key, PSA_KEY_USAGE_SIGN_HASH, &key_attr), 0);
223    TEST_EQUAL(mbedtls_pk_import_into_psa(&priv_key, &key_attr, &opaque_key_id), 0);
224    mbedtls_pk_free(&priv_key);
225    mbedtls_pk_init(&priv_key);
226    TEST_EQUAL(mbedtls_pk_setup_opaque(&priv_key, opaque_key_id), 0);
227
228    TEST_EQUAL(mbedtls_pk_write_pubkey_der(&priv_key, derived_key_raw,
229                                           derived_key_len), pub_key_len);
230
231    TEST_MEMORY_COMPARE(derived_key_raw, derived_key_len,
232                        pub_key_raw, pub_key_len);
233#endif /* MBEDTLS_USE_PSA_CRYPTO */
234
235exit:
236#if defined(MBEDTLS_USE_PSA_CRYPTO)
237    psa_destroy_key(opaque_key_id);
238#endif /* MBEDTLS_USE_PSA_CRYPTO */
239    mbedtls_free(derived_key_raw);
240    mbedtls_free(pub_key_raw);
241    mbedtls_pk_free(&priv_key);
242    USE_PSA_DONE();
243}
244/* END_CASE */
245