2 * Routines for Object Security for Constrained RESTful Environments dissection
3 * Copyright 2017, Malisa Vucinic <malishav@gmail.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include <epan/packet.h> /* Should be first Wireshark include (other than config.h) */
19 #include <epan/proto_data.h>
20 #include <epan/expert.h> /* Include only as needed */
22 #include <epan/strutil.h>
23 #include <epan/prefs.h> /* Include only as needed */
24 #include <epan/to_str.h>
26 #include <wsutil/wsgcrypt.h>
27 #include "packet-ieee802154.h" /* We use CCM implementation available as part of 802.15.4 dissector */
28 #include "packet-coap.h" /* packet-coap.h includes packet-oscore.h */
31 static unsigned oscore_alg_get_key_len(cose_aead_alg_t
);
32 static unsigned oscore_alg_get_iv_len(cose_aead_alg_t
);
33 static unsigned oscore_alg_get_tag_len(cose_aead_alg_t
);
34 static bool oscore_context_derive_params(oscore_context_t
*);
36 /* CBOR encoder prototypes */
37 static uint8_t cborencoder_put_text(uint8_t *buffer
, const char *text
, uint8_t text_len
);
38 static uint8_t cborencoder_put_null(uint8_t *buffer
);
39 static uint8_t cborencoder_put_unsigned(uint8_t *buffer
, uint8_t value
);
40 static uint8_t cborencoder_put_bytes(uint8_t *buffer
, const uint8_t *bytes
, uint8_t bytes_len
);
41 static uint8_t cborencoder_put_array(uint8_t *buffer
, uint8_t elements
);
43 /* (Required to prevent [-Wmissing-prototypes] warnings */
44 void proto_reg_handoff_oscore(void);
45 void proto_register_oscore(void);
47 /* Initialize the protocol and registered fields */
48 static int proto_oscore
;
49 static int proto_coap
;
51 static int hf_oscore_tag
;
53 static COAP_COMMON_LIST_T(dissect_oscore_hf
);
55 static expert_field ei_oscore_partial_iv_not_found
;
56 static expert_field ei_oscore_context_not_set
;
57 static expert_field ei_oscore_message_too_small
;
58 static expert_field ei_oscore_truncated
;
59 static expert_field ei_oscore_tag_check_failed
;
60 static expert_field ei_oscore_decrypt_error
;
61 static expert_field ei_oscore_cbc_mac_failed
;
62 static expert_field ei_oscore_piv_len_invalid
;
63 static expert_field ei_oscore_info_fetch_failed
;
65 /* Initialize the subtree pointers */
66 static int ett_oscore
;
69 static uat_t
*oscore_context_uat
;
70 static oscore_context_t
*oscore_contexts
;
71 static unsigned num_oscore_contexts
;
73 /* Enumeration for COSE algorithms used by OSCORE */
74 static const value_string oscore_context_alg_vals
[] = {
75 { COSE_AES_CCM_16_64_128
, "AES-CCM-16-64-128 (CCM*)"},
79 /* Field callbacks. */
80 UAT_CSTRING_CB_DEF(oscore_context_uat
, master_secret_prefs
, oscore_context_t
)
81 UAT_CSTRING_CB_DEF(oscore_context_uat
, master_salt_prefs
, oscore_context_t
)
82 UAT_CSTRING_CB_DEF(oscore_context_uat
, id_context_prefs
, oscore_context_t
)
83 UAT_CSTRING_CB_DEF(oscore_context_uat
, sender_id_prefs
, oscore_context_t
)
84 UAT_CSTRING_CB_DEF(oscore_context_uat
, recipient_id_prefs
, oscore_context_t
)
85 UAT_VS_DEF(oscore_context_uat
, algorithm
, oscore_context_t
, cose_aead_alg_t
, COSE_AES_CCM_16_64_128
, "AES-CCM-16-64-128 (CCM*)")
87 #define OSCORE_MIN_LENGTH 9 /* 1 byte for code plus 8 bytes for shortest authentication tag */
88 #define OSCORE_VERSION 1 /* rfc8613 */
89 #define TAG_MAX_LEN 16
90 #define AES_128_BLOCK_LEN 16
91 #define NONCE_MAX_LEN 13 /* longest nonce in RFC8152 is 13 bytes */
93 #define OSCORE_PIV_MAX_LEN 5 /* upper bound specified in the draft */
94 #define OSCORE_KID_MAX_LEN_CCM_STAR 7 /* upper bound on KID for AES-CCM-16-64-128 (CCM*) */
95 #define OSCORE_KID_MAX_LEN OSCORE_KID_MAX_LEN_CCM_STAR /* upper bound on KID coming from the default algorithm implemented */
96 #define OSCORE_KID_CONTEXT_MAX_LEN 64
98 /* Helper macros to correctly size the statically allocated buffers and verify if an overflow occurred */
100 #define OSCORE_INFO_MAX_LEN (1 + /* max return of cborencoder_put_array() */ \
101 2 + OSCORE_KID_MAX_LEN + /* max 2 to encode length, KID following */ \
102 2 + OSCORE_KID_CONTEXT_MAX_LEN + /* length + KID CONTEXT */ \
103 2 + /* max return of cborencoder_put_unsigned() */ \
104 2 + 3 + /* max 2 to encode length, "Key" following */ \
105 2 /* max return of cborencoder_put_unsigned() */ )
107 #define OSCORE_EXTERNAL_AAD_MAX_LEN (1 + /* max return of cborencoder_put_array() */ \
108 2 + /* max return of cborencoder_put_unsigned() */ \
109 1 + /* max return of cborencoder_put_array() */ \
110 2 + /* max return of cborencoder_put_unsigned() */ \
111 2 + OSCORE_KID_MAX_LEN + /* max 2 to encode length, KID following */ \
112 2 + OSCORE_PIV_MAX_LEN + /* max 2 to encode length, PIV following */ \
113 1 + 0 /* 1 to encode length, 0 bytes following */ )
115 #define OSCORE_AAD_MAX_LEN (1 + /* max return of cborencoder_put_array() */ \
116 2 + 8 +/* max 2 to encode length, "Encrypt0" following */ \
117 1 + 0 + /* 1 to encode length, 0 bytes following */ \
118 2 + OSCORE_EXTERNAL_AAD_MAX_LEN /* max 2 to encode length, external_aad following */ )
120 static void oscore_context_free_byte_arrays(oscore_context_t
*rec
) {
122 if (rec
->master_secret
) {
123 g_byte_array_free(rec
->master_secret
, true);
126 if (rec
->master_salt
) {
127 g_byte_array_free(rec
->master_salt
, true);
130 if (rec
->id_context
) {
131 g_byte_array_free(rec
->id_context
, true);
134 if (rec
->sender_id
) {
135 g_byte_array_free(rec
->sender_id
, true);
138 if (rec
->recipient_id
) {
139 g_byte_array_free(rec
->recipient_id
, true);
142 if (rec
->request_decryption_key
) {
143 g_byte_array_free(rec
->request_decryption_key
, true);
146 if (rec
->response_decryption_key
) {
147 g_byte_array_free(rec
->response_decryption_key
, true);
150 if (rec
->common_iv
) {
151 g_byte_array_free(rec
->common_iv
, true);
155 static void oscore_context_post_update_cb(void) {
160 for (i
= 0; i
< num_oscore_contexts
; i
++) {
162 /* Make sure to free the memory if it was allocated previously. */
163 oscore_context_free_byte_arrays(&oscore_contexts
[i
]);
165 oscore_contexts
[i
].master_secret
= g_byte_array_new();
166 oscore_contexts
[i
].master_salt
= g_byte_array_new();
167 oscore_contexts
[i
].id_context
= g_byte_array_new();
168 oscore_contexts
[i
].sender_id
= g_byte_array_new();
169 oscore_contexts
[i
].recipient_id
= g_byte_array_new();
171 /* Convert strings to byte arrays */
172 hex_str_to_bytes(oscore_contexts
[i
].sender_id_prefs
, oscore_contexts
[i
].sender_id
, false);
173 hex_str_to_bytes(oscore_contexts
[i
].recipient_id_prefs
, oscore_contexts
[i
].recipient_id
, false);
174 hex_str_to_bytes(oscore_contexts
[i
].id_context_prefs
, oscore_contexts
[i
].id_context
, false);
175 hex_str_to_bytes(oscore_contexts
[i
].master_secret_prefs
, oscore_contexts
[i
].master_secret
, false);
176 hex_str_to_bytes(oscore_contexts
[i
].master_salt_prefs
, oscore_contexts
[i
].master_salt
, false);
178 /* Algorithm-dependent key and IV length */
179 key_len
= oscore_alg_get_key_len(oscore_contexts
[i
].algorithm
);
180 iv_len
= oscore_alg_get_iv_len(oscore_contexts
[i
].algorithm
);
182 /* Allocate memory for derived parameters */
183 oscore_contexts
[i
].request_decryption_key
= g_byte_array_sized_new(key_len
);
184 oscore_contexts
[i
].response_decryption_key
= g_byte_array_sized_new(key_len
);
185 oscore_contexts
[i
].common_iv
= g_byte_array_sized_new(iv_len
);
187 oscore_context_derive_params(&oscore_contexts
[i
]);
191 /* Check user input, do not allocate any memory */
192 static bool oscore_context_update_cb(void *r
, char **err
) {
193 oscore_context_t
*rec
= (oscore_context_t
*) r
;
194 GByteArray
*bytes
; /* temp array to verify each parameter */
196 bytes
= g_byte_array_new();
198 if (hex_str_to_bytes(rec
->sender_id_prefs
, bytes
, false) == false) {
199 *err
= g_strdup("Sender ID is invalid.");
200 g_byte_array_free(bytes
, true);
204 if (bytes
->len
> OSCORE_KID_MAX_LEN
) {
205 *err
= ws_strdup_printf("Should be %u bytes or less.", OSCORE_KID_MAX_LEN
);
206 g_byte_array_free(bytes
, true);
210 if (hex_str_to_bytes(rec
->recipient_id_prefs
, bytes
, false) == false) {
211 *err
= g_strdup("Recipient ID is invalid.");
212 g_byte_array_free(bytes
, true);
216 if (bytes
->len
> OSCORE_KID_MAX_LEN
) {
217 *err
= ws_strdup_printf("Should be %u bytes or less.", OSCORE_KID_MAX_LEN
);
218 g_byte_array_free(bytes
, true);
222 if (hex_str_to_bytes(rec
->id_context_prefs
, bytes
, false) == false) {
223 *err
= g_strdup("ID Context is invalid.");
224 g_byte_array_free(bytes
, true);
228 if (bytes
->len
> OSCORE_KID_CONTEXT_MAX_LEN
) {
229 *err
= ws_strdup_printf("Should be %u bytes or less.", OSCORE_KID_CONTEXT_MAX_LEN
);
230 g_byte_array_free(bytes
, true);
234 if (hex_str_to_bytes(rec
->master_secret_prefs
, bytes
, false) == false) {
235 *err
= g_strdup("Master Secret is invalid.");
236 g_byte_array_free(bytes
, true);
240 /* No max length check on Master Secret. We use GByteArray to allocate memory
241 * and pass it to the context derivation routine */
242 if (bytes
->len
== 0) {
243 *err
= g_strdup("Master Secret is mandatory.");
244 g_byte_array_free(bytes
, true);
248 if (hex_str_to_bytes(rec
->master_salt_prefs
, bytes
, false) == false) {
249 *err
= g_strdup("Master Salt is invalid.");
250 g_byte_array_free(bytes
, true);
254 /* No (max) length check on optional Master Salt. We use GByteArray to allocate memory
255 * and pass it to the context derivation routine */
257 g_byte_array_free(bytes
, true);
261 static void* oscore_context_copy_cb(void *n
, const void *o
, size_t siz _U_
) {
262 oscore_context_t
*new_record
= (oscore_context_t
*) n
;
263 const oscore_context_t
*old_record
= (const oscore_context_t
*) o
;
265 /* Pre-Shared Parameters */
266 new_record
->master_secret_prefs
= g_strdup(old_record
->master_secret_prefs
);
267 new_record
->master_salt_prefs
= g_strdup(old_record
->master_salt_prefs
);
268 new_record
->id_context_prefs
= g_strdup(old_record
->id_context_prefs
);
269 new_record
->sender_id_prefs
= g_strdup(old_record
->sender_id_prefs
);
270 new_record
->recipient_id_prefs
= g_strdup(old_record
->recipient_id_prefs
);
271 new_record
->algorithm
= old_record
->algorithm
;
273 /* Initialize all to NULL, overwrite as needed */
274 new_record
->master_secret
= NULL
;
275 new_record
->master_salt
= NULL
;
276 new_record
->id_context
= NULL
;
277 new_record
->sender_id
= NULL
;
278 new_record
->recipient_id
= NULL
;
279 new_record
->request_decryption_key
= NULL
;
280 new_record
->response_decryption_key
= NULL
;
281 new_record
->common_iv
= NULL
;
283 /* We rely on oscore_context_post_update_cb() to convert strings to GByteArrays and derive params */
288 static void oscore_context_free_cb(void *r
) {
289 oscore_context_t
*rec
= (oscore_context_t
*) r
;
291 /* User-configured strings */
292 g_free(rec
->master_secret_prefs
);
293 g_free(rec
->master_salt_prefs
);
294 g_free(rec
->id_context_prefs
);
295 g_free(rec
->sender_id_prefs
);
296 g_free(rec
->recipient_id_prefs
);
298 /* Allocated byte arrays */
299 oscore_context_free_byte_arrays(rec
);
302 /* GByteArrays within the oscore_context_t object should be initialized before calling this function */
303 static bool oscore_context_derive_params(oscore_context_t
*context
) {
304 const char *iv_label
= "IV";
305 const char *key_label
= "Key";
306 uint8_t prk
[32]; /* Pseudo-random key from HKDF-Extract step. 32 for SHA256. */
309 uint8_t info_buf
[OSCORE_INFO_MAX_LEN
];
313 key_len
= oscore_alg_get_key_len(context
->algorithm
);
314 iv_len
= oscore_alg_get_iv_len(context
->algorithm
);
316 info
= g_byte_array_new();
318 /* Common HKDF-Extract step on master salt */
319 hkdf_extract(GCRY_MD_SHA256
, context
->master_salt
->data
, context
->master_salt
->len
, context
->master_secret
->data
, context
->master_secret
->len
, prk
);
321 /* Request Decryption Key */
323 info_len
+= cborencoder_put_array(&info_buf
[info_len
], 5);
324 info_len
+= cborencoder_put_bytes(&info_buf
[info_len
], context
->sender_id
->data
, context
->sender_id
->len
);
325 if (context
->id_context
->len
) {
326 info_len
+= cborencoder_put_bytes(&info_buf
[info_len
], context
->id_context
->data
, context
->id_context
->len
);
328 info_len
+= cborencoder_put_null(&info_buf
[info_len
]);
330 info_len
+= cborencoder_put_unsigned(&info_buf
[info_len
], context
->algorithm
);
331 info_len
+= cborencoder_put_text(&info_buf
[info_len
], key_label
, 3);
332 info_len
+= cborencoder_put_unsigned(&info_buf
[info_len
], key_len
);
333 /* sender_id->len comes from user input, it is validated by the UAT callback and the max length is accounted for
334 * in OSCORE_INFO_MAX_LEN */
335 DISSECTOR_ASSERT(info_len
< OSCORE_INFO_MAX_LEN
);
336 g_byte_array_append(info
, info_buf
, info_len
);
337 g_byte_array_set_size(context
->request_decryption_key
, key_len
);
338 hkdf_expand(GCRY_MD_SHA256
, prk
, sizeof(prk
), info
->data
, info
->len
, context
->request_decryption_key
->data
, key_len
); /* 32 for SHA256 */
342 /* Response Decryption Key */
344 g_byte_array_set_size(info
, 0);
345 info_len
+= cborencoder_put_array(&info_buf
[info_len
], 5);
346 info_len
+= cborencoder_put_bytes(&info_buf
[info_len
], context
->recipient_id
->data
, context
->recipient_id
->len
);
347 if (context
->id_context
->len
) {
348 info_len
+= cborencoder_put_bytes(&info_buf
[info_len
], context
->id_context
->data
, context
->id_context
->len
);
350 info_len
+= cborencoder_put_null(&info_buf
[info_len
]);
352 info_len
+= cborencoder_put_unsigned(&info_buf
[info_len
], context
->algorithm
);
353 info_len
+= cborencoder_put_text(&info_buf
[info_len
], key_label
, 3);
354 info_len
+= cborencoder_put_unsigned(&info_buf
[info_len
], key_len
);
355 /* recipient_id->len comes from user input, it is validated by the UAT callback and the max length is accounted for
356 * in OSCORE_INFO_MAX_LEN */
357 DISSECTOR_ASSERT(info_len
< OSCORE_INFO_MAX_LEN
);
358 g_byte_array_append(info
, info_buf
, info_len
);
359 g_byte_array_set_size(context
->response_decryption_key
, key_len
);
360 hkdf_expand(GCRY_MD_SHA256
, prk
, sizeof(prk
), info
->data
, info
->len
, context
->response_decryption_key
->data
, key_len
); /* 32 for SHA256 */
364 g_byte_array_set_size(info
, 0);
365 info_len
+= cborencoder_put_array(&info_buf
[info_len
], 5);
366 info_len
+= cborencoder_put_bytes(&info_buf
[info_len
], NULL
, 0);
367 if (context
->id_context
->len
) {
368 info_len
+= cborencoder_put_bytes(&info_buf
[info_len
], context
->id_context
->data
, context
->id_context
->len
);
370 info_len
+= cborencoder_put_null(&info_buf
[info_len
]);
372 info_len
+= cborencoder_put_unsigned(&info_buf
[info_len
], context
->algorithm
);
373 info_len
+= cborencoder_put_text(&info_buf
[info_len
], iv_label
, 2);
374 info_len
+= cborencoder_put_unsigned(&info_buf
[info_len
], iv_len
);
375 /* all static lengths, accounted for in OSCORE_INFO_MAX_LEN */
376 DISSECTOR_ASSERT(info_len
< OSCORE_INFO_MAX_LEN
);
377 g_byte_array_append(info
, info_buf
, info_len
);
378 g_byte_array_set_size(context
->common_iv
, iv_len
);
379 hkdf_expand(GCRY_MD_SHA256
, prk
, sizeof(prk
), info
->data
, info
->len
, context
->common_iv
->data
, iv_len
); /* 32 for SHA256 */
381 g_byte_array_free(info
, true);
385 static unsigned oscore_alg_get_key_len(cose_aead_alg_t algorithm
) {
387 case COSE_AES_CCM_16_64_128
:
388 return 16; /* RFC8152 */
395 static unsigned oscore_alg_get_tag_len(cose_aead_alg_t algorithm
) {
397 case COSE_AES_CCM_16_64_128
:
398 return 8; /* RFC8152 */
405 static unsigned oscore_alg_get_iv_len(cose_aead_alg_t algorithm
) {
407 case COSE_AES_CCM_16_64_128
:
408 return 13; /* RFC8152 */
415 static oscore_context_t
* oscore_find_context(oscore_info_t
*info
) {
418 for (i
= 0; i
< num_oscore_contexts
; i
++) {
419 if ((info
->kid_len
== oscore_contexts
[i
].sender_id
->len
) &&
420 memcmp(oscore_contexts
[i
].sender_id
->data
, info
->kid
, info
->kid_len
) == 0 &&
421 (info
->kid_context_len
== oscore_contexts
[i
].id_context
->len
) &&
422 memcmp(oscore_contexts
[i
].id_context
->data
, info
->kid_context
, info
->kid_context_len
) == 0) {
423 return &oscore_contexts
[i
];
430 CBOR encoding functions needed to construct HKDF info and aad.
431 Author Martin Gunnarsson <martin.gunnarsson@ri.se>
432 Modified by Malisa Vucinic <malishav@gmail.com>
435 cborencoder_put_text(uint8_t *buffer
, const char *text
, uint8_t text_len
) {
439 buffer
[ret
++] = 0x78;
440 buffer
[ret
++] = text_len
;
442 buffer
[ret
++] = (0x60 | text_len
);
445 if (text_len
!= 0 && text
!= NULL
) {
446 memcpy(&buffer
[ret
], text
, text_len
);
454 cborencoder_put_array(uint8_t *buffer
, uint8_t elements
) {
461 buffer
[ret
++] = (0x80 | elements
);
466 cborencoder_put_bytes(uint8_t *buffer
, const uint8_t *bytes
, uint8_t bytes_len
) {
470 buffer
[ret
++] = 0x58;
471 buffer
[ret
++] = bytes_len
;
473 buffer
[ret
++] = (0x40 | bytes_len
);
476 if (bytes_len
!= 0 && bytes
!= NULL
){
477 memcpy(&buffer
[ret
], bytes
, bytes_len
);
485 cborencoder_put_unsigned(uint8_t *buffer
, uint8_t value
) {
489 buffer
[ret
++] = 0x18;
490 buffer
[ret
++] = value
;
494 buffer
[ret
++] = value
;
499 cborencoder_put_null(uint8_t *buffer
) {
502 buffer
[ret
++] = 0xf6;
506 /* out should hold NONCE_MAX_LEN bytes at most */
508 oscore_create_nonce(uint8_t *out
,
509 oscore_context_t
*context
,
510 oscore_info_t
*info
) {
513 char piv_extended
[NONCE_MAX_LEN
] = { 0 };
517 GByteArray
*piv_generator
;
519 DISSECTOR_ASSERT(out
!= NULL
);
520 DISSECTOR_ASSERT(context
!= NULL
);
521 DISSECTOR_ASSERT(info
!= NULL
);
523 nonce_len
= oscore_alg_get_iv_len(context
->algorithm
);
524 DISSECTOR_ASSERT(nonce_len
<= NONCE_MAX_LEN
);
526 /* Recipient ID is the PIV generator ID if the PIV is present in the response */
527 if (info
->response
&& info
->piv_len
) {
528 piv_generator
= context
->recipient_id
;
530 piv_len
= info
->piv_len
;
532 piv_generator
= context
->sender_id
;
533 piv
= info
->request_piv
;
534 piv_len
= info
->request_piv_len
;
537 /* AEAD nonce is the XOR of Common IV left-padded to AEAD nonce length and the concatenation of:
538 * Step 3: Size of ID of the entity that generated PIV (1 byte),
539 * Step 2: ID of the entity that generated PIV (left-padded to "AEAD nonce length - 6 bytes"),
540 * Step 1: Partial IV (left-padded to OSCORE_PIV_MAX_LEN bytes).
544 DISSECTOR_ASSERT(piv_len
<= OSCORE_PIV_MAX_LEN
);
545 memcpy(&piv_extended
[nonce_len
- piv_len
], piv
, piv_len
);
548 DISSECTOR_ASSERT(piv_generator
->len
<= nonce_len
- 6);
549 memcpy(&piv_extended
[nonce_len
- OSCORE_PIV_MAX_LEN
- piv_generator
->len
], piv_generator
->data
, piv_generator
->len
);
552 piv_extended
[0] = piv_generator
->len
;
554 /* Now XOR with Common IV */
555 for (i
= 0; i
< nonce_len
; i
++) {
556 out
[i
] = piv_extended
[i
] ^ context
->common_iv
->data
[i
];
561 static oscore_decryption_status_t
562 oscore_decrypt_and_verify(tvbuff_t
*tvb_ciphertext
,
566 oscore_context_t
*context
,
568 tvbuff_t
**tvb_plaintext
) {
570 bool have_tag
= false;
571 uint8_t nonce
[NONCE_MAX_LEN
];
572 uint8_t tmp
[AES_128_BLOCK_LEN
];
574 uint8_t rx_tag
[TAG_MAX_LEN
];
575 unsigned tag_len
= 0;
576 uint8_t gen_tag
[TAG_MAX_LEN
];
577 uint8_t external_aad
[OSCORE_EXTERNAL_AAD_MAX_LEN
];
578 uint8_t external_aad_len
= 0;
579 uint8_t aad
[OSCORE_AAD_MAX_LEN
];
581 uint8_t *decryption_key
;
582 int ciphertext_captured_len
;
583 int ciphertext_reported_len
;
584 const char *encrypt0
= "Encrypt0";
585 proto_item
*item
= NULL
;
587 tag_len
= oscore_alg_get_tag_len(context
->algorithm
);
589 ciphertext_reported_len
= tvb_reported_length_remaining(tvb_ciphertext
, *offset
+ tag_len
);
591 if (ciphertext_reported_len
== 0) {
592 return STATUS_ERROR_MESSAGE_TOO_SMALL
;
595 /* Check if the payload is truncated. */
596 if (tvb_bytes_exist(tvb_ciphertext
, *offset
, ciphertext_reported_len
)) {
597 ciphertext_captured_len
= ciphertext_reported_len
;
600 ciphertext_captured_len
= tvb_captured_length_remaining(tvb_ciphertext
, *offset
);
603 /* Check if the tag is present in the captured data. */
604 have_tag
= tvb_bytes_exist(tvb_ciphertext
, *offset
+ ciphertext_reported_len
, tag_len
);
606 DISSECTOR_ASSERT(tag_len
<= sizeof(rx_tag
));
607 tvb_memcpy(tvb_ciphertext
, rx_tag
, *offset
+ ciphertext_reported_len
, tag_len
);
610 if (info
->response
) {
611 decryption_key
= context
->response_decryption_key
->data
;
613 decryption_key
= context
->request_decryption_key
->data
;
616 /* Create nonce to use for decryption and authenticity check */
617 oscore_create_nonce(nonce
, context
, info
);
620 * Create the CCM* initial block for decryption (Adata=0, M=0, counter=0).
621 * XXX: This only handles AES-CCM-16-64-128, add generic algorithm handling
623 ccm_init_block(tmp
, false, 0, 0, 0, 0, 0, nonce
);
626 * Make a copy of the ciphertext in heap memory.
628 * We will decrypt the message in-place and then use the buffer as the
629 * real data for the new tvb.
631 text
= (uint8_t *)tvb_memdup(pinfo
->pool
, tvb_ciphertext
, *offset
, ciphertext_captured_len
);
634 * Perform CTR-mode transformation and decrypt the tag.
635 * XXX: This only handles AES-CCM-16-64-128, add generic algorithm handling
637 if(ccm_ctr_encrypt(decryption_key
, tmp
, rx_tag
, text
, ciphertext_captured_len
) == false) {
638 return STATUS_ERROR_DECRYPT_FAILED
;
641 /* Create a tvbuff for the plaintext. */
642 *tvb_plaintext
= tvb_new_real_data(text
, ciphertext_captured_len
, ciphertext_reported_len
);
643 tvb_set_child_real_data_tvbuff(tvb_ciphertext
, *tvb_plaintext
);
644 add_new_data_source(pinfo
, *tvb_plaintext
, "Decrypted OSCORE");
647 /* Construct external_aad to be able to verify the tag */
649 /* Note that OSCORE_EXTERNAL_AAD_MAX_LEN calculation depends on the following construct.
650 * If this is updated - e.g. due to spec changes, added support for Class I options, or added
651 * support for other algorithms which would change max length of KID - do not forget to update the macro.
653 external_aad_len
+= cborencoder_put_array(&external_aad
[external_aad_len
], 5); /* 5 elements in the array */
654 external_aad_len
+= cborencoder_put_unsigned(&external_aad
[external_aad_len
], OSCORE_VERSION
);
655 external_aad_len
+= cborencoder_put_array(&external_aad
[external_aad_len
], 1);
656 external_aad_len
+= cborencoder_put_unsigned(&external_aad
[external_aad_len
], context
->algorithm
);
657 external_aad_len
+= cborencoder_put_bytes(&external_aad
[external_aad_len
], info
->kid
, info
->kid_len
);
658 external_aad_len
+= cborencoder_put_bytes(&external_aad
[external_aad_len
], info
->request_piv
, info
->request_piv_len
);
659 external_aad_len
+= cborencoder_put_bytes(&external_aad
[external_aad_len
], NULL
, 0); // Class I options not implemented/standardized yet
661 /* info->kid_len and info->piv_len come from the lower layer, other parameters are local.
662 * we end up here only if kid_len is matched to the one from the configured context through oscore_find_context()
663 * and piv_len is verified in the main dissection routine */
664 DISSECTOR_ASSERT(external_aad_len
< OSCORE_EXTERNAL_AAD_MAX_LEN
);
666 /* Note that OSCORE_AAD_MAX_LEN calculation depends on the following construct.
667 * If the construct below is modified, do not forget to update the macro.
669 aad_len
+= cborencoder_put_array(&aad
[aad_len
], 3); // COSE Encrypt0 structure with 3 elements
670 aad_len
+= cborencoder_put_text(&aad
[aad_len
], encrypt0
, 8); /* Text string "Encrypt0" */
671 aad_len
+= cborencoder_put_bytes(&aad
[aad_len
], NULL
, 0); /* Empty byte string */
672 aad_len
+= cborencoder_put_bytes(&aad
[aad_len
], external_aad
, external_aad_len
); /* OSCORE external_aad */
674 DISSECTOR_ASSERT(aad_len
< OSCORE_AAD_MAX_LEN
);
676 /* Compute CBC-MAC authentication tag. */
679 * Create the CCM* initial block for authentication (Adata!=0, M!=0, counter=l(m)).
680 * XXX: This only handles AES-CCM-16-64-128, add generic algorithm handling
682 DISSECTOR_ASSERT(tag_len
<= sizeof(gen_tag
));
683 ccm_init_block(tmp
, true, tag_len
, 0, 0, 0, ciphertext_captured_len
, nonce
);
684 /* text is already a raw buffer containing the plaintext since we just decrypted it in-place */
685 if (!ccm_cbc_mac(decryption_key
, tmp
, aad
, aad_len
, text
, ciphertext_captured_len
, gen_tag
)) {
686 return STATUS_ERROR_CBCMAC_FAILED
;
688 /* Compare the received tag with the one we generated. */
689 else if (memcmp(gen_tag
, rx_tag
, tag_len
) != 0) {
690 return STATUS_ERROR_TAG_CHECK_FAILED
;
693 /* Display the tag. */
695 item
= proto_tree_add_bytes(tree
, hf_oscore_tag
, tvb_ciphertext
, ciphertext_captured_len
, tag_len
, rx_tag
);
696 proto_item_set_generated(item
);
699 return STATUS_SUCCESS_DECRYPTED_TAG_CHECKED
;
700 } /* if (have_tag) */
702 return STATUS_SUCCESS_DECRYPTED_TAG_TRUNCATED
;
705 /* Code to actually dissect the packets */
707 oscore_dissect(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
710 /* Set up structures needed to add the protocol subtree and manage it */
712 proto_tree
*oscore_tree
;
713 /* Other misc. local variables. */
715 oscore_info_t
*info
= (oscore_info_t
*) data
;
716 oscore_context_t
*context
= NULL
;
717 oscore_decryption_status_t status
;
718 tvbuff_t
*tvb_decrypted
= NULL
;
723 /* Check that the packet is long enough for it to belong to us. */
724 if (tvb_reported_length(tvb
) < OSCORE_MIN_LENGTH
) {
729 /* Set the Protocol column to the constant string of oscore */
730 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "OSCORE");
732 /* create display subtree for the protocol */
733 ti
= proto_tree_add_item(tree
, proto_oscore
, tvb
, 0, -1, ENC_NA
);
735 oscore_tree
= proto_item_add_subtree(ti
, ett_oscore
);
737 if (info
->piv
== NULL
&& info
->request_piv
== NULL
) {
738 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_partial_iv_not_found
);
739 return tvb_reported_length(tvb
);
742 if ((context
= oscore_find_context(info
)) == NULL
) {
743 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_context_not_set
);
744 return tvb_reported_length(tvb
);
747 if (info
->piv_len
> OSCORE_PIV_MAX_LEN
) {
748 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_piv_len_invalid
);
749 return tvb_reported_length(tvb
);
752 status
= oscore_decrypt_and_verify(tvb
, pinfo
, &offset
, oscore_tree
, context
, info
, &tvb_decrypted
);
755 case STATUS_ERROR_DECRYPT_FAILED
:
756 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_decrypt_error
);
757 return tvb_reported_length(tvb
);
758 case STATUS_ERROR_CBCMAC_FAILED
:
759 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_cbc_mac_failed
);
760 return tvb_reported_length(tvb
);
761 case STATUS_ERROR_TAG_CHECK_FAILED
:
762 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_tag_check_failed
);
763 return tvb_reported_length(tvb
);
764 case STATUS_ERROR_MESSAGE_TOO_SMALL
:
765 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_message_too_small
);
766 return tvb_reported_length(tvb
);
767 case STATUS_SUCCESS_DECRYPTED_TAG_TRUNCATED
:
768 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_truncated
);
769 /* do not return, attempt dissection */
771 case STATUS_SUCCESS_DECRYPTED_TAG_CHECKED
:
775 DISSECTOR_ASSERT(tvb_decrypted
);
777 oscore_length
= tvb_reported_length(tvb_decrypted
);
779 /* Fetch CoAP info */
780 coinfo
= (coap_info
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_coap
, 0);
783 dissect_coap_code(tvb_decrypted
, oscore_tree
, &offset
, &dissect_oscore_hf
, &code_class
);
784 offset
= dissect_coap_options(tvb_decrypted
, pinfo
, oscore_tree
, offset
, oscore_length
, code_class
, coinfo
, &dissect_oscore_hf
);
785 if (oscore_length
> offset
) {
786 dissect_coap_payload(tvb_decrypted
, pinfo
, oscore_tree
, tree
, offset
, oscore_length
, code_class
, coinfo
, &dissect_oscore_hf
, true);
789 /* We don't support OSCORE over HTTP at the moment, where coinfo fetch will fail */
790 expert_add_info(pinfo
, oscore_tree
, &ei_oscore_info_fetch_failed
);
793 return tvb_captured_length(tvb
);
796 /* Register the protocol with Wireshark.
798 * This format is required because a script is used to build the C function that
799 * calls all the protocol registration.
802 proto_register_oscore(void)
804 module_t
*oscore_module
;
805 expert_module_t
*expert_oscore
;
807 static hf_register_info hf
[] = {
809 { "Decrypted Authentication Tag", "oscore.tag", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
812 COAP_COMMON_HF_LIST(dissect_oscore_hf
, "oscore")
815 /* Setup protocol subtree array */
816 static int *ett
[] = {
818 COAP_COMMON_ETT_LIST(dissect_oscore_hf
)
821 /* Setup protocol expert items */
822 static ei_register_info ei
[] = {
823 { &ei_oscore_partial_iv_not_found
,
824 { "oscore.partial_iv_not_found", PI_UNDECODED
, PI_WARN
,
825 "Partial IV not found - can't decrypt", EXPFILL
828 { &ei_oscore_context_not_set
,
829 { "oscore.context_not_set", PI_UNDECODED
, PI_WARN
,
830 "Security context not set - can't decrypt", EXPFILL
833 { &ei_oscore_message_too_small
,
834 { "oscore.message_too_small", PI_UNDECODED
, PI_WARN
,
835 "Message too small", EXPFILL
838 { &ei_oscore_truncated
,
839 { "oscore.truncated", PI_UNDECODED
, PI_WARN
,
840 "Message truncated, cannot verify authentication tag, but decryption is attempted", EXPFILL
843 { &ei_oscore_cbc_mac_failed
,
844 { "oscore.cbc_mac_failed", PI_UNDECODED
, PI_WARN
,
845 "Call to CBC-MAC failed", EXPFILL
848 { &ei_oscore_tag_check_failed
,
849 { "oscore.tag_check_failed", PI_UNDECODED
, PI_WARN
,
850 "Authentication tag check failed", EXPFILL
853 { &ei_oscore_decrypt_error
,
854 { "oscore.decrypt_error", PI_UNDECODED
, PI_WARN
,
855 "Decryption error", EXPFILL
858 { &ei_oscore_info_fetch_failed
,
859 { "oscore.info_fetch_failed", PI_UNDECODED
, PI_WARN
,
860 "Failed to fetch info from the lower layer - OSCORE over HTTP is not supported", EXPFILL
863 { &ei_oscore_piv_len_invalid
,
864 { "oscore.piv_len_invalid", PI_UNDECODED
, PI_WARN
,
865 "Partial IV length from the lower layer is invalid", EXPFILL
868 COAP_COMMON_EI_LIST(dissect_oscore_hf
, "oscore")
871 static uat_field_t oscore_context_uat_flds
[] = {
872 UAT_FLD_CSTRING(oscore_context_uat
,sender_id_prefs
,"Sender ID",
873 "Sender ID as HEX string. Should be 7 bytes or less. Mandatory."),
874 UAT_FLD_CSTRING(oscore_context_uat
,recipient_id_prefs
,"Recipient ID",
875 "Recipient ID as HEX string. Should be 7 bytes or less. Mandatory."),
876 UAT_FLD_CSTRING(oscore_context_uat
,master_secret_prefs
,"Master Secret",
877 "Master Secret as HEX string. Mandatory."),
878 UAT_FLD_CSTRING(oscore_context_uat
,master_salt_prefs
,"Master Salt",
879 "Master Salt as HEX string. Optional."),
880 UAT_FLD_CSTRING(oscore_context_uat
,id_context_prefs
,"ID Context",
881 "ID Context as HEX string. Optional."),
882 UAT_FLD_VS(oscore_context_uat
, algorithm
, "Algorithm", oscore_context_alg_vals
, "Decryption algorithm."),
886 /* Register the protocol name and description */
887 proto_oscore
= proto_register_protocol("Object Security for Constrained RESTful Environments",
890 /* Required function calls to register the header fields and subtrees */
891 proto_register_field_array(proto_oscore
, hf
, array_length(hf
));
892 proto_register_subtree_array(ett
, array_length(ett
));
894 /* Required function calls to register expert items */
895 expert_oscore
= expert_register_protocol(proto_oscore
);
896 expert_register_field_array(expert_oscore
, ei
, array_length(ei
));
898 oscore_module
= prefs_register_protocol(proto_oscore
, NULL
);
900 /* Create a UAT for security context management. */
901 oscore_context_uat
= uat_new("Security Contexts",
902 sizeof(oscore_context_t
), /* record size */
903 "oscore_contexts", /* filename */
904 true, /* from_profile */
905 &oscore_contexts
, /* data_ptr */
906 &num_oscore_contexts
, /* numitems_ptr */
907 UAT_AFFECTS_DISSECTION
, /* affects dissection of packets, but not set of named fields */
909 oscore_context_copy_cb
, /* copy callback */
910 oscore_context_update_cb
, /* update callback */
911 oscore_context_free_cb
, /* free callback */
912 oscore_context_post_update_cb
, /* post update callback */
913 NULL
, /* reset callback */
914 oscore_context_uat_flds
); /* UAT field definitions */
916 prefs_register_uat_preference(oscore_module
, "contexts",
918 "Security context configuration data",
921 register_dissector("oscore", oscore_dissect
, proto_oscore
);
923 proto_coap
= proto_get_id_by_short_name("CoAP");
926 /* We're called only by dissector name. */
927 void proto_reg_handoff_oscore(void)
932 * Editor modelines - https://www.wireshark.org/tools/modelines.html
937 * indent-tabs-mode: nil
940 * vi: set shiftwidth=4 tabstop=8 expandtab:
941 * :indentSize=4:tabSize=8:noTabs=true: