mirror of
https://github.com/eclipse/tinydtls.git
synced 2025-10-15 04:45:39 +08:00

this change is for resolving |sha2| symbol-conflict in using |tinydtls| with other crypto modules. this symbol-conflict could occur on the |tinydtls|-deployed systems with using |OpenSSL| or |OpenSSL|-like, which has another |SHA| symbols. In case of |iotivity-tinydtls|, the conflict occurs between |iotivity-tinydtls| and the deployed |crypto| library, which has also another |SHA|. [patch #1] initial commit [patch #2-3] updated commit message [patch #4] reverted renaming for internal symbols [patch #5] updated symbols with lower-case [patch #6] fixed typo for calling API bugs: https://bugs.eclipse.org/bugs/show_bug.cgi?id=490467 Change-Id: I6942870c7c31544e89a20209d21730406ffb08d2 Signed-off-by: Kyungsun Cho <goodsun.cho@samsung.com>
479 lines
12 KiB
C
479 lines
12 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <pcap/pcap.h>
|
|
|
|
#include "tinydtls.h"
|
|
#include "dtls_debug.h"
|
|
#include "dtls.h"
|
|
|
|
#define TRANSPORT_HEADER_SIZE (14+20+8) /* Ethernet + IP + UDP */
|
|
|
|
/* the pre_master_secret is generated from the PSK at startup */
|
|
unsigned char pre_master_secret[60];
|
|
size_t pre_master_len = 0;
|
|
|
|
unsigned char master_secret[DTLS_MASTER_SECRET_LENGTH];
|
|
size_t master_secret_len = 0;
|
|
|
|
dtls_security_parameters_t security_params[2];
|
|
int config = 0;
|
|
unsigned int epoch[2] = { 0, 0 };
|
|
|
|
#if DTLS_VERSION == 0xfeff
|
|
dtls_hash_t hs_hash[2];
|
|
#elif DTLS_VERSION == 0xfefd
|
|
dtls_hash_t hs_hash[1];
|
|
#endif
|
|
|
|
static inline void
|
|
update_hash(uint8 *record, size_t rlength,
|
|
uint8 *data, size_t data_length) {
|
|
int i;
|
|
|
|
if (!hs_hash[0])
|
|
return;
|
|
|
|
for (i = 0; i < sizeof(hs_hash) / sizeof(dtls_hash_t *); ++i) {
|
|
dtls_hash_update(hs_hash[i], data, data_length);
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
finalize_hash(uint8 *buf) {
|
|
#if DTLS_VERSION == 0xfeff
|
|
unsigned char statebuf[sizeof(md5_state_t) + sizeof(SHA_CTX)];
|
|
#elif DTLS_VERSION == 0xfefd
|
|
unsigned char statebuf[sizeof(dtls_sha256_ctx)];
|
|
#endif
|
|
|
|
if (!hs_hash[0])
|
|
return;
|
|
|
|
/* temporarily store hash status for roll-back after finalize */
|
|
#if DTLS_VERSION == 0xfeff
|
|
memcpy(statebuf, hs_hash[0], sizeof(md5_state_t));
|
|
memcpy(statebuf + sizeof(md5_state_t),
|
|
hs_hash[1],
|
|
sizeof(SHA_CTX));
|
|
#elif DTLS_VERSION == 0xfefd
|
|
memcpy(statebuf, hs_hash[0], sizeof(statebuf));
|
|
#endif
|
|
|
|
dtls_hash_finalize(buf, hs_hash[0]);
|
|
#if DTLS_VERSION == 0xfeff
|
|
dtls_hash_finalize(buf + 16, hs_hash[1]);
|
|
#endif
|
|
|
|
/* restore hash status */
|
|
#if DTLS_VERSION == 0xfeff
|
|
memcpy(hs_hash[0], statebuf, sizeof(md5_state_t));
|
|
memcpy(hs_hash[1],
|
|
statebuf + sizeof(md5_state_t),
|
|
sizeof(SHA_CTX));
|
|
#elif DTLS_VERSION == 0xfefd
|
|
memcpy(hs_hash[0], statebuf, sizeof(statebuf));
|
|
#endif
|
|
}
|
|
|
|
static inline void
|
|
clear_hash() {
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(hs_hash) / sizeof(dtls_hash_t *); ++i)
|
|
free(hs_hash[i]);
|
|
memset(hs_hash, 0, sizeof(hs_hash));
|
|
}
|
|
|
|
#undef CURRENT_CONFIG
|
|
#undef OTHER_CONFIG
|
|
#undef SWITCH_CONFIG
|
|
#define CURRENT_CONFIG (&security_params[config])
|
|
#define OTHER_CONFIG (&security_params[!(config & 0x01)])
|
|
#define SWITCH_CONFIG (config = !(config & 0x01))
|
|
|
|
int
|
|
pcap_verify(dtls_security_parameters_t *sec,
|
|
int is_client,
|
|
const unsigned char *record, size_t record_length,
|
|
const unsigned char *cleartext, size_t cleartext_length) {
|
|
|
|
unsigned char mac[DTLS_HMAC_MAX];
|
|
dtls_hmac_context_t hmac_ctx;
|
|
int ok;
|
|
|
|
if (cleartext_length < dtls_kb_digest_size(sec))
|
|
return 0;
|
|
|
|
dtls_hmac_init(&hmac_ctx,
|
|
is_client
|
|
? dtls_kb_client_mac_secret(sec)
|
|
: dtls_kb_server_mac_secret(sec),
|
|
dtls_kb_mac_secret_size(sec));
|
|
|
|
cleartext_length -= dtls_kb_digest_size(sec);
|
|
|
|
/* calculate MAC even if padding is wrong */
|
|
dtls_mac(&hmac_ctx,
|
|
record, /* the pre-filled record header */
|
|
cleartext, cleartext_length,
|
|
mac);
|
|
|
|
ok = memcmp(mac, cleartext + cleartext_length,
|
|
dtls_kb_digest_size(sec)) == 0;
|
|
#ifndef NDEBUG
|
|
printf("MAC (%s): ", ok ? "valid" : "invalid");
|
|
dump(mac, dtls_kb_digest_size(sec));
|
|
printf("\n");
|
|
#endif
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
decrypt_verify(int is_client, const uint8 *packet, size_t length,
|
|
uint8 **cleartext, size_t *clen) {
|
|
int res, ok = 0;
|
|
dtls_cipher_context_t *cipher;
|
|
|
|
static unsigned char buf[1000];
|
|
|
|
switch (CURRENT_CONFIG->cipher) {
|
|
case AES128: /* TLS_PSK_WITH_AES128_CBC_SHA */
|
|
*cleartext = buf;
|
|
*clen = length - sizeof(dtls_record_header_t);
|
|
|
|
if (is_client)
|
|
cipher = CURRENT_CONFIG->read_cipher;
|
|
else
|
|
cipher = CURRENT_CONFIG->write_cipher;
|
|
|
|
res = dtls_decrypt(cipher,
|
|
(uint8 *)packet + sizeof(dtls_record_header_t), *clen,
|
|
buf, NULL, 0);
|
|
|
|
if (res < 0) {
|
|
warn("decryption failed!\n");
|
|
} else {
|
|
ok = pcap_verify(CURRENT_CONFIG, is_client, (uint8 *)packet, length,
|
|
*cleartext, res);
|
|
|
|
if (ok)
|
|
*clen = res - dtls_kb_digest_size(CURRENT_CONFIG);
|
|
}
|
|
break;
|
|
default: /* no cipher suite selected */
|
|
*cleartext = (uint8 *)packet + sizeof(dtls_record_header_t);
|
|
*clen = length - sizeof(dtls_record_header_t);
|
|
|
|
ok = 1;
|
|
}
|
|
|
|
if (ok)
|
|
printf("verify OK\n");
|
|
else
|
|
printf("verification failed!\n");
|
|
return ok;
|
|
}
|
|
|
|
#define SKIP_ETH_HEADER(M,L) \
|
|
if ((L) < 14) \
|
|
return; \
|
|
else { \
|
|
(M) += 14; \
|
|
(L) -= 14; \
|
|
}
|
|
|
|
#define SKIP_IP_HEADER(M,L) \
|
|
if (((M)[0] & 0xF0) == 0x40) { /* IPv4 */ \
|
|
(M) += (M[0] & 0x0F) * 4; \
|
|
(L) -= (M[0] & 0x0F) * 4; \
|
|
} else \
|
|
if (((M)[0] & 0xF0) == 0x60) { /* IPv6 */ \
|
|
(M) += 40; \
|
|
(L) -= 40; \
|
|
}
|
|
|
|
#define SKIP_UDP_HEADER(M,L) { \
|
|
(M) += 8; \
|
|
(L) -= 8; \
|
|
}
|
|
|
|
void
|
|
handle_packet(const u_char *packet, int length) {
|
|
static int n = 0;
|
|
static unsigned char initial_hello[] = {
|
|
0x16, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
uint8 *data;
|
|
size_t data_length, rlen;
|
|
int i, res;
|
|
#if DTLS_VERSION == 0xfeff
|
|
#ifndef SHA1_DIGEST_LENGTH
|
|
#define SHA1_DIGEST_LENGTH 20
|
|
#endif
|
|
uint8 hash_buf[16 + SHA1_DIGEST_LENGTH];
|
|
#elif DTLS_VERSION == 0xfefd
|
|
uint8 hash_buf[DTLS_SHA256_DIGEST_LENGTH];
|
|
#endif
|
|
#define verify_data_length 12
|
|
int is_client;
|
|
n++;
|
|
|
|
SKIP_ETH_HEADER(packet, length);
|
|
SKIP_IP_HEADER(packet, length);
|
|
|
|
/* determine from port if this is a client */
|
|
is_client = dtls_uint16_to_int(packet) != 20220;
|
|
|
|
SKIP_UDP_HEADER(packet, length);
|
|
|
|
while (length) {
|
|
rlen = dtls_uint16_to_int(packet + 11) + sizeof(dtls_record_header_t);
|
|
|
|
if (!rlen) {
|
|
fprintf(stderr, "invalid length!\n");
|
|
return;
|
|
}
|
|
|
|
/* skip packet if it is from a different epoch */
|
|
if (dtls_uint16_to_int(packet + 3) != epoch[is_client])
|
|
goto next;
|
|
|
|
res = decrypt_verify(is_client, packet, rlen,
|
|
&data, &data_length);
|
|
|
|
if (res <= 0)
|
|
goto next;
|
|
|
|
printf("packet %d (from %s):\n", n, is_client ? "client" : "server");
|
|
hexdump(packet, sizeof(dtls_record_header_t));
|
|
printf("\n");
|
|
hexdump(data, data_length);
|
|
printf("\n");
|
|
|
|
if (packet[0] == 22 && data[0] == 1) { /* ClientHello */
|
|
if (memcmp(packet, initial_hello, sizeof(initial_hello)) == 0)
|
|
goto next;
|
|
|
|
memcpy(dtls_kb_client_iv(OTHER_CONFIG), data + 14, 32);
|
|
|
|
clear_hash();
|
|
#if DTLS_VERSION == 0xfeff
|
|
hs_hash[0] = dtls_new_hash(HASH_MD5);
|
|
hs_hash[1] = dtls_new_hash(HASH_SHA1);
|
|
|
|
hs_hash[0]->init(hs_hash[0]->data);
|
|
hs_hash[1]->init(hs_hash[1]->data);
|
|
#elif DTLS_VERSION == 0xfefd
|
|
dtls_hash_init(hs_hash[0]);
|
|
#endif
|
|
}
|
|
|
|
if (packet[0] == 22 && data[0] == 2) { /* ServerHello */
|
|
memcpy(dtls_kb_server_iv(OTHER_CONFIG), data + 14, 32);
|
|
/* FIXME: search in ciphers */
|
|
OTHER_CONFIG->cipher = TLS_PSK_WITH_AES_128_CCM_8;
|
|
}
|
|
|
|
if (packet[0] == 20 && data[0] == 1) { /* ChangeCipherSpec */
|
|
printf("client random: ");
|
|
dump(dtls_kb_client_iv(OTHER_CONFIG), 32);
|
|
printf("\nserver random: ");
|
|
dump(dtls_kb_server_iv(OTHER_CONFIG), 32);
|
|
printf("\n");
|
|
master_secret_len =
|
|
dtls_prf(pre_master_secret, pre_master_len,
|
|
(unsigned char *)"master secret", 13,
|
|
dtls_kb_client_iv(OTHER_CONFIG), 32,
|
|
dtls_kb_server_iv(OTHER_CONFIG), 32,
|
|
master_secret, DTLS_MASTER_SECRET_LENGTH);
|
|
|
|
printf("master_secret:\n ");
|
|
for(i = 0; i < master_secret_len; i++)
|
|
printf("%02x", master_secret[i]);
|
|
printf("\n");
|
|
|
|
/* create key_block from master_secret
|
|
* key_block = PRF(master_secret,
|
|
"key expansion" + server_random + client_random) */
|
|
dtls_prf(master_secret, master_secret_len,
|
|
(unsigned char *)"key expansion", 13,
|
|
dtls_kb_server_iv(OTHER_CONFIG), 32,
|
|
dtls_kb_client_iv(OTHER_CONFIG), 32,
|
|
OTHER_CONFIG->key_block,
|
|
dtls_kb_size(OTHER_CONFIG));
|
|
|
|
OTHER_CONFIG->read_cipher =
|
|
dtls_cipher_new(OTHER_CONFIG->cipher,
|
|
dtls_kb_client_write_key(OTHER_CONFIG),
|
|
dtls_kb_key_size(OTHER_CONFIG));
|
|
|
|
if (!OTHER_CONFIG->read_cipher) {
|
|
warn("cannot create read cipher\n");
|
|
} else {
|
|
dtls_cipher_set_iv(OTHER_CONFIG->read_cipher,
|
|
dtls_kb_client_iv(OTHER_CONFIG),
|
|
dtls_kb_iv_size(OTHER_CONFIG));
|
|
}
|
|
|
|
OTHER_CONFIG->write_cipher =
|
|
dtls_cipher_new(OTHER_CONFIG->cipher,
|
|
dtls_kb_server_write_key(OTHER_CONFIG),
|
|
dtls_kb_key_size(OTHER_CONFIG));
|
|
|
|
if (!OTHER_CONFIG->write_cipher) {
|
|
warn("cannot create write cipher\n");
|
|
} else {
|
|
dtls_cipher_set_iv(OTHER_CONFIG->write_cipher,
|
|
dtls_kb_server_iv(OTHER_CONFIG),
|
|
dtls_kb_iv_size(OTHER_CONFIG));
|
|
}
|
|
|
|
/* if (is_client) */
|
|
SWITCH_CONFIG;
|
|
epoch[is_client]++;
|
|
|
|
printf("key_block:\n");
|
|
printf(" client_MAC_secret:\t");
|
|
dump(dtls_kb_client_mac_secret(CURRENT_CONFIG),
|
|
dtls_kb_mac_secret_size(CURRENT_CONFIG));
|
|
printf("\n");
|
|
|
|
printf(" server_MAC_secret:\t");
|
|
dump(dtls_kb_server_mac_secret(CURRENT_CONFIG),
|
|
dtls_kb_mac_secret_size(CURRENT_CONFIG));
|
|
printf("\n");
|
|
|
|
printf(" client_write_key:\t");
|
|
dump(dtls_kb_client_write_key(CURRENT_CONFIG),
|
|
dtls_kb_key_size(CURRENT_CONFIG));
|
|
printf("\n");
|
|
|
|
printf(" server_write_key:\t");
|
|
dump(dtls_kb_server_write_key(CURRENT_CONFIG),
|
|
dtls_kb_key_size(CURRENT_CONFIG));
|
|
printf("\n");
|
|
|
|
printf(" client_IV:\t\t");
|
|
dump(dtls_kb_client_iv(CURRENT_CONFIG),
|
|
dtls_kb_iv_size(CURRENT_CONFIG));
|
|
printf("\n");
|
|
|
|
printf(" server_IV:\t\t");
|
|
dump(dtls_kb_server_iv(CURRENT_CONFIG),
|
|
dtls_kb_iv_size(CURRENT_CONFIG));
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
if (packet[0] == 22) {
|
|
if (data[0] == 20) { /* Finished */
|
|
finalize_hash(hash_buf);
|
|
/* clear_hash(); */
|
|
|
|
update_hash((unsigned char *)packet, sizeof(dtls_record_header_t),
|
|
data, data_length);
|
|
|
|
dtls_prf(master_secret, master_secret_len,
|
|
is_client
|
|
? (unsigned char *)"client finished"
|
|
: (unsigned char *)"server finished"
|
|
, 15,
|
|
hash_buf, sizeof(hash_buf),
|
|
NULL, 0,
|
|
data + sizeof(dtls_handshake_header_t),
|
|
verify_data_length);
|
|
printf("verify_data:\n");
|
|
dump(data, data_length);
|
|
printf("\n");
|
|
} else {
|
|
update_hash((unsigned char *)packet, sizeof(dtls_record_header_t),
|
|
data, data_length);
|
|
}
|
|
}
|
|
|
|
if (packet[0] == 23) { /* Application Data */
|
|
printf("Application Data:\n");
|
|
dump(data, data_length);
|
|
printf("\n");
|
|
}
|
|
|
|
next:
|
|
length -= rlen;
|
|
packet += rlen;
|
|
}
|
|
}
|
|
|
|
void init() {
|
|
memset(security_params, 0, sizeof(security_params));
|
|
CURRENT_CONFIG->cipher = -1;
|
|
|
|
memset(hs_hash, 0, sizeof(hs_hash));
|
|
|
|
/* set pre_master_secret to default if no PSK was given */
|
|
if (!pre_master_len) {
|
|
/* unsigned char psk[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; */
|
|
pre_master_len =
|
|
dtls_pre_master_secret((unsigned char *)"secretPSK", 9,
|
|
pre_master_secret);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
pcap_t *pcap;
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
struct pcap_pkthdr *pkthdr;
|
|
const u_char *packet;
|
|
int res = 0;
|
|
int c, option_index = 0;
|
|
|
|
static struct option opts[] = {
|
|
{ "psk", 1, 0, 'p' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
/* handle command line options */
|
|
while (1) {
|
|
c = getopt_long(argc, argv, "p:", opts, &option_index);
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 'p':
|
|
pre_master_len = dtls_pre_master_secret((unsigned char *)optarg,
|
|
strlen(optarg), pre_master_secret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argc <= optind) {
|
|
fprintf(stderr, "usage: %s [-p|--psk PSK] pcapfile\n", argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
init();
|
|
|
|
pcap = pcap_open_offline(argv[optind], errbuf);
|
|
if (!pcap) {
|
|
fprintf(stderr, "pcap_open_offline: %s\n", errbuf);
|
|
return -2;
|
|
}
|
|
|
|
for (;;) {
|
|
res = pcap_next_ex(pcap, &pkthdr, &packet);
|
|
|
|
switch(res) {
|
|
case -2: goto done;
|
|
case -1: pcap_perror(pcap, "read packet"); break;
|
|
case 1: handle_packet(packet, pkthdr->caplen); break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
done:
|
|
|
|
pcap_close(pcap);
|
|
|
|
return 0;
|
|
}
|