1 /*
2    Copyright (c) 2021 Fraunhofer AISEC. See the COPYRIGHT
3    file at the top-level directory of this distribution.
4 
5    Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6    http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7    <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8    option. This file may not be copied, modified, or distributed
9    except according to those terms.
10 */
11 
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <string.h>
16 
17 #include "oscore.h"
18 
19 #include "oscore/aad.h"
20 #include "oscore/oscore_coap.h"
21 #include "oscore/nonce.h"
22 #include "oscore/option.h"
23 #include "oscore/oscore_cose.h"
24 #include "oscore/security_context.h"
25 #include "oscore/replay_protection.h"
26 
27 #include "common/byte_array.h"
28 #include "common/oscore_edhoc_error.h"
29 #include "common/memcpy_s.h"
30 #include "common/print_util.h"
31 #include "common/unit_test.h"
32 
33 /**
34  * @brief 	Parse all received options to find the OSCORE option. If it doesn't
35  * 		 	have OSCORE option, then this packet is a normal CoAP. If it does
36  * 			have, it's an OSCORE packet, and then parse the compressed OSCORE
37  * 			option value to get value of PIV, KID and KID context of the client.
38  * @param opt: input array of options
39  * @param opt_cnt: number of elements in the array
40  * @param out: pointer output compressed OSCORE_option
41  * @return error code
42  */
oscore_option_parser(const struct o_coap_option * opt,uint8_t opt_cnt,struct compressed_oscore_option * out)43 STATIC enum err oscore_option_parser(const struct o_coap_option *opt,
44 				     uint8_t opt_cnt,
45 				     struct compressed_oscore_option *out)
46 {
47 	uint8_t *val_ptr;
48 	uint16_t temp_kid_len = 0;
49 
50 	enum err r = not_oscore_pkt;
51 
52 	for (uint8_t i = 0; i < opt_cnt; i++) {
53 		temp_kid_len = opt[i].len;
54 
55 		/* Check current option is OSCORE_option or not */
56 		if (opt[i].option_number == OSCORE) {
57 			if (opt[i].len == 0) {
58 				/* No OSCORE option value*/
59 				out->h = 0;
60 				out->k = 0;
61 				out->n = 0;
62 				out->piv.ptr = NULL;
63 				out->piv.len = 0;
64 				out->kid.ptr = NULL;
65 				out->kid.len = 0;
66 				out->kid_context.ptr = NULL;
67 				out->kid_context.len = 0;
68 			} else {
69 				/* Get address of current option value*/
70 				val_ptr = opt[i].value;
71 				/* Parse first byte of OSCORE value*/
72 				out->h = ((*val_ptr) &
73 					  COMP_OSCORE_OPT_KIDC_H_MASK) >>
74 					 COMP_OSCORE_OPT_KIDC_H_OFFSET;
75 				out->k = ((*val_ptr) &
76 					  COMP_OSCORE_OPT_KID_K_MASK) >>
77 					 COMP_OSCORE_OPT_KID_K_OFFSET;
78 				out->n = ((*val_ptr) &
79 					  COMP_OSCORE_OPT_PIV_N_MASK) >>
80 					 COMP_OSCORE_OPT_PIV_N_OFFSET;
81 				val_ptr++;
82 				temp_kid_len--;
83 
84 				/* Get PIV */
85 				switch (out->n) {
86 				case 0:
87 					/* NO PIV in COSE object*/
88 					out->piv.ptr = NULL;
89 					out->piv.len = 0;
90 					break;
91 				case 6:
92 				case 7:
93 					/* ERROR: Byte length of PIV not right, max. 5 bytes */
94 					return oscore_inpkt_invalid_piv;
95 					break;
96 				default:
97 					out->piv.ptr = val_ptr;
98 					out->piv.len = out->n;
99 					val_ptr += out->n;
100 					temp_kid_len = (uint8_t)(temp_kid_len -
101 								 out->n);
102 					break;
103 				}
104 
105 				/* Get KID context */
106 				if (out->h == 0) {
107 					out->kid_context.len = 0;
108 					out->kid_context.ptr = NULL;
109 				} else {
110 					out->kid_context.len = *val_ptr;
111 					out->kid_context.ptr = ++val_ptr;
112 					val_ptr += out->kid_context.len;
113 					temp_kid_len = (uint8_t)(
114 						temp_kid_len -
115 						(out->kid_context.len + 1));
116 				}
117 
118 				/* Get KID */
119 				if (out->k == 0) {
120 					out->kid.len = 0;
121 					out->kid.ptr = NULL;
122 				} else {
123 					out->kid.len = temp_kid_len;
124 					out->kid.ptr = val_ptr;
125 				}
126 			}
127 
128 			r = ok;
129 		}
130 	}
131 
132 	return r;
133 }
134 
135 /**
136  * @brief Reorder E-options and other U-options, and update their delta, and combine them all to normal CoAP packet
137  * @param oscore_pkt: input OSCORE, which contains U-options
138  * @param E_options: input pointer to E-options array
139  * @param E_options_cnt: count number of input E-options
140  * @param out: output pointer to CoAP packet, which will have all reordered options
141  * @return ok or error code
142  */
143 
144 STATIC enum err
options_reorder(struct o_coap_option * U_options,uint8_t U_options_cnt,struct o_coap_option * E_options,uint8_t E_options_cnt,struct o_coap_option * out_options,uint8_t * out_options_cnt)145 options_reorder(struct o_coap_option *U_options, uint8_t U_options_cnt,
146 		struct o_coap_option *E_options, uint8_t E_options_cnt,
147 		struct o_coap_option *out_options, uint8_t *out_options_cnt)
148 {
149 	/*the maximum amount of options for the CoAP packet
150 	is the amount of all options -1 (for the OSCORE option)*/
151 	uint8_t max_coap_opt_cnt = (uint8_t)(U_options_cnt + E_options_cnt - 1);
152 
153 	TRY(check_buffer_size(MAX_OPTION_COUNT, max_coap_opt_cnt));
154 	*out_options_cnt = 0;
155 	memset(out_options, 0, sizeof(struct o_coap_option) * max_coap_opt_cnt);
156 
157 	/*Get the all outer options. Discard OSCORE and outer OBSERVE as specified in 8.2 and 8.4 */
158 	for (uint8_t i = 0; i < U_options_cnt; i++) {
159 		if ((U_options[i].option_number != OSCORE) &&
160 		    (U_options[i].option_number != OBSERVE)) {
161 			out_options[*out_options_cnt] = U_options[i];
162 			*out_options_cnt += 1;
163 		}
164 	}
165 
166 	/*Get the inner options.*/
167 	for (uint8_t i = 0; i < E_options_cnt; i++) {
168 		out_options[*out_options_cnt] = E_options[i];
169 		*out_options_cnt += 1;
170 	}
171 
172 	uint16_t delta = 0;
173 	/* Order the options starting with minimum option number to maximum */
174 	for (uint8_t i = 0; i < *out_options_cnt; i++) {
175 		uint8_t ipp = (uint8_t)(i + 1);
176 		for (uint8_t k = ipp; k < *out_options_cnt; k++) {
177 			if (out_options[i].option_number >
178 			    out_options[k].option_number) {
179 				struct o_coap_option tmp;
180 				tmp = out_options[i];
181 				out_options[i] = out_options[k];
182 				out_options[k] = tmp;
183 			}
184 		}
185 		/*update the delta*/
186 		out_options[i].delta = out_options[i].option_number - delta;
187 		delta = out_options[i].option_number;
188 	}
189 
190 	return ok;
191 }
192 
193 /**
194  * @brief Generate CoAP packet from OSCORE packet
195  * @param decrypted_payload: decrypted OSCORE payload, which contains code, E-options and original unprotected CoAP payload
196  * @param oscore_pkt:  input OSCORE packet
197  * @param out: pointer to output CoAP packet
198  * @return
199  */
o_coap_pkg_generate(struct byte_array * decrypted_payload,struct o_coap_packet * oscore_pkt,struct o_coap_packet * out)200 static inline enum err o_coap_pkg_generate(struct byte_array *decrypted_payload,
201 					   struct o_coap_packet *oscore_pkt,
202 					   struct o_coap_packet *out)
203 {
204 	uint8_t code = 0;
205 	struct byte_array unprotected_o_coap_payload = BYTE_ARRAY_INIT(NULL, 0);
206 	struct o_coap_option E_options[MAX_E_OPTION_COUNT];
207 	uint8_t E_options_cnt = 0;
208 
209 	/* Parse decrypted payload: code + options + unprotected CoAP payload*/
210 	TRY(oscore_decrypted_payload_parser(decrypted_payload, &code, E_options,
211 					    &E_options_cnt,
212 					    &unprotected_o_coap_payload));
213 
214 	/* Copy each items from OSCORE packet to CoAP packet */
215 	/* Header */
216 	out->header.ver = oscore_pkt->header.ver;
217 	out->header.type = oscore_pkt->header.type;
218 	out->header.TKL = oscore_pkt->header.TKL;
219 	out->token = oscore_pkt->token;
220 	out->header.code = code; //decrypted code must be used, see 8.2 p.7
221 	out->header.MID = oscore_pkt->header.MID;
222 
223 	/* Payload */
224 	out->payload.len = unprotected_o_coap_payload.len;
225 	if (unprotected_o_coap_payload.len == 0) {
226 		out->payload.ptr = NULL;
227 	} else {
228 		out->payload.ptr = unprotected_o_coap_payload.ptr;
229 	}
230 
231 	/* reorder all options, and copy it to output coap packet */
232 	TRY(options_reorder(oscore_pkt->options, oscore_pkt->options_cnt,
233 			    E_options, E_options_cnt, out->options,
234 			    &out->options_cnt));
235 	return ok;
236 }
237 
238 /**
239  * @brief Wrapper function with common operations for decrypting the payload.
240  *        These operations are shared in all possible scenarios.
241  *        For more info, see RFC8616 8.2 and 8.4.
242  *
243  * @param ciphertext Input encrypted payload.
244  * @param plaintext Output decrypted payload.
245  * @param c Security context.
246  * @param new_nonce_oscore_option Input OSCORE option from the packet.
247  *        Use proper pointer for cases when new nonce are generated, or
248  *        NULL if data from corresponding request should be used.
249  * @param input_oscore Input OSCORE packet.
250  * @param output_coap Output decrypted coap packet.
251  * @return enum err
252  */
253 static enum err
decrypt_wrapper(struct byte_array * ciphertext,struct byte_array * plaintext,struct context * c,struct compressed_oscore_option * new_nonce_oscore_option,struct o_coap_packet * input_oscore,struct o_coap_packet * output_coap)254 decrypt_wrapper(struct byte_array *ciphertext, struct byte_array *plaintext,
255 		struct context *c,
256 		struct compressed_oscore_option *new_nonce_oscore_option,
257 		struct o_coap_packet *input_oscore,
258 		struct o_coap_packet *output_coap)
259 {
260 	BYTE_ARRAY_NEW(new_nonce, NONCE_LEN, NONCE_LEN);
261 	struct byte_array nonce;
262 
263 	/* Read necessary fields from the input packet. */
264 	enum o_coap_msg msg_type_oscore;
265 	TRY(coap_get_message_type(input_oscore, &msg_type_oscore));
266 	struct byte_array token =
267 		BYTE_ARRAY_INIT(input_oscore->token, input_oscore->header.TKL);
268 
269 	/* Read Request PIV and KID fields from OSCORE option, if available. Update using interactions wrapper. */
270 	struct byte_array request_piv;
271 	struct byte_array request_kid;
272 	if (NULL != new_nonce_oscore_option) {
273 		request_piv = new_nonce_oscore_option->piv;
274 		request_kid = new_nonce_oscore_option->kid;
275 	}
276 	TRY(oscore_interactions_read_wrapper(msg_type_oscore, &token,
277 					     c->rrc.interactions, &request_piv,
278 					     &request_kid));
279 	/* Message type read from encrypted packet can be invalid due to external OBSERVE option change,
280 	   but it is sufficient enough for the interactions read wrapper to work properly,
281 	   as it only need to know whether the packet is any kind of response. */
282 
283 	/* Calculate new nonce from oscore option - only if required by the usecase.
284 	   If not, nonce from the corresponding request (rcc.nonce) is used. */
285 	if (NULL != new_nonce_oscore_option) {
286 		TRY(create_nonce(&new_nonce_oscore_option->kid,
287 				 &new_nonce_oscore_option->piv,
288 				 &c->cc.common_iv, &new_nonce));
289 		nonce = new_nonce;
290 	} else {
291 		nonce = c->rrc.nonce;
292 	}
293 
294 	/* compute AAD */
295 	uint8_t aad_buf[MAX_AAD_LEN];
296 	struct byte_array aad = BYTE_ARRAY_INIT(aad_buf, sizeof(aad_buf));
297 	TRY(create_aad(NULL, 0, c->cc.aead_alg, &request_kid, &request_piv,
298 		       &aad));
299 
300 	/* Decrypt the ciphertext */
301 	TRY(oscore_cose_decrypt(ciphertext, plaintext, &nonce, &aad,
302 				&c->rc.recipient_key));
303 
304 	/* Update nonce only after successful decryption (for handling future responses) */
305 	if (NULL != new_nonce_oscore_option) {
306 		TRY(byte_array_cpy(&c->rrc.nonce, &nonce, NONCE_LEN));
307 	}
308 
309 	/* Generate corresponding CoAP packet */
310 	TRY(o_coap_pkg_generate(plaintext, input_oscore, output_coap));
311 
312 	/* Handle OSCORE interactions after successful decryption.
313 	   Decrypted packet is used for URI Paths and message type, as original values are modified while encrypting. */
314 	enum o_coap_msg msg_type;
315 	TRY(coap_get_message_type(output_coap, &msg_type));
316 	BYTE_ARRAY_NEW(uri_paths, OSCORE_MAX_URI_PATH_LEN,
317 		       OSCORE_MAX_URI_PATH_LEN);
318 	TRY(uri_path_create(output_coap->options, output_coap->options_cnt,
319 			    uri_paths.ptr, &(uri_paths.len)));
320 	TRY(oscore_interactions_update_wrapper(msg_type, &token, &uri_paths,
321 					       c->rrc.interactions,
322 					       &request_piv, &request_kid));
323 
324 	return ok;
325 }
326 
oscore2coap(uint8_t * buf_in,uint32_t buf_in_len,uint8_t * buf_out,uint32_t * buf_out_len,struct context * c)327 enum err oscore2coap(uint8_t *buf_in, uint32_t buf_in_len, uint8_t *buf_out,
328 		     uint32_t *buf_out_len, struct context *c)
329 {
330 	struct o_coap_packet oscore_packet;
331 	struct compressed_oscore_option oscore_option;
332 	struct byte_array buf;
333 
334 	PRINT_MSG("\n\n\noscore2coap***************************************\n");
335 	PRINT_ARRAY("Input OSCORE packet", buf_in, buf_in_len);
336 
337 	buf.ptr = buf_in;
338 	buf.len = buf_in_len;
339 
340 	/* Make sure that given context is fresh enough to process the message. */
341 	TRY(check_context_freshness(c));
342 
343 	/*Parse the incoming message (buf_in) into a CoAP struct*/
344 	memset(&oscore_packet, 0, sizeof(oscore_packet));
345 	TRY(coap_deserialize(&buf, &oscore_packet));
346 
347 	/* Check if the packet is OSCORE packet and if so parse the OSCORE option */
348 	TRY(oscore_option_parser(oscore_packet.options,
349 				 oscore_packet.options_cnt, &oscore_option));
350 
351 	/* Encrypted packet payload */
352 	struct byte_array *ciphertext = &oscore_packet.payload;
353 
354 	/* Setup buffer for the plaintext. The plaintext is shorter than the
355 	ciphertext because of the authentication tag*/
356 	uint32_t plaintext_bytes_len = ciphertext->len - AUTH_TAG_LEN;
357 	BYTE_ARRAY_NEW(plaintext, MAX_PLAINTEXT_LEN, plaintext_bytes_len);
358 	/* TODO plaintext can be moved inside decrypt_wrapper to simplify the code.
359 	   To do so, refactor of echo_val_is_fresh is needed, to operate on o_coap_packet. */
360 
361 	/* Helper structure for decrypted coap packet */
362 	struct o_coap_packet output_coap;
363 
364 	/*In requests the OSCORE packet contains at least a KID = sender ID
365         and eventually sender sequence number*/
366 	if (is_request(&oscore_packet)) {
367 		/*Check that the recipient context c->rc has a  Recipient ID that
368 			 matches the received with the oscore option KID (Sender ID).
369 			 If this is not true return an error which indicates the caller
370 			 application to tray another context. This is useful when the caller
371 			 app doesn't know in advance to which context an incoming packet
372              belongs.*/
373 		if (!array_equals(&c->rc.recipient_id, &oscore_option.kid)) {
374 			return oscore_kid_recipient_id_mismatch;
375 		}
376 
377 		/* Check if the packet is replayed - in case of normal operation (replay window already synchronized).
378 		   It must be performed before decrypting the packet (see RFC 8613 p. 7.4). */
379 		if (ECHO_SYNCHRONIZED == c->rrc.echo_state_machine) {
380 			uint64_t ssn;
381 			piv2ssn(&oscore_option.piv, &ssn);
382 			if (!server_is_sequence_number_valid(
383 				    ssn, &c->rc.replay_window)) {
384 				PRINT_MSG("Replayed message detected!\n");
385 				return oscore_replay_window_protection_error;
386 			}
387 		}
388 
389 		/* Decrypt packet using new nonce based on the packet */
390 		TRY(decrypt_wrapper(ciphertext, &plaintext, c, &oscore_option,
391 				    &oscore_packet, &output_coap));
392 
393 		if (ECHO_REBOOT == c->rrc.echo_state_machine) {
394 			/* Abort the execution if this is the the first request after reboot.
395 			   Let the application layer know that it should prepare a special response with ECHO option
396 			   and prepare for verifying ECHO of the next request. */
397 			PRINT_MSG("Abort -- first request after reboot!\n");
398 			c->rrc.echo_state_machine = ECHO_VERIFY;
399 			return first_request_after_reboot;
400 		} else if (ECHO_VERIFY == c->rrc.echo_state_machine) {
401 			/* Next request should already have proper ECHO option for proving freshness.
402 			   If so, perform replay window reinitialization and start normal operation.
403 			   If not, repeat the whole process until normal operation can be started. */
404 			if (ok == echo_val_is_fresh(&c->rrc.echo_opt_val,
405 						    &plaintext)) {
406 				uint64_t ssn;
407 				piv2ssn(&oscore_option.piv, &ssn);
408 				TRY(server_replay_window_reinit(
409 					ssn, &c->rc.replay_window));
410 				c->rrc.echo_state_machine = ECHO_SYNCHRONIZED;
411 			} else {
412 				PRINT_MSG(
413 					"Abort -- ECHO validation failed! Repeating the challenge.\n");
414 				return echo_validation_failed;
415 			}
416 		} else {
417 			/* Normal operation - update replay window. */
418 			TRY_EXPECT(c->rrc.echo_state_machine,
419 				   ECHO_SYNCHRONIZED);
420 			server_replay_window_update(*oscore_option.piv.ptr,
421 						    &c->rc.replay_window);
422 		}
423 	} else {
424 		/* received any kind of response */
425 		if (is_observe(oscore_packet.options,
426 			       oscore_packet.options_cnt)) {
427 			if (oscore_option.piv.len != 0) {
428 				/*Notification with PIV received*/
429 				PRINT_MSG(
430 					"Observe notification with PIV received\n");
431 
432 				TRY(replay_protection_check_notification(
433 					c->rc.notification_num,
434 					c->rc.notification_num_initialized,
435 					&oscore_option.piv));
436 
437 				/* Decrypt packet using new nonce based on the packet */
438 				TRY(decrypt_wrapper(ciphertext, &plaintext, c,
439 						    &oscore_option,
440 						    &oscore_packet,
441 						    &output_coap));
442 
443 				/*update replay protection value in context*/
444 				TRY(notification_number_update(
445 					&c->rc.notification_num,
446 					&c->rc.notification_num_initialized,
447 					&oscore_option.piv));
448 			} else {
449 				/*Notification without PIV received -- Currently not supported*/
450 				return not_supported_feature; //LCOV_EXCL_LINE
451 			}
452 		} else {
453 			/*regular response received*/
454 			if (oscore_option.piv.len != 0) {
455 				/*response with PIV*/
456 				TRY(decrypt_wrapper(ciphertext, &plaintext, c,
457 						    &oscore_option,
458 						    &oscore_packet,
459 						    &output_coap));
460 			} else {
461 				/*response without PIV*/
462 				TRY(decrypt_wrapper(ciphertext, &plaintext, c,
463 						    NULL, &oscore_packet,
464 						    &output_coap));
465 			}
466 		}
467 	}
468 
469 	/*Convert to byte string*/
470 	return coap_serialize(&output_coap, buf_out, buf_out_len);
471 }
472