1 /*
2  * Copyright (C) 2018 Linaro Ltd
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include <zephyr/types.h>
9 #include <errno.h>
10 
11 #include <zephyr/data/jwt.h>
12 #include <zephyr/data/json.h>
13 
14 #ifdef CONFIG_JWT_SIGN_RSA
15 #include <mbedtls/pk.h>
16 #include <mbedtls/rsa.h>
17 #include <mbedtls/sha256.h>
18 #include <zephyr/random/random.h>
19 #endif
20 
21 #ifdef CONFIG_JWT_SIGN_ECDSA
22 #include <tinycrypt/ctr_prng.h>
23 #include <tinycrypt/sha256.h>
24 #include <tinycrypt/ecc_dsa.h>
25 #include <tinycrypt/constants.h>
26 
27 #include <zephyr/random/random.h>
28 #endif
29 
30 /*
31  * Base-64 encoding is typically done by lookup into a 64-byte static
32  * array.  As an experiment, lets look at both code size and time for
33  * one that does the character encoding computationally.  Like the
34  * array version, this doesn't do bounds checking, and assumes the
35  * passed value has been masked.
36  *
37  * On Cortex-M, this function is 34 bytes of code, which is only a
38  * little more than half of the size of the lookup table.
39  */
40 #if 1
base64_char(int value)41 static int base64_char(int value)
42 {
43 	if (value < 26) {
44 		return value + 'A';
45 	} else if (value < 52) {
46 		return value + 'a' - 26;
47 	} else if (value < 62) {
48 		return value + '0' - 52;
49 	} else if (value == 62) {
50 		return '-';
51 	} else {
52 		return '_';
53 	}
54 }
55 #else
56 static const char b64_table[] =
57 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
base64_char(int value)58 static inline int base64_char(int value)
59 {
60 	return b64_table[value];
61 }
62 #endif
63 
64 /*
65  * Add a single character to the jwt buffer.  Detects overflow, and
66  * always keeps the buffer null terminated.
67  */
base64_outch(struct jwt_builder * st,char ch)68 static void base64_outch(struct jwt_builder *st, char ch)
69 {
70 	if (st->overflowed) {
71 		return;
72 	}
73 
74 	if (st->len < 2) {
75 		st->overflowed = true;
76 		return;
77 	}
78 
79 	*st->buf++ = ch;
80 	st->len--;
81 	*st->buf = 0;
82 }
83 
84 /*
85  * Flush any pending base64 character data out.  If we have all three
86  * bytes are present, this will generate 4 characters, otherwise it
87  * may generate fewer.
88  */
base64_flush(struct jwt_builder * st)89 static void base64_flush(struct jwt_builder *st)
90 {
91 	if (st->pending < 1) {
92 		return;
93 	}
94 
95 	base64_outch(st, base64_char(st->wip[0] >> 2));
96 	base64_outch(st, base64_char(((st->wip[0] & 0x03) << 4) |
97 				(st->wip[1] >> 4)));
98 
99 	if (st->pending >= 2) {
100 		base64_outch(st, base64_char(((st->wip[1] & 0x0f) << 2) |
101 				(st->wip[2] >> 6)));
102 	}
103 	if (st->pending >= 3) {
104 		base64_outch(st, base64_char(st->wip[2] & 0x3f));
105 	}
106 
107 	st->pending = 0;
108 	memset(st->wip, 0, 3);
109 }
110 
base64_addbyte(struct jwt_builder * st,uint8_t byte)111 static void base64_addbyte(struct jwt_builder *st, uint8_t byte)
112 {
113 	st->wip[st->pending++] = byte;
114 	if (st->pending == 3) {
115 		base64_flush(st);
116 	}
117 }
118 
base64_append_bytes(const char * bytes,size_t len,void * data)119 static int base64_append_bytes(const char *bytes, size_t len,
120 			 void *data)
121 {
122 	struct jwt_builder *st = data;
123 
124 	while (len-- > 0) {
125 		base64_addbyte(st, *bytes++);
126 	}
127 
128 	return 0;
129 }
130 
131 struct jwt_payload {
132 	int32_t exp;
133 	int32_t iat;
134 	const char *aud;
135 };
136 
137 static struct json_obj_descr jwt_payload_desc[] = {
138 	JSON_OBJ_DESCR_PRIM(struct jwt_payload, aud, JSON_TOK_STRING),
139 	JSON_OBJ_DESCR_PRIM(struct jwt_payload, exp, JSON_TOK_NUMBER),
140 	JSON_OBJ_DESCR_PRIM(struct jwt_payload, iat, JSON_TOK_NUMBER),
141 };
142 
143 /*
144  * Add the JWT header to the buffer.
145  */
jwt_add_header(struct jwt_builder * builder)146 static int jwt_add_header(struct jwt_builder *builder)
147 {
148 	/*
149 	 * Pre-computed JWT header
150 	 * Use https://www.base64encode.org/ for update
151 	 */
152 	const char jwt_header[] =
153 #ifdef CONFIG_JWT_SIGN_RSA
154 		/* {"alg":"RS256","typ":"JWT"} */
155 		"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";
156 #endif
157 #ifdef CONFIG_JWT_SIGN_ECDSA
158 		/* {"alg":"ES256","typ":"JWT"} */
159 		"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9";
160 #endif
161 	int jwt_header_len = ARRAY_SIZE(jwt_header);
162 
163 	if (jwt_header_len > builder->len) {
164 		builder->overflowed = true;
165 		return -ENOSPC;
166 	}
167 	strcpy(builder->buf, jwt_header);
168 	builder->buf += jwt_header_len - 1;
169 	builder->len -= jwt_header_len - 1;
170 	return 0;
171 }
172 
jwt_add_payload(struct jwt_builder * builder,int32_t exp,int32_t iat,const char * aud)173 int jwt_add_payload(struct jwt_builder *builder,
174 		     int32_t exp,
175 		     int32_t iat,
176 		     const char *aud)
177 {
178 	struct jwt_payload payload = {
179 		.exp = exp,
180 		.iat = iat,
181 		.aud = aud,
182 	};
183 
184 	base64_outch(builder, '.');
185 	int res = json_obj_encode(jwt_payload_desc,
186 				  ARRAY_SIZE(jwt_payload_desc),
187 				  &payload, base64_append_bytes, builder);
188 
189 	base64_flush(builder);
190 	return res;
191 }
192 
193 #ifdef CONFIG_JWT_SIGN_RSA
194 
csprng_wrapper(void * ctx,unsigned char * dest,size_t size)195 static int csprng_wrapper(void *ctx, unsigned char *dest, size_t size)
196 {
197 	ARG_UNUSED(ctx);
198 
199 	return sys_csrand_get((void *)dest, size);
200 }
201 
jwt_sign(struct jwt_builder * builder,const char * der_key,size_t der_key_len)202 int jwt_sign(struct jwt_builder *builder,
203 	     const char *der_key,
204 	     size_t der_key_len)
205 {
206 	int res;
207 	mbedtls_pk_context ctx;
208 
209 	mbedtls_pk_init(&ctx);
210 
211 	res = mbedtls_pk_parse_key(&ctx, der_key, der_key_len,
212 			       NULL, 0, csprng_wrapper, NULL);
213 	if (res != 0) {
214 		return res;
215 	}
216 
217 	uint8_t hash[32], sig[256];
218 	size_t sig_len = sizeof(sig);
219 
220 	/*
221 	 * The '0' indicates to mbedtls to do a SHA256, instead of
222 	 * 224.
223 	 */
224 	mbedtls_sha256(builder->base, builder->buf - builder->base,
225 		       hash, 0);
226 
227 	res = mbedtls_pk_sign(&ctx, MBEDTLS_MD_SHA256,
228 			      hash, sizeof(hash),
229 			      sig, sig_len, &sig_len,
230 			      csprng_wrapper, NULL);
231 	if (res != 0) {
232 		return res;
233 	}
234 
235 	base64_outch(builder, '.');
236 	base64_append_bytes(sig, sig_len, builder);
237 	base64_flush(builder);
238 
239 	return builder->overflowed ? -ENOMEM : 0;
240 }
241 #endif
242 
243 #ifdef CONFIG_JWT_SIGN_ECDSA
244 static TCCtrPrng_t prng_state;
245 static bool prng_init;
246 
247 static const char personalize[] = "zephyr:drivers/jwt/jwt.c";
248 
setup_prng(void)249 static int setup_prng(void)
250 {
251 	if (prng_init) {
252 		return 0;
253 	}
254 	prng_init = true;
255 
256 	uint8_t entropy[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE];
257 
258 	for (int i = 0; i < sizeof(entropy); i += sizeof(uint32_t)) {
259 		uint32_t rv = sys_rand32_get();
260 
261 		memcpy(entropy + i, &rv, sizeof(uint32_t));
262 	}
263 
264 	int res = tc_ctr_prng_init(&prng_state,
265 				   (const uint8_t *) &entropy, sizeof(entropy),
266 				   personalize,
267 				   sizeof(personalize));
268 
269 	return res == TC_CRYPTO_SUCCESS ? 0 : -EINVAL;
270 }
271 
default_CSPRNG(uint8_t * dest,unsigned int size)272 int default_CSPRNG(uint8_t *dest, unsigned int size)
273 {
274 	int res = tc_ctr_prng_generate(&prng_state, NULL, 0, dest, size);
275 	return res;
276 }
277 
jwt_sign(struct jwt_builder * builder,const char * der_key,size_t der_key_len)278 int jwt_sign(struct jwt_builder *builder,
279 	     const char *der_key,
280 	     size_t der_key_len)
281 {
282 	struct tc_sha256_state_struct ctx;
283 	uint8_t hash[32], sig[64];
284 	int res;
285 
286 	tc_sha256_init(&ctx);
287 	tc_sha256_update(&ctx, builder->base, builder->buf - builder->base);
288 	tc_sha256_final(hash, &ctx);
289 
290 	res = setup_prng();
291 
292 	if (res != 0) {
293 		return res;
294 	}
295 	uECC_set_rng(&default_CSPRNG);
296 
297 	/* Note that tinycrypt only supports P-256. */
298 	res = uECC_sign(der_key, hash, sizeof(hash),
299 			sig, &curve_secp256r1);
300 	if (res != TC_CRYPTO_SUCCESS) {
301 		return -EINVAL;
302 	}
303 
304 	base64_outch(builder, '.');
305 	base64_append_bytes(sig, sizeof(sig), builder);
306 	base64_flush(builder);
307 
308 	return 0;
309 }
310 #endif
311 
jwt_init_builder(struct jwt_builder * builder,char * buffer,size_t buffer_size)312 int jwt_init_builder(struct jwt_builder *builder,
313 		     char *buffer,
314 		     size_t buffer_size)
315 {
316 	builder->base = buffer;
317 	builder->buf = buffer;
318 	builder->len = buffer_size;
319 	builder->overflowed = false;
320 	builder->pending = 0;
321 
322 	return jwt_add_header(builder);
323 }
324