1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

tests/pkg_edhoc: initial import

This commit is contained in:
Francisco Molina 2021-03-05 12:21:52 +01:00
parent 4cef100781
commit fda3cbc60a
No known key found for this signature in database
GPG Key ID: 3E94EAC3DBDEEDA8
9 changed files with 1471 additions and 0 deletions

View File

@ -0,0 +1,39 @@
include ../Makefile.tests_common
# Edhoc related packages
USEPKG += edhoc-c
USEMODULE += edhoc-c_crypto_tinycrypt
# USEMODULE += edhoc-c_crypto_wolfssl
USEMODULE += edhoc-c_cbor_nanocbor
# Include packages that pull up and auto-init the link layer.
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
USEMODULE += gnrc_netdev_default
USEMODULE += auto_init_gnrc_netif
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_router_default
USEMODULE += sock_udp
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
USEMODULE += nanocoap_sock
# include this for printing IP addresses
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
USEMODULE += xtimer
# This is an optimized stack value based on testing, if you observe
# a segmentation fault please increase this stack size.
CFLAGS += -DTHREAD_STACKSIZE_MAIN=3*THREAD_STACKSIZE_LARGE
# Include responder code
CONFIG_INITIATOR ?= 1
CFLAGS += -DCONFIG_INITIATOR=$(CONFIG_INITIATOR)
# Include responder code
CONFIG_RESPONDER ?= 1
CFLAGS += -DCONFIG_RESPONDER=$(CONFIG_RESPONDER)
include $(RIOTBASE)/Makefile.include
include $(RIOTMAKE)/default-radio-settings.inc.mk

View File

@ -0,0 +1,51 @@
BOARD_INSUFFICIENT_MEMORY := \
airfy-beacon \
b-l072z-lrwan1 \
blackpill \
blackpill-128kib \
bluepill \
bluepill-128kib \
bluepill-stm32f030c8 \
calliope-mini \
cc1350-launchpad \
cc2650-launchpad \
cc2650stk \
e104-bt5010a-tb \
e104-bt5011a-tb \
hifive1 \
hifive1b \
i-nucleo-lrwan1 \
im880b \
lsn50 \
maple-mini \
microbit \
nrf51dk \
nrf51dongle \
nrf6310 \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f070rb \
nucleo-f072rb \
nucleo-f103rb \
nucleo-f302r8 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
nucleo-l073rz \
olimexino-stm32 \
opencm904 \
samd10-xmini \
saml10-xpro \
saml11-xpro \
slstk3400a \
spark-core \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32l0538-disco \
stm32mp157c-dk2 \
yunjia-nrf51822 \
#

418
tests/pkg_edhoc_c/README.md Normal file
View File

