Difference between revisions of "CoMatrix: OSCORE"
(Zwischenspeichern) |
|||
Line 17: | Line 17: | ||
OSCORE is a security protocol designed to provide end-to-end protection between endpoints communicating using CoAP. | OSCORE is a security protocol designed to provide end-to-end protection between endpoints communicating using CoAP. | ||
The keys are pre-shared between the client and the gateway because there is no key exchange protocol in OSCORE itself. However, there is ongoing work with key exchange protocols such as EDHOC or [https://datatracker.ietf.org/doc/html/draft-ietf-ace-oscore-profile-11 ACE-OSCORE] | |||
== Gateway == | == Gateway == | ||
The Python library aiocoap | The Python library aiocoap provides the OSCORE functionality and was used in order for the gateway to understand OSCORE within CoAP. | ||
=== Imports === | === Imports === | ||
The following imports were added | |||
from aiocoap.oscore_sitewrapper import OscoreSiteWrapper | from aiocoap.oscore_sitewrapper import OscoreSiteWrapper | ||
Line 111: | Line 111: | ||
== Client == | == Client == | ||
// | |||
The Comatrix project already uses RIOT-OS for the client board so we continued using it. For the implementation of OSCORE on RIOT-OS we tried using liboscore. It comes with it's own build stack and is therefore different from the instructions provided by the Comatrix project. | |||
=== Setup the build stack === | |||
First, the liboscore project has to be cloned locally with the recursive parameter so that it contains RIOT-OS itself. This can be achieved by following the steps provided in [https://oscore.gitlab.io/liboscore/demo_common.html Common set-up instructions for RIOT-based demos] | |||
The folders 'comatrix', 'example_comatrix_chat' and 'example_comatrix_tempsensor' from the Comatrix client directory then have to be copied into the folder 'liboscore/tests/riot-tests/'. | |||
Because the RIOT-OS is now different in relation to the code the Makefile has to be adapted. | |||
Remove this line: | |||
include $(RIOTBASE)/Makefile.include | |||
Add this line: | |||
include ../Makefile.include | |||
The project can then be built by using | |||
make all | |||
=== Adapt liboscore === | |||
The comatrix examples for the chat and the tempsensor both use the source code in the comatrix folder which handles the CoAP messaging. This is why we did not change the examples but rather the comatrix code to implement OSCORE centrally. | |||
Because the liboscore is still in a very early state and wasn't maintained for about two years we rarely found working examples so the focus was set onto the 'plugtest-server' code within the riot-tests folder which is documented as [https://oscore.gitlab.io/liboscore/demo_plugtest_linux.html Demo: Running the OSCORE plug test server on RIOT native ]. | |||
This code makes use of threading and mutex because it also acts as a server which we don't need because the chat and tempsensor examples only act as clients. We needed to remvove those parts and only use the functions for preparing and encrypting the OSCORE message. | |||
First we added the includes to oscore | |||
#include <oscore_native/message.h> | |||
#include <oscore/contextpair.h> | |||
#include <oscore/context_impl/primitive.h> | |||
#include <oscore/context_impl/b1.h> | |||
#include <oscore/protection.h> | |||
Then, to generate the security context, we need to call the script 'oscore-key-derivation' as documented in [https://oscore.gitlab.io/liboscore/demo_peertopeer.html Demo: Peer-to-peer exchanges in 6LoWPAN network], but we used the parameter '--format header' to retrieve the context in a matter that the compiler understands. | |||
This block is then copied to the comatrix.c file. An example of this is: | |||
#define SENDER_KEY {255, 177, 78, 9, 60, 148, 201, 202, 201, 71, 22, 72, 180, 249, 135, 16} | |||
#define RECIPIENT_KEY {240, 145, 14, 215, 41, 94, 106, 212, 181, 79, 199, 147, 21, 67, 2, 255} | |||
#define COMMON_IV {70, 34, 212, 221, 109, 148, 65, 104, 238, 251, 84, 152, 124} | |||
#define SENDER_ID {1} | |||
#define RECIPIENT_ID {} | |||
The security context then has to be created | |||
static struct oscore_context_primitive_immutables immutables_d = { | |||
.common_iv = COMMON_IV, | |||
.recipient_id_len = 0, | |||
.recipient_key = RECIPIENT_KEY, | |||
.sender_id_len = 1, | |||
.sender_id = "\x01", | |||
.sender_key = SENDER_KEY, | |||
}; | |||
static struct oscore_context_primitive primitive_d = { .immutables = &immutables_d }; | |||
oscore_context_t secctx_d = { | |||
.type = OSCORE_CONTEXT_PRIMITIVE, | |||
.data = (void*)(&primitive_d), | |||
}; | |||
int16_t secctx_d_change = 0; | |||
== Used Hardware == | == Used Hardware == |
Revision as of 21:16, 4 July 2023
Summary
This documentation describes the usage of an application-layer protocol OSCORE to provide end-to-end protection between the CoMatrix Gateway and CoMatrix Client.
Requirements
- Operating system:
- Ubuntu 22.04 (Gateway)
- RIOT OS (Client)
- See #Used Hardware
In order to complete these steps, you must set up the CoMatrix project before.
Description
OSCORE is a security protocol designed to provide end-to-end protection between endpoints communicating using CoAP.
The keys are pre-shared between the client and the gateway because there is no key exchange protocol in OSCORE itself. However, there is ongoing work with key exchange protocols such as EDHOC or ACE-OSCORE
Gateway
The Python library aiocoap provides the OSCORE functionality and was used in order for the gateway to understand OSCORE within CoAP.
Imports
The following imports were added
from aiocoap.oscore_sitewrapper import OscoreSiteWrapper from aiocoap.credentials import CredentialsMap from plugtest_common import * from aiocoap.cli.common import server_context_from_arguments, add_server_arguments
- The plugtest_common import was extracted from the aiocoap OSCORE plugtest and provides verification for external AADs and returning the security context.
Main
When starting the gateway, new parameters have been added to list the directory where the pre-shared secrets are held and sequence numbers will be saved. These sequence numbers are necessary for replay protection.
parser.add_argument("contextdir", help="Directory name where to persist sequence numbers and the location of the secrets", type=Path) // Necessary to add these arguments provided by the aiocoap library in order for the server to start correctly. More info under aiocoap/cli/common.py add_server_arguments(parser)
The context directory may contain two files:
- secrets.json
- settings.json
As per implementation it is sufficient to only have one of these files in the directory.
The format of the file is the following JSON:
{ "sender-id_hex": "01", "recipient-id_ascii": "file", "secret_ascii": "Correct Horse Battery Staple" }
Important properties are:
- sender-id
- recipient-id
- secret
These three properties must be appended with either _hex or _ascii and have the values according to the suffix.
Assume that the file above was for the server and for our client we would need the following file:
{ "sender-id_ascii": "file", "recipient-id_hex": "01", "secret_ascii": "Correct Horse Battery Staple" }
Notice how sender-id and recipient-id had to be swapped. With these two secret files it is now possible to start a gateway and a client each using a different JSON file with the ids flipped.
For further information see aiocoap-OSCORE
Loading these in the code was done in the following way - For testing purposes there were two pairs of credentials a / b and c / d whereas the first one is used for the test client and the latter for the gateway:
// Loading the pre-shared secrets server_credentials = CredentialsMap() // the first parameter states the name of the context in this case it's b but can be named in any way and the second parameter is the path to the folder e.g. the file would be located in /contextdir/b/settings.json server_credentials[':b'] = get_security_context('b', args.contextdir / "b") server_credentials[':d'] = get_security_context('d', args.contextdir / "d")
To enable OSCORE for the server now the following code was added/adjusted:
// root is the variable for the site // Enable the site to talK OSCORE with the credentials loaded from before root = OscoreSiteWrapper(root, server_credentials) args.bind = bind // Now we create a server context with arguments added asyncio.Task(server_context_from_arguments(root, args))
USAGE: ./comatrix_gateway.py contextdir
To test if the gateway actually talks in OSCORE the plugtest-client from the aiocoap library was adjusted to meet our needs for comatrix.
USAGE: ./unit-tests.py host contextdir
Wireshark Capture
The Wireshark Capture can be downloaded here: Media:OSCORE-wireshark-capture.zip
Client
The Comatrix project already uses RIOT-OS for the client board so we continued using it. For the implementation of OSCORE on RIOT-OS we tried using liboscore. It comes with it's own build stack and is therefore different from the instructions provided by the Comatrix project.
Setup the build stack
First, the liboscore project has to be cloned locally with the recursive parameter so that it contains RIOT-OS itself. This can be achieved by following the steps provided in Common set-up instructions for RIOT-based demos The folders 'comatrix', 'example_comatrix_chat' and 'example_comatrix_tempsensor' from the Comatrix client directory then have to be copied into the folder 'liboscore/tests/riot-tests/'. Because the RIOT-OS is now different in relation to the code the Makefile has to be adapted.
Remove this line:
include $(RIOTBASE)/Makefile.include
Add this line:
include ../Makefile.include
The project can then be built by using
make all
Adapt liboscore
The comatrix examples for the chat and the tempsensor both use the source code in the comatrix folder which handles the CoAP messaging. This is why we did not change the examples but rather the comatrix code to implement OSCORE centrally. Because the liboscore is still in a very early state and wasn't maintained for about two years we rarely found working examples so the focus was set onto the 'plugtest-server' code within the riot-tests folder which is documented as Demo: Running the OSCORE plug test server on RIOT native . This code makes use of threading and mutex because it also acts as a server which we don't need because the chat and tempsensor examples only act as clients. We needed to remvove those parts and only use the functions for preparing and encrypting the OSCORE message.
First we added the includes to oscore
#include <oscore_native/message.h> #include <oscore/contextpair.h> #include <oscore/context_impl/primitive.h> #include <oscore/context_impl/b1.h> #include <oscore/protection.h>
Then, to generate the security context, we need to call the script 'oscore-key-derivation' as documented in Demo: Peer-to-peer exchanges in 6LoWPAN network, but we used the parameter '--format header' to retrieve the context in a matter that the compiler understands. This block is then copied to the comatrix.c file. An example of this is:
#define SENDER_KEY {255, 177, 78, 9, 60, 148, 201, 202, 201, 71, 22, 72, 180, 249, 135, 16} #define RECIPIENT_KEY {240, 145, 14, 215, 41, 94, 106, 212, 181, 79, 199, 147, 21, 67, 2, 255} #define COMMON_IV {70, 34, 212, 221, 109, 148, 65, 104, 238, 251, 84, 152, 124} #define SENDER_ID {1} #define RECIPIENT_ID {}
The security context then has to be created
static struct oscore_context_primitive_immutables immutables_d = { .common_iv = COMMON_IV,
.recipient_id_len = 0, .recipient_key = RECIPIENT_KEY,
.sender_id_len = 1, .sender_id = "\x01", .sender_key = SENDER_KEY, }; static struct oscore_context_primitive primitive_d = { .immutables = &immutables_d }; oscore_context_t secctx_d = { .type = OSCORE_CONTEXT_PRIMITIVE, .data = (void*)(&primitive_d), }; int16_t secctx_d_change = 0;
Used Hardware
- Raspberry Pi 3B+ Model (CoMatrix Gateway)
- OpenLabs 802.15.4. radio module
- SAMR21-xpro (CoMatrix Client)
- (optional) Raspberry Pi 4 (Matrix Homeserver)