@ -0,0 +1,418 @@
# EDHOC-C test application
This test application sets up a RIOT node that can run as an EDHOC handshake
initiator and/or responder. The handshake can be run between two RIOT nodes or
between a RIOT node and a Linux host or for testing purposes the node can
perform an auto-handshake.
In this example credentials based on LAKE IETF WG are used. These are RPK keys.
Normally different credentials should be used depending on the authentication
method, but currently no validation is done so the same credentials can be
used for any method.
## EDHOC handshake between host and RIOT node
### Pre-requisites
- install [py-edhoc](https://github.com/openwsn-berkeley/py-edhoc):
```
$ pip install edhoc
```
This will install two cli utils `edhoc-responder` and `edhoc-initiator` to
facilitate testing.
#### `native`
- if using native set up a tap interface:
```
$ sudo ip tuntap add tap0 mode tap user ${USER}
$ sudo ip link set tap0 up
```
#### physical `BOARD`
- If using any other (non-emulated) `BOARD` then in one terminal:
```
$ sudo dist/tools/ethos/setup_network.sh riot0 2001:db8::/64
```
This will create a tap interface called `riot0`, owned by the user. It will
also run an instance of uhcpcd, which starts serving the prefix
`2001:db8::/64`. Keep the shell open as long as you need the network.
Make sure to exit the "make term" instance from the next section *before*
exiting this, as otherwise the "riot0" interface doesn't get cleaned up
properly.
### Responder
Find out the ipv6 address of the device is by running the `ifconfig`
command in the shell.
```
ifconfig
Starting the shell
> ifconfig
ifconfig
Iface 5 HWaddr: 0A:94:29:80:74:23
L2-PDU:1500 MTU:1500 HL:64 RTR
RTR_ADV
Source address length: 6
Link type: wired
inet6 addr: fe80::894:29ff:fe80:7423 scope: link VAL
inet6 group: ff02::2
inet6 group: ff02::1
inet6 group: ff02::1:ff80:7423
```
In this case its `fe80::894:29ff:fe80:7423` and since we setup `tap0` as
the tap interface the device is reachable at `[fe80::894:29ff:fe80:7423%tap0]`.
Initiate the handshake by running the `edhoc-initiator` cli tool:
```
$ edhoc-initiator fe80::894:29ff:fe80:7423%tap0]
INFO:root:POST (EdhocState.MSG_1_SENT) b'\x01\x00X \x89\x8f\xf7\x9a\x02\x06z\x16\xea\x1e\xcc\xb9\x0f\xa5"F\xf5\xaaM\xd6\xec\x07k\xba\x02Y\xd9\x04\xb7\xec\x8b\x0c@'
INFO:root:CHANGED (EdhocState.MSG_1_SENT) b'X q\xa3\xd5\x99\xc2\x1d\xa1\x89\x02\xa1\xae\xa8\x10\xb2\xb68,\xcd\x8d_\x9b\xf0\x19R\x81uL^\xbc\xaf0\x1e\x13XP\x99\xd58\x01\xa7%\xbf\xd6\xa4\xe7\x1d\x04\x84\xb7U\xec8=\xf7z\x91n\xc0\xdb\xc0+\xba|!\xa2\x00\x80{OX_r\x8bg\x1a\xd6x\xa4:\xac\xd3;x\xeb\xd5f\xcd\x00O\xc6\xf1\xd4\x06\xf0\x1d\x97\x04\xe7\x05\xb2\x15R\xa9\xeb(\xea1j\xb6P7\xd7\x17\x86.'
INFO:root:POST (EdhocState.MSG_3_SENT) b'\x01\x00X \x89\x8f\xf7\x9a\x02\x06z\x16\xea\x1e\xcc\xb9\x0f\xa5"F\xf5\xaaM\xd6\xec\x07k\xba\x02Y\xd9\x04\xb7\xec\x8b\x0c@'
INFO:root:EDHOC key exchange successfully completed:
INFO:root: - connection IDr: b'+'
INFO:root: - connection IDi: b''
INFO:root: - aead algorithm: AES_CCM_16_64_128
INFO:root: - hash algorithm: SHA_256
INFO:root: - OSCORE secret : b'Y!1k\xae\x12\xc9\xc4\xd2\xb9\xfb \xcc\x1a\x12\xdd'
INFO:root: - OSCORE salt : b'\xad\xf9\xfd\xbed\x98\xa3\x02'
```
And on the device (responder):
```
> [responder]: received an EDHOC message (len 37)
0x01 0x00 0x58 0x20 0x89 0x8f 0xf7 0x9a
0x02 0x06 0x7a 0x16 0xea 0x1e 0xcc 0xb9
0x0f 0xa5 0x22 0x46 0xf5 0xaa 0x4d 0xd6
0xec 0x07 0x6b 0xba 0x02 0x59 0xd9 0x04
0xb7 0xec 0x8b 0x0c 0x40
[responder]: sending msg2 (117 bytes)
0x58 0x20 0x71 0xa3 0xd5 0x99 0xc2 0x1d
0xa1 0x89 0x02 0xa1 0xae 0xa8 0x10 0xb2
0xb6 0x38 0x2c 0xcd 0x8d 0x5f 0x9b 0xf0
0x19 0x52 0x81 0x75 0x4c 0x5e 0xbc 0xaf
0x30 0x1e 0x13 0x58 0x50 0x99 0xd5 0x38
0x01 0xa7 0x25 0xbf 0xd6 0xa4 0xe7 0x1d
0x04 0x84 0xb7 0x55 0xec 0x38 0x3d 0xf7
0x7a 0x91 0x6e 0xc0 0xdb 0xc0 0x2b 0xba
0x7c 0x21 0xa2 0x00 0x80 0x7b 0x4f 0x58
0x5f 0x72 0x8b 0x67 0x1a 0xd6 0x78 0xa4
0x3a 0xac 0xd3 0x3b 0x78 0xeb 0xd5 0x66
0xcd 0x00 0x4f 0xc6 0xf1 0xd4 0x06 0xf0
0x1d 0x97 0x04 0xe7 0x05 0xb2 0x15 0x52
0xa9 0xeb 0x28 0xea 0x31 0x6a 0xb6 0x50
0x37 0xd7 0x17 0x86 0x2e
[responder]: received an EDHOC message (len 91)
0x13 0x58 0x58 0x2d 0x88 0xff 0x86 0xda
0x47 0x48 0x2c 0x0d 0xfa 0x55 0x9a 0xc8
0x24 0xa4 0xa7 0x83 0xd8 0x70 0xc9 0xdb
0xa4 0x78 0x05 0xe8 0xaa 0xfb 0xad 0x69
0x74 0xc4 0x96 0x46 0x58 0x65 0x03 0xfa
0x9b 0xbf 0x3e 0x00 0x01 0x2c 0x03 0x7e
0xaf 0x56 0xe4 0x5e 0x30 0x19 0x20 0x83
0x9b 0x81 0x3a 0x53 0xf6 0xd4 0xc5 0x57
0x48 0x0f 0x6c 0x79 0x7d 0x5b 0x76 0xf0
0xe4 0x62 0xf5 0xf5 0x7a 0x3d 0xb6 0xd2
0xb5 0x0c 0x32 0x31 0x9f 0x34 0x0f 0x4a
0xc5 0xaf 0x9a
[responder]: finalize exchange
[responder]: handshake successfully completed
```
### Initiator
First find out the local ipv6 address of the tap interface:
```
ifconfig tap0
tap0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::894:29ff:fe80:7422 prefixlen 64 scopeid 0x20<link>
ether 0a:94:29:80:74:22 txqueuelen 1000 (Ethernet)
RX packets 68 bytes 5962 (5.9 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 542 bytes 55480 (55.4 KB)
TX errors 0 dropped 1 overruns 0 carrier 0 collisions 0
```
In this case its `fe80::894:29ff:fe80:7422`. Next start the `py-edhoc`
based responder.
```
$ edhoc-reponder
INFO:root:Booting CoAP server
INFO:root:Initializing 'core' resource
INFO:root:Initializing 'edhoc' resource
```
Now from the RIOT shell initiate the handshake:
```
Starting the shell
> ini handshake fe80::894:29ff:fe80:7422 5683
init handshake fe80::894:29ff:fe80:7422 5683
[initiator]: sending message (37 bytes)
0x01 0x00 0x58 0x20 0x71 0xa3 0xd5 0x99
0xc2 0x1d 0xa1 0x89 0x02 0xa1 0xae 0xa8
0x10 0xb2 0xb6 0x38 0x2c 0xcd 0x8d 0x5f
0x9b 0xf0 0x19 0x52 0x81 0x75 0x4c 0x5e
0xbc 0xaf 0x30 0x1e 0x13
[initiator]: send 37 bytes to fe80::894:29ff:fe80:7422 on port 5683
[initiator]: received a message (124 bytes):
0x58 0x20 0x71 0xa3 0xd5 0x99 0xc2 0x1d
0xa1 0x89 0x02 0xa1 0xae 0xa8 0x10 0xb2
0xb6 0x38 0x2c 0xcd 0x8d 0x5f 0x9b 0xf0
0x19 0x52 0x81 0x75 0x4c 0x5e 0xbc 0xaf
0x30 0x1e 0x13 0x58 0x50 0x99 0xe1 0x3b
0xa4 0x65 0x44 0x44 0xe8 0xb6 0xd4 0x04
0x01 0x1e 0x01 0xa3 0x29 0xa3 0x26 0x05
0x45 0x99 0x33 0x95 0xf9 0x34 0x2b 0x43
0xa7 0x54 0xf9 0xe1 0x8b 0x0f 0xdc 0x46
0xc2 0xcc 0x4e 0x25 0x24 0x77 0xe0 0x83
0x52 0x0b 0xf4 0x36 0x74 0x53 0xb6 0x2b
0xbf 0x3e 0x14 0xb7 0xb0 0xea 0x0e 0xee
0x84 0xc5 0x5b 0x9e 0x64 0xfc 0x03 0x97
0xc0 0x45 0x18 0x6d 0x14 0xdb 0x88 0x8c
0x73 0x2f 0x95 0x52 0xf5 0x68 0xd1 0x61
0x56 0x6c 0xd1 0x61
[initiator]: sending message (91 bytes)
0x13 0x58 0x58 0x49 0x7a 0x3e 0x46 0xac
0xa1 0x36 0xbf 0xff 0xb9 0x5c 0x00 0x46
0x89 0x69 0x68 0x4a 0xe8 0x2d 0x83 0xf0
0xe5 0xc5 0xe3 0x3f 0x8f 0x17 0xdf 0x7c
0x72 0xe1 0xf2 0x9e 0x7a 0x2a 0xe8 0x88
0x75 0x16 0xd2 0x6a 0xe3 0xa7 0x73 0x76
0xe8 0xe5 0x22 0x14 0x43 0x6d 0xb0 0x37
0xb8 0x48 0x31 0xf3 0xa9 0xb3 0xfc 0x82
0x9c 0x4a 0x92 0x19 0x2c 0x3e 0x4a 0xfe
0x42 0x6c 0x11 0x39 0x6c 0x48 0x48 0x06
0x6b 0xf0 0xed 0x2e 0xff 0x16 0x91 0x08
0xf4 0xee 0x6e
[initiator]: send 91 bytes to fe80::894:29ff:fe80:7422 on port 5683
[initiator]: handshake successfully completed
```
And on the `edhoc-responder`:
```
INFO:root:CHANGED (EdhocState.MSG_2_SENT) b'X q\xa3\xd5\x99\xc2\x1d\xa1\x89\x02\xa1\xae\xa8\x10\xb2\xb68,\xcd\x8d_\x9b\xf0\x19R\x81uL^\xbc\xaf0\x1e\x13XP\x99\xe1;\xa4eDD\xe8\xb6\xd4\x04\x01\x1e\x01\xa3)\xa3&\x05E\x993\x95\xf94+C\xa7T\xf9\xe1\x8b\x0f\xdcF\xc2\xccN%$w\xe0\x83R\x0b\xf46tS\xb6+\xbf>\x14\xb7\xb0\xea\x0e\xee\x84\xc5[\x9ed\xfc\x03\x97\xc0E\x18m\x14\xdb\x88\x8cs/\x95R\xf5'
INFO:root:POST (EdhocState.MSG_2_SENT) b'\x13XXIz>F\xac\xa16\xbf\xff\xb9\\\x00F\x89ihJ\xe8-\x83\xf0\xe5\xc5\xe3?\x8f\x17\xdf|r\xe1\xf2\x9ez*\xe8\x88u\x16\xd2j\xe3\xa7sv\xe8\xe5"\x14Cm\xb07\xb8H1\xf3\xa9\xb3\xfc\x82\x9cJ\x92\x19,>J\xfeBl\x119lHH\x06k\xf0\xed.\xff\x16\x91\x08\xf4\xeen'
INFO:root:EDHOC key exchange successfully completed:
INFO:root: - connection IDr: b'+'
INFO:root: - connection IDi: b'+'
INFO:root: - aead algorithm: AES_CCM_16_64_128
INFO:root: - hash algorithm: SHA_256
INFO:root: - OSCORE secret : b'\xd8\x1e\xa3@\xec\xe3?3\xe1\xfe\x8a\x1d\x0c|\xd0\xbe'
INFO:root: - OSCORE salt : b'\x87\xf9J\xf7\x82Tq\xa3'
```
Congratulations you have performed an EDHOC handshake fo your HOST to
a RIOT node running as the initiator and responder. In all cases you can
now derive symmetric encryption keys from the shared master secret, in this
case a shell command `initiator/responder oscore` is available that derives
keys that could be used for an OSCORE context:
```
> ini oscore
init oscore
OSCORE secret:
0x43 0x83 0x97 0xe7 0xa8 0x3b 0xd7 0x35
0xdf 0x0d 0x47 0xdc 0x45 0x44 0xa4 0x63
OSCORE salt:
0x22 0x3b 0x3c 0xb1 0x03 0xc8 0xa3 0xd0
```
## EDHOC handshake between two RIOT nodes
### Pre-requisites
#### `native`
- if using `native` `BOARD`'s then create two tap interfaces linked
through a bridge:
```
sudo dist/tools/tapsetup/tapsetup -c 2
```
- bootstrap the `BOARD`s and specify the tap interface to use for each
```
PORT=tap0 make -C tests/pkg_edhoc_c all term
PORT=tap1 make -C tests/pkg_edhoc_c all term
```
#### physical `BOARD`s
- for other `BOARD`s make sure the chosen `BOARD`s has a netdev
through which they will be able to communicate.
- bootstrap the `BOARD`s
```
make -C tests/pkg_edhoc_c flash term
```
### Perform the handshake
One of the devices shall be the initiator and the other one the responder.
Both are already setup to listen for the first message.
In the shell of the node that will act as the responder identify its ipv6
address:
```
Starting the shell
> ifconfig
ifconfig
Iface 5 HWaddr: D6:76:BB:62:F2:AE
L2-PDU:1500 MTU:1500 HL:64 RTR
RTR_ADV
Source address length: 6
Link type: wired
inet6 addr: fe80::d476:bbff:fe62:f2ae scope: link VAL
inet6 group: ff02::2
inet6 group: ff02::1
inet6 group: ff02::1:ff62:f2ae
```
In this case `fe80::d476:bbff:fe62:f2ae`.
From the initiator now start the handshake:
```
initiator handshake fe80::d476:bbff:fe62:f2ae 5683
```
Yo should see the different messages being exchanged on both nodes, and
now both can derive OSCORE keys as well with the `responder/initiator oscore`
command:
- initiator:
```
> ini handshake fe80::d476:bbff:fe62:f2ae 5683
init handshake fe80::d476:bbff:fe62:f2ae 5683
[initiator]: sending message (37 bytes)
0x01 0x00 0x58 0x20 0x71 0xa3 0xd5 0x99
0xc2 0x1d 0xa1 0x89 0x02 0xa1 0xae 0xa8
0x10 0xb2 0xb6 0x38 0x2c 0xcd 0x8d 0x5f
0x9b 0xf0 0x19 0x52 0x81 0x75 0x4c 0x5e
0xbc 0xaf 0x30 0x1e 0x13
[initiator]: send 37 bytes to fe80::d476:bbff:fe62:f2ae on port 5683
[initiator]: received a message (126 bytes):
0x58 0x20 0x71 0xa3 0xd5 0x99 0xc2 0x1d
0xa1 0x89 0x02 0xa1 0xae 0xa8 0x10 0xb2
0xb6 0x38 0x2c 0xcd 0x8d 0x5f 0x9b 0xf0
0x19 0x52 0x81 0x75 0x4c 0x5e 0xbc 0xaf
0x30 0x1e 0x13 0x58 0x50 0x99 0xe1 0x3b
0xa4 0x65 0x44 0x44 0xe8 0xb6 0xd4 0x04
0x01 0x1e 0x01 0xa3 0x29 0xa3 0x26 0x05
0x45 0x99 0x33 0x95 0xf9 0x34 0x2b 0x43
0xa7 0x54 0xf9 0xe1 0x8b 0x0f 0xdc 0x46
0xc2 0xcc 0x4e 0x25 0x24 0x77 0xe0 0x83
0x52 0x0b 0xf4 0x36 0x74 0x53 0xb6 0x2b
0xbf 0x3e 0x14 0xb7 0xb0 0xea 0x0e 0xee
0x84 0xc5 0x5b 0x9e 0x64 0xfc 0x03 0x97
0xc0 0x45 0x18 0x6d 0x14 0xdb 0x88 0x8c
0x73 0x2f 0x95 0x52 0xf5 0x60 0x56 0x6c
0xa1 0x60 0x56 0x70 0xa1 0x60
[initiator]: sending message (91 bytes)
0x13 0x58 0x58 0x49 0x7a 0x3e 0x46 0xac
0xa1 0x36 0xbf 0xff 0xb9 0x5c 0x00 0x46
0x89 0x69 0x68 0x4a 0xe8 0x2d 0x83 0xf0
0xe5 0xc5 0xe3 0x3f 0x8f 0x17 0xdf 0x7c
0x72 0xe1 0xf2 0x9e 0x7a 0x2a 0xe8 0x88
0x75 0x16 0xd2 0x6a 0xe3 0xa7 0x73 0x76
0xe8 0xe5 0x22 0x14 0x43 0x6d 0xb0 0x37
0xb8 0x48 0x31 0xf3 0xa9 0xb3 0xfc 0x82
0x9c 0x4a 0x92 0x19 0x2c 0x3e 0x4a 0xfe
0x42 0x6c 0x11 0x39 0x6c 0x48 0x48 0x06
0x6b 0xf0 0xed 0x2e 0xff 0x16 0x91 0x08
0xf4 0xee 0x6e
[initiator]: send 91 bytes to fe80::d476:bbff:fe62:f2ae on port 5683
[initiator]: handshake successfully completed
```
- responder:
```
> [responder]: received an EDHOC message (len 37)
0x01 0x00 0x58 0x20 0x71 0xa3 0xd5 0x99
0xc2 0x1d 0xa1 0x89 0x02 0xa1 0xae 0xa8
0x10 0xb2 0xb6 0x38 0x2c 0xcd 0x8d 0x5f
0x9b 0xf0 0x19 0x52 0x81 0x75 0x4c 0x5e
0xbc 0xaf 0x30 0x1e 0x13
[responder]: sending msg2 (117 bytes)
0x58 0x20 0x71 0xa3 0xd5 0x99 0xc2 0x1d
0xa1 0x89 0x02 0xa1 0xae 0xa8 0x10 0xb2
0xb6 0x38 0x2c 0xcd 0x8d 0x5f 0x9b 0xf0
0x19 0x52 0x81 0x75 0x4c 0x5e 0xbc 0xaf
0x30 0x1e 0x13 0x58 0x50 0x99 0xe1 0x3b
0xa4 0x65 0x44 0x44 0xe8 0xb6 0xd4 0x04
0x01 0x1e 0x01 0xa3 0x29 0xa3 0x26 0x05
0x45 0x99 0x33 0x95 0xf9 0x34 0x2b 0x43
0xa7 0x54 0xf9 0xe1 0x8b 0x0f 0xdc 0x46
0xc2 0xcc 0x4e 0x25 0x24 0x77 0xe0 0x83
0x52 0x0b 0xf4 0x36 0x74 0x53 0xb6 0x2b
0xbf 0x3e 0x14 0xb7 0xb0 0xea 0x0e 0xee
0x84 0xc5 0x5b 0x9e 0x64 0xfc 0x03 0x97
0xc0 0x45 0x18 0x6d 0x14 0xdb 0x88 0x8c
0x73 0x2f 0x95 0x52 0xf5
[responder]: received an EDHOC message (len 91)
0x13 0x58 0x58 0x49 0x7a 0x3e 0x46 0xac
0xa1 0x36 0xbf 0xff 0xb9 0x5c 0x00 0x46
0x89 0x69 0x68 0x4a 0xe8 0x2d 0x83 0xf0
0xe5 0xc5 0xe3 0x3f 0x8f 0x17 0xdf 0x7c
0x72 0xe1 0xf2 0x9e 0x7a 0x2a 0xe8 0x88
0x75 0x16 0xd2 0x6a 0xe3 0xa7 0x73 0x76
0xe8 0xe5 0x22 0x14 0x43 0x6d 0xb0 0x37
0xb8 0x48 0x31 0xf3 0xa9 0xb3 0xfc 0x82
0x9c 0x4a 0x92 0x19 0x2c 0x3e 0x4a 0xfe
0x42 0x6c 0x11 0x39 0x6c 0x48 0x48 0x06
0x6b 0xf0 0xed 0x2e 0xff 0x16 0x91 0x08
0xf4 0xee 0x6e
[responder]: finalize exchange
[responder]: handshake successfully completed
```
- oscore keys:
```
> ini oscore
init oscore
OSCORE secret:
0x43 0x83 0x97 0xe7 0xa8 0x3b 0xd7 0x35
0xdf 0x0d 0x47 0xdc 0x45 0x44 0xa4 0x63
OSCORE salt:
0x22 0x3b 0x3c 0xb1 0x03 0xc8 0xa3 0xd0
```
## EDHOC automatic test
As long as a BOARD with a netdev interface is used is as simple as:
```
$ make -C tests/pkg_edhoc_c flash test-with-config
```

141
tests/pkg_edhoc_c/common.c Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2021 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief EDHOC initiator/responder common setup code
*
* @author Timothy Claeys <timothy.claeys@inria.fr>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*/
#include <stdio.h>
#include <string.h>
#include "kernel_defines.h"
#include "edhoc/edhoc.h"
#include "edhoc_keys.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define CRED_DB_SIZE ARRAY_SIZE(cred_db)
int _cred_cb(const uint8_t *k, size_t k_len, const uint8_t **o, size_t *o_len)
{
for (uint8_t i = 0; i < (uint8_t)CRED_DB_SIZE; i++) {
if (cred_db[i].id_len == k_len) {
if (memcmp(cred_db[i].id, k, k_len) == 0) {
*o = cred_db[i].cred;
*o_len = cred_db[i].cred_len;
return 0;
}
}
}
*o = NULL;
*o_len = 0;
return EDHOC_ERR_INVALID_CRED_ID;
}
void print_bstr(const uint8_t *bstr, size_t bstr_len)
{
for (size_t i = 0; i < bstr_len; i++) {
if ((i + 1) % 8 == 0) {
printf("0x%02x \n", bstr[i]);
}
else {
printf("0x%02x ", bstr[i]);
}
}
printf("\n");
}
int edhoc_setup(edhoc_ctx_t *ctx, edhoc_conf_t *conf, edhoc_role_t role,
cose_key_t *auth_key, cred_id_t *cred_id, rpk_t *rpk,
void *hash_ctx)
{
/* clear/init context and configuration */
edhoc_ctx_init(ctx);
edhoc_conf_init(conf);
cred_id_init(cred_id);
cred_rpk_init(rpk);
cose_key_init(auth_key);
/* only for testing load preset keys for role */
const uint8_t *cbor_auth_key = NULL;
const uint8_t *cbor_rpk = NULL;
const uint8_t *cbor_rpk_id = NULL;
size_t cbor_auth_key_len = 0;
size_t cbor_rpk_len = 0;
size_t cbor_rpk_id_len;
if (role == EDHOC_IS_RESPONDER) {
DEBUG_PUTS("[edhoc]: setting up responder");
cbor_auth_key = resp_cbor_auth_key;
cbor_auth_key_len = sizeof(resp_cbor_auth_key);
cbor_rpk = resp_cbor_rpk;
cbor_rpk_len = sizeof(resp_cbor_rpk);
cbor_rpk_id = resp_cbor_rpk_id;
cbor_rpk_id_len = sizeof(resp_cbor_rpk_id);
}
else {
DEBUG_PUTS("[edhoc]: setting up initiator");
cbor_auth_key = init_cbor_auth_key;
cbor_auth_key_len = sizeof(init_cbor_auth_key);
cbor_rpk = init_cbor_rpk;
cbor_rpk_len = sizeof(init_cbor_rpk);
cbor_rpk_id = init_cbor_rpk_id;
cbor_rpk_id_len = sizeof(init_cbor_rpk_id);
}
DEBUG_PUTS("[edhoc]: load private authentication key");
if (cose_key_from_cbor(auth_key, cbor_auth_key, cbor_auth_key_len) != 0) {
return -1;
}
DEBUG_PUTS("[edhoc]: load and set CBOR RPK");
if (cred_rpk_from_cbor(rpk, cbor_rpk, cbor_rpk_len) != 0) {
return -1;
}
DEBUG_PUTS("[edhoc]: load credential identifier information");
if (cred_id_from_cbor(cred_id, cbor_rpk_id, cbor_rpk_id_len) != 0) {
return -1;
}
DEBUG_PUTS("[edhoc]: set up EDHOC callbacks and role");
edhoc_conf_setup_ad_callbacks(conf, NULL, NULL, NULL);
if (edhoc_conf_setup_role(conf, role) != 0) {
return -1;
}
DEBUG_PUTS("[edhoc]: set up EDHOC credentials");
if (edhoc_conf_setup_credentials(conf, auth_key, CRED_TYPE_RPK, rpk, cred_id, _cred_cb) != 0) {
return -1;
}
DEBUG_PUTS("[edhoc]: EDHOC context setup");
edhoc_ctx_setup(ctx, conf, hash_ctx);
return 0;
}
void edhoc_oscore_exporter(edhoc_ctx_t *ctx, uint8_t *secret, size_t secret_len,
uint8_t *salt, size_t salt_len)
{
edhoc_exporter(ctx, "OSCORE secret", secret_len, secret, secret_len);
edhoc_exporter(ctx, "OSCORE salt", salt_len, salt, salt_len);
puts("OSCORE secret:");
print_bstr(secret, secret_len);
puts("OSCORE salt:");
print_bstr(salt, salt_len);
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (C) 2021 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Certificates and keys for the edhoc example. This values
* are taken from the IETF lake WG test vectors, specifically
* test vector 34900, see:
* https://github.com/lake-wg/edhoc/blob/5ef58e6ee998f4b9aca4b53b35e87375ca356f32/test-vectors-05/vectors.txt
*
* @author Timothy Claeys <timothy.claeys@inria.fr>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#ifndef EDHOC_KEYS_H
#define EDHOC_KEYS_H
#include <inttypes.h>
#include "kernel_defines.h"
#ifdef __cplusplus
extern "C" {
#endif
/* initiator CBOR-encoded authentication key */
static const uint8_t init_cbor_auth_key[] = {
0xa4, 0x01, 0x01, 0x20, 0x06, 0x21, 0x58, 0x20,
0x2c, 0x44, 0x0c, 0xc1, 0x21, 0xf8, 0xd7, 0xf2,
0x4c, 0x3b, 0x0e, 0x41, 0xae, 0xda, 0xfe, 0x9c,
0xaa, 0x4f, 0x4e, 0x7a, 0xbb, 0x83, 0x5e, 0xc3,
0x0f, 0x1d, 0xe8, 0x8a, 0xdb, 0x96, 0xff, 0x71,
0x23, 0x58, 0x20, 0x2b, 0xbe, 0xa6, 0x55, 0xc2,
0x33, 0x71, 0xc3, 0x29, 0xcf, 0xbd, 0x3b, 0x1f,
0x02, 0xc6, 0xc0, 0x62, 0x03, 0x38, 0x37, 0xb8,
0xb5, 0x90, 0x99, 0xa4, 0x43, 0x6f, 0x66, 0x60,
0x81, 0xb0, 0x8e
};
/* initiator CBOR-encoded ephemeral key */
static const uint8_t init_cbor_eph_key[] = {
0xa4, 0x01, 0x01, 0x20, 0x04, 0x21, 0x58, 0x20,
0x8d, 0x3e, 0xf5, 0x6d, 0x1b, 0x75, 0x0a, 0x43,
0x51, 0xd6, 0x8a, 0xc2, 0x50, 0xa0, 0xe8, 0x83,
0x79, 0x0e, 0xfc, 0x80, 0xa5, 0x38, 0xa4, 0x44,
0xee, 0x9e, 0x2b, 0x57, 0xe2, 0x44, 0x1a, 0x7c,
0x23, 0x58, 0x20, 0xae, 0x11, 0xa0, 0xdb, 0x86,
0x3c, 0x02, 0x27, 0xe5, 0x39, 0x92, 0xfe, 0xb8,
0xf5, 0x92, 0x4c, 0x50, 0xd0, 0xa7, 0xba, 0x6e,
0xea, 0xb4, 0xad, 0x1f, 0xf2, 0x45, 0x72, 0xf4,
0xf5, 0x7c, 0xfa
};
/* initiator CBOR-encoded RPK */
static const uint8_t init_cbor_rpk[] = {
0xa4, 0x01, 0x01, 0x20, 0x04, 0x21, 0x58, 0x20,
0x2c, 0x44, 0x0c, 0xc1, 0x21, 0xf8, 0xd7, 0xf2,
0x4c, 0x3b, 0x0e, 0x41, 0xae, 0xda, 0xfe, 0x9c,
0xaa, 0x4f, 0x4e, 0x7a, 0xbb, 0x83, 0x5e, 0xc3,
0x0f, 0x1d, 0xe8, 0x8a, 0xdb, 0x96, 0xff, 0x71,
0x6c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x20, 0x6e, 0x61, 0x6d, 0x65, 0x60
};
/* initiator CBOR-encoded rpk identifier */
static const uint8_t init_cbor_rpk_id[] = {
0xa1, 0x04, 0x41, 0x23
};
/* initiator CBOR-encoded rpk identifier */
static const uint8_t init_cbor_rpk_id_value[] = {
0x23
};
/* initiator session identifier preset */
static const uint8_t init_cid[] = {
0x16
};
/* responder CBOR-encoded authentication key */
static const uint8_t resp_cbor_auth_key[] = {
0xa4, 0x01, 0x01, 0x20, 0x06, 0x21, 0x58, 0x20,
0xa3, 0xff, 0x26, 0x35, 0x95, 0xbe, 0xb3, 0x77,
0xd1, 0xa0, 0xce, 0x1d, 0x04, 0xda, 0xd2, 0xd4,
0x09, 0x66, 0xac, 0x6b, 0xcb, 0x62, 0x20, 0x51,
0xb8, 0x46, 0x59, 0x18, 0x4d, 0x5d, 0x9a, 0x32,
0x23, 0x58, 0x20, 0xbb, 0x50, 0x1a, 0xac, 0x67,
0xb9, 0xa9, 0x5f, 0x97, 0xe0, 0xed, 0xed, 0x6b,
0x82, 0xa6, 0x62, 0x93, 0x4f, 0xbb, 0xfc, 0x7a,
0xd1, 0xb7, 0x4c, 0x1f, 0xca, 0xd6, 0x6a, 0x07,
0x94, 0x22, 0xd0
};
/* responder CBOR-encoded ephemeral key */
static const uint8_t resp_cbor_eph_key[] = {
0xa4, 0x01, 0x01, 0x20, 0x04, 0x21, 0x58, 0x20,
0x52, 0xfb, 0xa0, 0xbd, 0xc8, 0xd9, 0x53, 0xdd,
0x86, 0xce, 0x1a, 0xb2, 0xfd, 0x7c, 0x05, 0xa4,
0x65, 0x8c, 0x7c, 0x30, 0xaf, 0xdb, 0xfc, 0x33,
0x01, 0x04, 0x70, 0x69, 0x45, 0x1b, 0xaf, 0x35,
0x23, 0x58, 0x20, 0xc6, 0x46, 0xcd, 0xdc, 0x58,
0x12, 0x6e, 0x18, 0x10, 0x5f, 0x01, 0xce, 0x35,
0x05, 0x6e, 0x5e, 0xbc, 0x35, 0xf4, 0xd4, 0xcc,
0x51, 0x07, 0x49, 0xa3, 0xa5, 0xe0, 0x69, 0xc1,
0x16, 0x16, 0x9a
};
/* responder CBOR-encoded RPK */
static const uint8_t resp_cbor_rpk[] = {
0xa4, 0x01, 0x01, 0x20, 0x04, 0x21, 0x58, 0x20,
0xa3, 0xff, 0x26, 0x35, 0x95, 0xbe, 0xb3, 0x77,
0xd1, 0xa0, 0xce, 0x1d, 0x04, 0xda, 0xd2, 0xd4,
0x09, 0x66, 0xac, 0x6b, 0xcb, 0x62, 0x20, 0x51,
0xb8, 0x46, 0x59, 0x18, 0x4d, 0x5d, 0x9a, 0x32,
0x6c, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x20, 0x6e, 0x61, 0x6d, 0x65, 0x60
};
/* responder CBOR-encoded rpk identifier */
static const uint8_t resp_cbor_rpk_id[] = {
0xa1, 0x04, 0x41, 0x05
};
/* responder CBOR-encoded rpk identifier */
static const uint8_t resp_cbor_rpk_id_value[] = {
0x05
};
/* responder session identifier preset */
static const uint8_t resp_cid[] = {
0x00
};
/**
* @brief Credential database entry
*/
typedef struct {
const uint8_t *id; /**< credential id pointer */
size_t id_len; /**< credential id length */
const uint8_t *cred; /**< credential pointer */
size_t cred_len; /**< credential length */
} cred_db_entry_t;
/* credential database */
static const cred_db_entry_t cred_db[] = {
{
resp_cbor_rpk_id_value,
sizeof(resp_cbor_rpk_id_value),
resp_cbor_rpk,
sizeof(resp_cbor_rpk)
},
{
init_cbor_rpk_id_value,
sizeof(init_cbor_rpk_id_value),
init_cbor_rpk,
sizeof(init_cbor_rpk)
},
};
#ifdef __cplusplus
}
#endif
#endif /* EDHOC_KEYS_H */

View File

@ -0,0 +1,294 @@
/*
* Copyright (C) 2021 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief EDHOC coap initiator implementation
*
* @author Timothy Claeys <timothy.claeys@inria.fr>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*/
#include <stdio.h>
#include "net/ipv6.h"
#include "net/nanocoap_sock.h"
#include "shell.h"
#include "edhoc/edhoc.h"
#include "edhoc_keys.h"
#if IS_USED(MODULE_WOLFSSL)
#include "wolfssl/wolfcrypt/sha256.h"
#elif IS_USED(MODULE_TINYCRYPT)
#include "tinycrypt/sha256.h"
#endif
#define ENABLE_DEBUG 0
#include "debug.h"
#define COAP_BUF_SIZE (256U)
#if IS_ACTIVE(CONFIG_INITIATOR)
extern void print_bstr(const uint8_t *bstr, size_t bstr_len);
extern int edhoc_setup(edhoc_ctx_t *ctx, edhoc_conf_t *conf, edhoc_role_t role,
cose_key_t *auth_key, cred_id_t *cred_id, rpk_t *rpk,
void *hash_ctx);
extern void edhoc_oscore_exporter(edhoc_ctx_t *ctx, uint8_t *secret, size_t secret_len,
uint8_t *salt, size_t salt_len);
static edhoc_ctx_t _ctx;
static edhoc_conf_t _conf;
static rpk_t _rpk;
static cred_id_t _cred_id;
static cose_key_t _auth_key;
#if IS_USED(MODULE_WOLFSSL)
static wc_Sha256 _sha_i;
#elif IS_USED(MODULE_TINYCRYPT)
struct tc_sha256_state_struct _sha_i;
#endif
static uint8_t _method;
static uint8_t _suite;
static ssize_t _send(coap_pkt_t *pkt, size_t len, char *addr_str, uint16_t port)
{
ipv6_addr_t addr;
sock_udp_ep_t remote;
remote.family = AF_INET6;
remote.port = port;
/* parse for interface */
char *iface = ipv6_addr_split_iface(addr_str);
if (!iface) {
if (gnrc_netif_numof() == 1) {
/* assign the single interface found in gnrc_netif_numof() */
remote.netif = (uint16_t)gnrc_netif_iter(NULL)->pid;
}
else {
remote.netif = SOCK_ADDR_ANY_NETIF;
}
}
else {
int pid = atoi(iface);
if (gnrc_netif_get_by_pid(pid) == NULL) {
puts("[initiator]: interface not valid");
return 0;
}
remote.netif = pid;
}
/* parse destination address */
if (ipv6_addr_from_str(&addr, addr_str) == NULL) {
puts("[initiator]: unable to parse destination address");
return 0;
}
if ((remote.netif == SOCK_ADDR_ANY_NETIF) && ipv6_addr_is_link_local(&addr)) {
puts("[initiator]: must specify interface for link local target");
return 0;
}
memcpy(&remote.addr.ipv6[0], &addr.u8[0], sizeof(addr.u8));
return nanocoap_request(pkt, NULL, &remote, len);
}
static ssize_t _build_coap_pkt(coap_pkt_t *pkt, uint8_t *buf, ssize_t buflen,
uint8_t *payload, ssize_t payload_len)
{
uint8_t token[2] = { 0xDA, 0xEC };
ssize_t len = 0;
/* set pkt buffer */
pkt->hdr = (coap_hdr_t *)buf;
/* build header, confirmed message always post */
ssize_t hdrlen = coap_build_hdr(pkt->hdr, COAP_TYPE_CON, token,
sizeof(token), COAP_METHOD_POST, 1);
coap_pkt_init(pkt, buf, buflen, hdrlen);
coap_opt_add_string(pkt, COAP_OPT_URI_PATH, "/.well-known/edhoc", '/');
coap_opt_add_uint(pkt, COAP_OPT_CONTENT_FORMAT, COAP_FORMAT_OCTET);
len = coap_opt_finish(pkt, COAP_OPT_FINISH_PAYLOAD);
/* copy msg payload */
pkt->payload_len = payload_len;
memcpy(pkt->payload, payload, payload_len);
len += pkt->payload_len;
return len;
}
int _handshake_cmd(int argc, char **argv)
{
uint8_t buf[COAP_BUF_SIZE] = { 0 };
coap_pkt_t pkt;
ssize_t len = 0;
uint16_t port = COAP_PORT;
uint8_t msg[COAP_BUF_SIZE];
ssize_t msg_len = 0;
/* correlation value is transport specific */
corr_t corr = CORR_1_2;
if (argc < 2) {
printf("usage: %s <addr>[%%iface] <port>\n", argv[0]);
return -1;
}
if (argc == 3) {
port = atoi(argv[2]);
}
/* reset state */
_ctx.state = EDHOC_WAITING;
if ((msg_len = edhoc_create_msg1(&_ctx, corr, _method, _suite, msg, sizeof(msg))) > 0) {
printf("[initiator]: sending msg1 (%d bytes):\n", (int) msg_len);
print_bstr(msg, msg_len);
_build_coap_pkt(&pkt, buf, sizeof(buf), msg, msg_len);
len = _send(&pkt, COAP_BUF_SIZE, argv[1], port);
}
else {
puts("[initiator]: failed to create msg1");
return -1;
}
if (len < 0) {
puts("[initiator]: failed to send msg1");
return -1;
}
printf("[initiator]: received a message (%d bytes):\n", pkt.payload_len);
print_bstr(pkt.payload, pkt.payload_len);
if ((msg_len = edhoc_create_msg3(&_ctx, pkt.payload, pkt.payload_len, msg, sizeof(msg))) > 0) {
printf("[initiator]: sending msg3 (%d bytes):\n", (int) msg_len);
print_bstr(msg, msg_len);
_build_coap_pkt(&pkt, buf, sizeof(buf), msg, msg_len);
len = _send(&pkt, COAP_BUF_SIZE, argv[1], port);
}
else {
puts("[initiator]: failed to create msg3");
return -1;
}
if (edhoc_init_finalize(&_ctx)) {
puts("[initiator]: handshake failed");
return -1;
}
puts("[initiator]: handshake successfully completed");
printf("[initiator]: Transcript hash 4 (%d bytes):\n", EDHOC_DIGEST_SIZE);
print_bstr(_ctx.session.th4, EDHOC_DIGEST_SIZE);
_ctx.state = EDHOC_FINALIZED;
return 0;
}
static void _print_initiator_usage(void)
{
puts("Usage:");
puts("\tinit set method <sgn-sgn|sgn-stat|stat-sgn|stat-stat>:"
" choose authentication method [initiator-responder]");
puts("\tinit set suite <0|1>: choose cypher-suit, only 0 and 1 support");
puts("\tinit handshake <addr>[%%iface] <port>: start handshake");
puts("\tinit oscore: derive OSCORE secret and salt");
}
static int _set_cmd(int argc, char **argv)
{
if (argc < 2) {
_print_initiator_usage();
return -1;
}
if (!strcmp(argv[1], "method")) {
if (!strcmp(argv[2], "sgn-sgn")) {
_method = EDHOC_AUTH_SIGN_SIGN;
}
else if (!strcmp(argv[2], "sgn-stat")) {
_method = EDHOC_AUTH_SIGN_STATIC;
}
else if (!strcmp(argv[2], "stat-sgn")) {
_method = EDHOC_AUTH_STATIC_SIGN;
}
else if (!strcmp(argv[2], "stat-stat")) {
_method = EDHOC_AUTH_STATIC_STATIC;
}
else {
printf("error: invalid method %s, sgn-sgn|sgn-stat|stat-sgn|stat-stat\n", argv[1]);
return -1;
}
}
if (!strcmp(argv[1], "suite")) {
uint8_t suite = atoi(argv[2]);
if (suite > 1) {
puts("error: only cypher suits 0 and 1 are supported");
return -1;
}
_suite = suite;
}
return 0;
}
int initiator_cmd(int argc, char **argv)
{
if (argc < 2) {
_print_initiator_usage();
return -1;
}
if (!strcmp(argv[1], "set")) {
return _set_cmd(argc - 1, &argv[1]);
}
if (!strcmp(argv[1], "handshake")) {
return _handshake_cmd(argc - 1, &argv[1]);
}
if (!strcmp(argv[1], "oscore")) {
if (_ctx.state == EDHOC_FINALIZED) {
uint8_t secret[16];
uint8_t salt[8];
edhoc_oscore_exporter(&_ctx, secret, sizeof(secret), salt, sizeof(salt));
}
else {
puts("error: perform edhoc handshake first");
}
}
return 0;
}
int initiator_cli_init(void)
{
/* default to static-static (method 3) since we are using RPK keys */
_method = EDHOC_AUTH_STATIC_STATIC;
_suite = EDHOC_CIPHER_SUITE_0;
if (edhoc_setup(&_ctx, &_conf, EDHOC_IS_INITIATOR, &_auth_key, &_cred_id,
&_rpk, &_sha_i)) {
puts("[initiator]: error during setup");
return -1;
}
/* use fixed values only for testing purposes */
puts("[initiator]: load ephemeral key: ONLY FOR TESTING");
if (edhoc_load_ephkey(&_ctx, init_cbor_eph_key, sizeof(init_cbor_eph_key)) != 0) {
return -1;
}
puts("[initiator]: preset cid: ONLY FOR TESTING");
if (edhoc_session_preset_cidi(&_ctx, init_cid, sizeof(init_cid)) != 0) {
return -1;
}
return 0;
}
#endif /* CONFIG_INITIATOR */

103
tests/pkg_edhoc_c/main.c Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2021 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief EDHOC handhshake over COAP using EDHOC-C
*
* @author Timothy Claeys <timothy.claeys@inria.fr>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*/
#include <stdio.h>
#include "net/nanocoap_sock.h"
#include "shell.h"
#include "thread.h"
#include "edhoc/edhoc.h"
#include "edhoc_keys.h"
#define MAIN_QUEUE_SIZE (4)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
#if IS_ACTIVE(CONFIG_RESPONDER)
static char _nanocoap_server_stack[THREAD_STACKSIZE_MAIN];
#define NANOCOAP_SERVER_QUEUE_SIZE (4)
static msg_t _nanocoap_server_msg_queue[NANOCOAP_SERVER_QUEUE_SIZE];
#define NANOCOAP_BUF_SIZE (512U)
extern int responder_cli_init(void);
extern int responder_cmd(int argc, char **argv);
#endif
#if IS_ACTIVE(CONFIG_INITIATOR)
extern int initiator_cmd(int argc, char **argv);
extern int initiator_cli_init(void);
#endif
static const shell_command_t shell_commands[] = {
#if IS_ACTIVE(CONFIG_INITIATOR)
{ "init", "EDHOC Initiator cli", initiator_cmd },
#endif
#if IS_ACTIVE(CONFIG_RESPONDER)
{ "resp", "EDHOC Responder cli", responder_cmd },
#endif
{ NULL, NULL, NULL }
};
#if IS_ACTIVE(CONFIG_RESPONDER)
static void *_nanocoap_server_thread(void *arg)
{
(void)arg;
/* nanocoap_server uses gnrc sock which uses gnrc which needs a msg queue */
msg_init_queue(_nanocoap_server_msg_queue, NANOCOAP_SERVER_QUEUE_SIZE);
/* initialize nanocoap server instance */
uint8_t buf[NANOCOAP_BUF_SIZE];
sock_udp_ep_t local = { .port = COAP_PORT, .family = AF_INET6 };
nanocoap_server(&local, buf, sizeof(buf));
return NULL;
}
#endif
int main(void)
{
#if IS_ACTIVE(CONFIG_INITIATOR)
if (initiator_cli_init()) {
return -1;
}
#endif
#if IS_ACTIVE(CONFIG_RESPONDER)
if (responder_cli_init()) {
return -1;
}
/* start nanocoap server thread */
thread_create(_nanocoap_server_stack, sizeof(_nanocoap_server_stack),
THREAD_PRIORITY_MAIN - 1,
THREAD_CREATE_STACKTEST,
_nanocoap_server_thread, NULL, "nanocoap server");
#endif
/* the shell contains commands that receive packets via GNRC and thus
needs a msg queue */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
puts("Starting the shell");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
/* should be never reached */
return 0;
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2021 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief EDHOC coap responder implementation
*
* @author Timothy Claeys <timothy.claeys@inria.fr>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
*/
#include <stdio.h>
#include "net/ipv6.h"
#include "net/nanocoap_sock.h"
#include "shell.h"
#include "edhoc/edhoc.h"
#include "edhoc_keys.h"
#if IS_USED(MODULE_WOLFSSL)
#include "wolfssl/wolfcrypt/sha256.h"
#elif IS_USED(MODULE_TINYCRYPT)
#include "tinycrypt/sha256.h"
#endif
#define ENABLE_DEBUG 0
#include "debug.h"
#define COAP_BUF_SIZE (256U)
#if IS_ACTIVE(CONFIG_RESPONDER)
extern void print_bstr(const uint8_t *bstr, size_t bstr_len);
extern int edhoc_setup(edhoc_ctx_t *ctx, edhoc_conf_t *conf, edhoc_role_t role,
cose_key_t *auth_key, cred_id_t *cred_id, rpk_t *rpk,
void *hash_ctx);
extern void edhoc_oscore_exporter(edhoc_ctx_t *ctx, uint8_t *secret, size_t secret_len,
uint8_t *salt, size_t salt_len);
static edhoc_ctx_t _ctx;
static edhoc_conf_t _conf;
static rpk_t _rpk;
static cred_id_t _cred_id;
static cose_key_t _auth_key;
#if IS_USED(MODULE_WOLFSSL)
static wc_Sha256 _sha_r;
#elif IS_USED(MODULE_TINYCRYPT)
struct tc_sha256_state_struct _sha_r;
#endif
ssize_t _edhoc_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context)
{
(void)context;
ssize_t msg_len = 0;
printf("[responder]: received an EDHOC message (len %d):\n", pkt->payload_len);
print_bstr(pkt->payload, pkt->payload_len);
if (_ctx.state == EDHOC_FINALIZED || _ctx.state == EDHOC_FAILED) {
_ctx.state = EDHOC_WAITING;
}
if (_ctx.state == EDHOC_WAITING) {
uint8_t msg[COAP_BUF_SIZE];
if ((msg_len =
edhoc_create_msg2(&_ctx, pkt->payload, pkt->payload_len, msg, sizeof(msg))) >= 0) {
printf("[responder]: sending msg2 (%d bytes):\n", (int) msg_len);
print_bstr(msg, msg_len);
msg_len = coap_reply_simple(pkt, COAP_CODE_204, buf, len, COAP_FORMAT_OCTET, msg,
msg_len);
}
else {
puts("[responder]: failed to create msg2");
coap_reply_simple(pkt, COAP_CODE_404, buf, len, COAP_FORMAT_TEXT, NULL, 0);
return -1;
}
}
else if (_ctx.state == EDHOC_SENT_MESSAGE_2) {
puts("[responder]: finalize exchange");
edhoc_resp_finalize(&_ctx, pkt->payload, pkt->payload_len, false, NULL, 0);
msg_len = coap_reply_simple(pkt, COAP_CODE_204, buf, len, COAP_FORMAT_OCTET, NULL, 0);
}
if (_ctx.state == EDHOC_FINALIZED) {
puts("[responder]: handshake successfully completed");
}
return msg_len;
}
/* must be sorted by path (ASCII order) */
const coap_resource_t coap_resources[] = {
COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER,
{ "/.well-known/edhoc", COAP_POST, _edhoc_handler, NULL },
};
const unsigned coap_resources_numof = ARRAY_SIZE(coap_resources);
int responder_cmd(int argc, char **argv)
{
if (argc < 2) {
puts("Usage:");
puts("\tresp oscore: derive OSCORE secret and salt");
return -1;
}
if (!strcmp(argv[1], "oscore")) {
if (_ctx.state == EDHOC_FINALIZED) {
uint8_t secret[16];
uint8_t salt[8];
edhoc_oscore_exporter(&_ctx, secret, sizeof(secret), salt, sizeof(salt));
}
else {
puts("error: perform edhoc handshake first");
}
}
return 0;
}
int responder_cli_init(void)
{
if (edhoc_setup(&_ctx, &_conf, EDHOC_IS_RESPONDER, &_auth_key, &_cred_id,
&_rpk, &_sha_r)) {
puts("[responder]: error during setup");
return -1;
}
/* use fixed values only for testing purposes */
puts("[responder]: load ephemeral key: ONLY FOR TESTING");
if (edhoc_load_ephkey(&_ctx, resp_cbor_eph_key, sizeof(resp_cbor_eph_key)) != 0) {
return -1;
}
puts("[responder]: preset cid: ONLY FOR TESTING");
if (edhoc_session_preset_cidr(&_ctx, resp_cid, sizeof(resp_cid)) != 0) {
return -1;
}
return 0;
}
#endif /* CONFIG_RESPONDER */

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python3
# Copyright (C) 2021 Inria
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import os
import sys
from testrunner import run
# Default COAP port on which the edhoc responder is running
COAP_PORT = int(os.getenv("COAP_PORT", "5683"))
LAKE_WG_EDHOC_TV_34900_MSG1 = \
"0x0d 0x00 0x58 0x20 0x8d 0x3e 0xf5 0x6d\n" + \
"0x1b 0x75 0x0a 0x43 0x51 0xd6 0x8a 0xc2\n" + \
"0x50 0xa0 0xe8 0x83 0x79 0x0e 0xfc 0x80\n" + \
"0xa5 0x38 0xa4 0x44 0xee 0x9e 0x2b 0x57\n" + \
"0xe2 0x44 0x1a 0x7c 0x21"
LAKE_WG_EDHOC_TV_34900_MSG2 = \
"0x58 0x20 0x52 0xfb 0xa0 0xbd 0xc8 0xd9\n" + \
"0x53 0xdd 0x86 0xce 0x1a 0xb2 0xfd 0x7c\n" + \
"0x05 0xa4 0x65 0x8c 0x7c 0x30 0xaf 0xdb\n" + \
"0xfc 0x33 0x01 0x04 0x70 0x69 0x45 0x1b\n" + \
"0xaf 0x35 0x37 0x4a 0xa3 0xf1 0xbd 0x5d\n" + \
"0x02 0x8d 0x19 0xcf 0x3c 0x99"
LAKE_WG_EDHOC_TV_34900_MSG3 = \
"0x37 0x52 0xd5 0x53 0x5f 0x31 0x47 0xe8\n" + \
"0x5f 0x1c 0xfa 0xcd 0x9e 0x78 0xab 0xf9\n" + \
"0xe0 0xa8 0x1b 0xbf"
LAKE_WG_EDHOC_TV_34900_TH4 = \
"0x7c 0xcf 0xde 0xdc 0x2c 0x10 0xca 0x03\n" + \
"0x56 0xe9 0x57 0xb9 0xf6 0xa5 0x92 0xe0\n" + \
"0xfa 0x74 0xdb 0x2a 0xb5 0x4f 0x59 0x24\n" + \
"0x40 0x96 0xf9 0xa2 0xac 0x56 0xd2 0x07"
LAKE_WG_EDHOC_TV_34900_OSCORE_SECRET = \
"0x5b 0xb2 0xae 0xe2 0x5b 0x16 0x0e 0x7c\n" + \
"0x6d 0x26 0x12 0xb0 0xa6 0x01 0x09 0x16"
LAKE_WG_EDHOC_TV_34900_OSCORE_SALT = \
"0x8e 0x44 0x92 0x10 0xe0 0x3b 0xc2 0x9d"
def get_ipv6_addr(child):
child.expect_exact('>')
child.sendline('ifconfig')
# Get device local address
child.expect(
r"inet6\s+addr:\s+(?P<lladdr>[0-9a-fA-F:]+:[A-Fa-f:0-9]+)"
" scope: link VAL"
)
return child.match.group("lladdr").lower()
def testfunc(child):
child.sendline("init handshake {} {}".format(
get_ipv6_addr(child), COAP_PORT))
child.expect_exact("[initiator]: sending msg1 (37 bytes):")
for line in LAKE_WG_EDHOC_TV_34900_MSG1.split('\n'):
child.expect_exact(line)
child.expect_exact("[responder]: received an EDHOC message (len 37):")
child.expect_exact("[responder]: sending msg2 (46 bytes):")
for line in LAKE_WG_EDHOC_TV_34900_MSG2.split('\n'):
child.expect_exact(line)
child.expect_exact("[initiator]: received a message (46 bytes):")
child.expect_exact("[initiator]: sending msg3 (20 bytes)")
for line in LAKE_WG_EDHOC_TV_34900_MSG3.split('\n'):
child.expect_exact(line)
child.expect_exact("[responder]: finalize exchange")
child.expect_exact("[responder]: handshake successfully completed")
child.expect_exact("[initiator]: handshake successfully completed")
child.expect_exact("[initiator]: Transcript hash 4 (32 bytes):")
for line in LAKE_WG_EDHOC_TV_34900_TH4.split('\n'):
child.expect_exact(line)
child.sendline("init oscore")
child.expect_exact("OSCORE secret:")
for line in LAKE_WG_EDHOC_TV_34900_OSCORE_SECRET.split('\n'):
child.expect_exact(line)
child.expect_exact("OSCORE salt:")
child.expect_exact(LAKE_WG_EDHOC_TV_34900_OSCORE_SALT)
child.sendline("resp oscore")
child.expect_exact("OSCORE secret:")
for line in LAKE_WG_EDHOC_TV_34900_OSCORE_SECRET.split('\n'):
child.expect_exact(line)
child.expect_exact("OSCORE salt:")
child.expect_exact(LAKE_WG_EDHOC_TV_34900_OSCORE_SALT)
if __name__ == "__main__":
sys.exit(run(testfunc))