From d66fc0c3b7eebf3080be0be125b2a451f1fad22b Mon Sep 17 00:00:00 2001 From: Michal Podhradsky Date: Thu, 18 Jan 2018 15:09:17 -0800 Subject: [PATCH] galois embedded crypto module and secure transport (#2205) * Added RNG for NPS, cleaned up telemetry config files * Added Rust submodules * Added simple status message * Updated gec transport and key exchange * Update pprzink * Compilation fix for Travis --- .gitmodules | 6 + .travis.yml | 2 +- Makefile.ac | 14 +- .../AGGIEAIR/aggieair_ark_hexa_1-8.xml | 6 +- conf/airframes/AGGIEAIR/aggieair_conf.xml | 2 +- .../AGGIEAIR/aggieair_control_panel.xml | 10 + conf/modules/haclc.xml | 8 +- conf/modules/rng.xml | 2 +- conf/modules/telemetry_nps_secure.xml | 43 ++ conf/modules/telemetry_secure_common.xml | 46 +++ conf/modules/telemetry_transparent_gec.xml | 27 ++ .../AGGIEAIR/aggieair_rotorcraft.xml | 2 + sw/airborne/arch/sim/mcu_periph/rng_arch.c | 66 ++++ sw/airborne/modules/datalink/gec/gec.c | 127 ++++++ sw/airborne/modules/datalink/gec/gec.h | 130 ++++++ sw/airborne/modules/datalink/gec_dl.c | 372 ++++++++++++++++++ sw/airborne/modules/datalink/gec_dl.h | 82 ++++ sw/airborne/modules/datalink/spprz_dl.c | 40 -- sw/airborne/modules/datalink/spprz_dl.h | 49 --- sw/airborne/subsystems/datalink/datalink.h | 2 +- sw/ext/Makefile | 4 +- sw/ext/key_generator | 1 + sw/ext/pprzlink | 2 +- sw/ext/rustlink | 1 + 24 files changed, 943 insertions(+), 101 deletions(-) create mode 100644 conf/modules/telemetry_nps_secure.xml create mode 100644 conf/modules/telemetry_secure_common.xml create mode 100644 conf/modules/telemetry_transparent_gec.xml create mode 100644 sw/airborne/arch/sim/mcu_periph/rng_arch.c create mode 100644 sw/airborne/modules/datalink/gec/gec.c create mode 100644 sw/airborne/modules/datalink/gec/gec.h create mode 100644 sw/airborne/modules/datalink/gec_dl.c create mode 100644 sw/airborne/modules/datalink/gec_dl.h delete mode 100644 sw/airborne/modules/datalink/spprz_dl.c delete mode 100644 sw/airborne/modules/datalink/spprz_dl.h create mode 160000 sw/ext/key_generator create mode 160000 sw/ext/rustlink diff --git a/.gitmodules b/.gitmodules index 8182da7cd3..98ab8ff31c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,9 @@ [submodule "sw/ext/hacl-c"] path = sw/ext/hacl-c url = https://github.com/paparazzi/hacl-c.git +[submodule "sw/ext/rustlink"] + path = sw/ext/rustlink + url = https://github.com/paparazzi/rustlink.git +[submodule "sw/ext/key_generator"] + path = sw/ext/key_generator + url = https://github.com/paparazzi/key_generator.git diff --git a/.travis.yml b/.travis.yml index 38bd1e02ab..83114b5a7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_install: - sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa -y - sudo apt-get update -q install: - - sudo apt-get install -y --force-yes paparazzi-dev paparazzi-jsbsim gcc-arm-embedded gcc-arm-linux-gnueabi libipc-run-perl cmake + - sudo apt-get install -y --force-yes paparazzi-dev paparazzi-jsbsim gcc-arm-embedded gcc-arm-linux-gnueabi libipc-run-perl rustc cargo script: - arm-none-eabi-gcc --version - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then make test_all_confs; else make test; fi; fi diff --git a/Makefile.ac b/Makefile.ac index 053d8471e6..44e593dc88 100644 --- a/Makefile.ac +++ b/Makefile.ac @@ -70,6 +70,7 @@ MODULES_H=$(AC_GENERATED)/modules.h MODULES_DIR=$(PAPARAZZI_HOME)/conf/modules/ AUTOPILOT_DIR=$(AC_GENERATED)/ AIRCRAFT_MD5=$(AIRCRAFT_CONF_DIR)/aircraft.md5 +GENERATE_KEYS ?= 0 UNAME = $(shell uname -s) ifeq ("$(UNAME)","Darwin") @@ -176,6 +177,17 @@ else $(Q)cd $(PAPARAZZI_SRC) ; ./sw/tools/find_vpaths.py $(CXX) $(TMP_LIST) $(PAPARAZZI_SRC) >> $(SRCS_LIST) endif +CARGO=$(shell which cargo) +generate_keys: +ifeq ($(GENERATE_KEYS),1) +ifneq ($(CARGO),) + @echo GENERATE KEYS + $(Q)cargo run --manifest-path $(PAPARAZZI_SRC)/sw/ext/key_generator/Cargo.toml --release $(AC_GENERATED) +else + @echo "Error: Cargo (Rust) is not found, keys are not generated. Please install cargo." +endif +endif + qt_project : $(SRCS_LIST) ifneq ($(PAPARAZZI_QT_GEN),) $(Q)./sw/tools/qt_project.py $(AIRCRAFT) $(CONF_XML) $(SRCS_LIST) @@ -187,7 +199,7 @@ flight_plan_ac_h : $(FLIGHT_PLAN_H) $(FLIGHT_PLAN_XML) makefile_ac: $(MAKEFILE_AC) -$(AIRFRAME_H) : $(CONF)/$(AIRFRAME_XML) $(CONF_XML) $(AIRCRAFT_MD5) +$(AIRFRAME_H) : $(CONF)/$(AIRFRAME_XML) $(CONF_XML) $(AIRCRAFT_MD5) generate_keys $(Q)test -d $(AC_GENERATED) || mkdir -p $(AC_GENERATED) @echo GENERATE $@ from $(AIRFRAME_XML) $(eval $@_TMP := $(shell $(MKTEMP))) diff --git a/conf/airframes/AGGIEAIR/aggieair_ark_hexa_1-8.xml b/conf/airframes/AGGIEAIR/aggieair_ark_hexa_1-8.xml index efb1fb1b7c..57c262ffe2 100644 --- a/conf/airframes/AGGIEAIR/aggieair_ark_hexa_1-8.xml +++ b/conf/airframes/AGGIEAIR/aggieair_ark_hexa_1-8.xml @@ -26,6 +26,7 @@ Aggie Air ARK + - + @@ -56,7 +58,7 @@ Aggie Air ARK - + diff --git a/conf/airframes/AGGIEAIR/aggieair_conf.xml b/conf/airframes/AGGIEAIR/aggieair_conf.xml index fde5881372..99c751e739 100644 --- a/conf/airframes/AGGIEAIR/aggieair_conf.xml +++ b/conf/airframes/AGGIEAIR/aggieair_conf.xml @@ -7,7 +7,7 @@ telemetry="telemetry/AGGIEAIR/aggieair_rotorcraft.xml" flight_plan="flight_plans/rotorcraft_basic_geofence.xml" settings="settings/rotorcraft_basic.xml settings/nps.xml" - settings_modules="modules/battery_monitor.xml modules/gps.xml modules/stabilization_float_euler.xml modules/nav_basic_rotorcraft.xml modules/guidance_rotorcraft.xml modules/imu_common.xml" + settings_modules="modules/battery_monitor.xml modules/gps.xml modules/stabilization_float_euler.xml modules/nav_basic_rotorcraft.xml modules/guidance_rotorcraft.xml" gui_color="#ffff954c0000" /> + + + + + + + + + + diff --git a/conf/modules/haclc.xml b/conf/modules/haclc.xml index 4fd448651c..52287cd5c6 100644 --- a/conf/modules/haclc.xml +++ b/conf/modules/haclc.xml @@ -7,18 +7,20 @@ see https://github.com/mitls/hacl-star for details - + + + - # to not use 128 bit arithmetic - ap.CFLAGS += -DKRML_NOUINT128 + # tell Makefile.ac to generate keys + GENERATE_KEYS = 1 diff --git a/conf/modules/rng.xml b/conf/modules/rng.xml index e4a213aeff..0a29142924 100644 --- a/conf/modules/rng.xml +++ b/conf/modules/rng.xml @@ -9,7 +9,7 @@
- + diff --git a/conf/modules/telemetry_nps_secure.xml b/conf/modules/telemetry_nps_secure.xml new file mode 100644 index 0000000000..00e3e1c72a --- /dev/null +++ b/conf/modules/telemetry_nps_secure.xml @@ -0,0 +1,43 @@ + + + + + + Telemetry module for NPS simulation + Common parts of comms, not to be used as a stadalone module. Instead, this module + is autoloaded where appropriate. + + + + + + + + + + + + + + + + + + + + + + include $(CFG_SHARED)/udp.makefile + + + + + + + + + + + + + diff --git a/conf/modules/telemetry_secure_common.xml b/conf/modules/telemetry_secure_common.xml new file mode 100644 index 0000000000..64f8f65a02 --- /dev/null +++ b/conf/modules/telemetry_secure_common.xml @@ -0,0 +1,46 @@ + + + + + + Telemetry using secure PPRZ protocol - common parts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/modules/telemetry_transparent_gec.xml b/conf/modules/telemetry_transparent_gec.xml new file mode 100644 index 0000000000..a278d40052 --- /dev/null +++ b/conf/modules/telemetry_transparent_gec.xml @@ -0,0 +1,27 @@ + + + + + + Galois Embedded Crypto over transparent datalink + + + + + + + +
+ +
+ + + + + + + + + +
+ diff --git a/conf/telemetry/AGGIEAIR/aggieair_rotorcraft.xml b/conf/telemetry/AGGIEAIR/aggieair_rotorcraft.xml index 633107ed63..b7037d2906 100644 --- a/conf/telemetry/AGGIEAIR/aggieair_rotorcraft.xml +++ b/conf/telemetry/AGGIEAIR/aggieair_rotorcraft.xml @@ -33,6 +33,7 @@ + @@ -174,6 +175,7 @@ + diff --git a/sw/airborne/arch/sim/mcu_periph/rng_arch.c b/sw/airborne/arch/sim/mcu_periph/rng_arch.c new file mode 100644 index 0000000000..abae42395f --- /dev/null +++ b/sw/airborne/arch/sim/mcu_periph/rng_arch.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 Michal Podhradsky + * + * This file is part of Paparazzi. + * + * Paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * Paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Paparazzi; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/** \file rng_arch.c + * \brief arch specific Random Number Generator API + * + */ +#include "mcu_periph/rng.h" + +#include +#include +#include +#include + +void rng_init(void) {} + +void rng_deinit(void) {} + +// Return true only if we got a new number +// that is different from the previous one +bool rng_get(uint32_t *rand_nr) +{ + int fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) { + printf("Cannot open /dev/urandom\n"); + return false; + } + + uint8_t len = sizeof(uint32_t); + uint8_t res = read(fd, rand_nr, len); + if (res != len) { + printf("Error on reading, expected %u bytes, got %u bytes\n", + len, res); + return false; + } + close(fd); + return true; +} + +// Wait until we get a new number that is different +// from the previous one. We can wait forever here if +// the clocks are not setup properly. +uint32_t rng_wait_and_get(void) { + uint32_t tmp = 0; + while (!rng_get(&tmp)) {}; + return tmp; +} diff --git a/sw/airborne/modules/datalink/gec/gec.c b/sw/airborne/modules/datalink/gec/gec.c new file mode 100644 index 0000000000..894a840d40 --- /dev/null +++ b/sw/airborne/modules/datalink/gec/gec.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 Michal Podhradsky + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file datalink/gec/gec.c + * + * Galois embedded crypto iplementation + * + */ +#include "modules/datalink/gec/gec.h" + +void gec_sts_init(struct gec_sts_ctx * sts) +{ + clear_sts(sts); + + uint8_t theirPublicKey[PPRZ_KEY_LEN] = GCS_PUBLIC; + memcpy(&sts->theirPublicKey, theirPublicKey, PPRZ_KEY_LEN); + + uint8_t myPublicKey[PPRZ_KEY_LEN] = UAV_PUBLIC; + memcpy(&sts->myPrivateKey.pub, myPublicKey, PPRZ_KEY_LEN); + + uint8_t myPrivateKey[PPRZ_KEY_LEN] = UAV_PRIVATE; + memcpy(&sts->myPrivateKey.priv, myPrivateKey, PPRZ_KEY_LEN); + +} + +void clear_sts(struct gec_sts_ctx * sts) +{ + memset(&sts->theirPublicKeyEphemeral, 0, sizeof(struct gec_pubkey)); + memset(&sts->myPrivateKeyEphemeral, 0, sizeof(struct gec_privkey)); + memset(&sts->theirSymmetricKey, 0, sizeof(struct gec_sym_key)); + memset(&sts->mySymmetricKey, 0, sizeof(struct gec_sym_key)); + sts->protocol_stage = WAIT_MSG1; + sts->party = RESPONDER; + sts->last_error = ERROR_NONE; +} + +/** + * Generate private and public key pairs for future use. + */ +void generate_ephemeral_keys(struct gec_privkey *sk) +{ + for (uint16_t i = 0; i < (PPRZ_KEY_LEN / sizeof(uint32_t)); i++) { + uint32_t tmp = rng_wait_and_get(); + sk->priv[i] = (uint8_t) tmp; + sk->priv[i + 1] = (uint8_t) (tmp >> 8); + sk->priv[i + 2] = (uint8_t) (tmp >> 16); + sk->priv[i + 3] = (uint8_t) (tmp >> 24); + } + uint8_t basepoint[32] = {0}; + basepoint[0] = 9; // default basepoint + Hacl_Curve25519_crypto_scalarmult(sk->pub, sk->priv, basepoint); +} + + +/** + * Derive key material for both sender and receiver + */ +void derive_key_material(struct gec_sts_ctx *ctx, uint8_t* z) { + uint8_t tmp[PPRZ_KEY_LEN*2] = {0}; + uint8_t input[PPRZ_KEY_LEN+1] = {0}; + + // Ka|| Sa = kdf(z,0) + memcpy(input, z, PPRZ_KEY_LEN); + input[PPRZ_KEY_LEN] = 0; + Hacl_SHA2_512_hash(tmp, input, sizeof(input)); + memcpy(ctx->theirSymmetricKey.key, tmp, PPRZ_KEY_LEN); // K_a + memcpy(ctx->theirSymmetricKey.nonce, &tmp[PPRZ_KEY_LEN], PPRZ_NONCE_LEN); // S_a + + // Kb|| Sb = kdf(z,1) + input[PPRZ_KEY_LEN] = 1; + Hacl_SHA2_512_hash(tmp, input, sizeof(input)); + memcpy(ctx->mySymmetricKey.key, tmp, PPRZ_KEY_LEN); // K_b + memcpy(ctx->mySymmetricKey.nonce, &tmp[PPRZ_KEY_LEN], PPRZ_NONCE_LEN); // S_b +} + + +/** + * Decrypt a message, no AAD for now + * if res == 0 everything OK + */ +uint32_t gec_decrypt(struct gec_sym_key *k, uint8_t *plaintext, uint8_t *ciphertext, uint8_t len, uint8_t *mac){ + uint32_t res = Hacl_Chacha20Poly1305_aead_decrypt(plaintext, + ciphertext, + len, + mac, + NULL, + 0, + k->key, + k->nonce); + return res; +} + +/** + * Encrypt a message, no AAD for now + * if res == 0 everything OK + */ +uint32_t gec_encrypt(struct gec_sym_key *k, uint8_t *ciphertext, uint8_t *plaintext, uint8_t len, uint8_t *mac) { + uint32_t res = Hacl_Chacha20Poly1305_aead_encrypt(ciphertext, // ciphertext + mac, // mac + plaintext, // plaintext + len, // plaintext len + NULL, // aad + 0, // aad len + k->key, // key + k->nonce); // nonce + return res; +} + + diff --git a/sw/airborne/modules/datalink/gec/gec.h b/sw/airborne/modules/datalink/gec/gec.h new file mode 100644 index 0000000000..a4b7d7cf08 --- /dev/null +++ b/sw/airborne/modules/datalink/gec/gec.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2017 Michal Podhradsky + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file datalink/gec/gec.h + * + * Galois embedded crypto iplementation + * + */ +#ifndef SPPRZ_GEC_H +#define SPPRZ_GEC_H + +#include "std.h" +#include "mcu_periph/rng.h" +#include "generated/keys_uav.h" +#include "../ext/hacl-c/Hacl_Ed25519.h" +#include "../ext/hacl-c/Hacl_Curve25519.h" +#include "../ext/hacl-c/Hacl_SHA2_512.h" +#include "../ext/hacl-c/Hacl_Chacha20Poly1305.h" + + +#define PPRZ_SIGN_LEN 64 +#define PPRZ_KEY_LEN 32 +#define PPRZ_NONCE_LEN 12 +#define PPRZ_MAC_LEN 16 + +typedef unsigned char ed25519_signature[64]; + +struct gec_privkey +{ + uint8_t priv[PPRZ_KEY_LEN]; + uint8_t pub[PPRZ_KEY_LEN]; +}; + +struct gec_pubkey +{ + uint8_t pub[PPRZ_KEY_LEN]; +}; + +struct gec_sym_key +{ + uint8_t key[PPRZ_KEY_LEN]; + uint8_t nonce[PPRZ_NONCE_LEN]; + uint32_t ctr; +}; + +typedef enum +{ + INIT, WAIT_MSG1, WAIT_MSG2, WAIT_MSG3, CRYPTO_OK, +} stage_t; + +typedef enum +{ + INITIATOR, RESPONDER, CLIENT, INVALID_PARTY +} party_t; + +typedef enum +{ + P_AE, P_BE, SIG, +} gec_sts_msg_type_t; + +typedef enum +{ + ERROR_NONE, + // RESPONDER ERRORS + MSG1_TIMEOUT_ERROR, + MSG1_ENCRYPT_ERROR, + MSG3_TIMEOUT_ERROR, + MSG3_DECRYPT_ERROR, + MSG3_SIGNVERIFY_ERROR, + // INITIATOR ERRORS + MSG2_TIMEOUT_ERROR, + MSG2_DECRYPT_ERROR, + MSG2_SIGNVERIFY_ERROR, + MSG3_ENCRYPT_ERROR, + // BOTH PARTIES + UNEXPECTED_MSG_TYPE_ERROR, + UNEXPECTED_STS_STAGE_ERROR, + UNEXPECTED_MSG_ERROR +} sts_error_t; + +// Intermediate data structure containing information relating to the stage of +// the STS protocol. +struct gec_sts_ctx +{ + struct gec_pubkey theirPublicKey; + struct gec_privkey myPrivateKey; + struct gec_pubkey theirPublicKeyEphemeral; + struct gec_privkey myPrivateKeyEphemeral; + struct gec_sym_key theirSymmetricKey; + struct gec_sym_key mySymmetricKey; + stage_t protocol_stage; + party_t party; + sts_error_t last_error; + uint32_t counter_err; + uint32_t encrypt_err; + uint32_t decrypt_err; +}; + +void gec_sts_init(struct gec_sts_ctx * sts); + +void clear_sts(struct gec_sts_ctx * sts); + +void generate_ephemeral_keys(struct gec_privkey *sk); + +void derive_key_material(struct gec_sts_ctx *sts, uint8_t* z); + +uint32_t gec_encrypt(struct gec_sym_key *k, uint8_t *ciphertext, + uint8_t *plaintext, uint8_t len, uint8_t *mac); +uint32_t gec_decrypt(struct gec_sym_key *k, uint8_t *plaintext, + uint8_t *ciphertext, uint8_t len, uint8_t *mac); + +#endif /* SPPRZ_GEC_H */ diff --git a/sw/airborne/modules/datalink/gec_dl.c b/sw/airborne/modules/datalink/gec_dl.c new file mode 100644 index 0000000000..5c63c61618 --- /dev/null +++ b/sw/airborne/modules/datalink/gec_dl.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2017 Michal Podhradsky + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, see + * . + * + */ + +/** \file modules/datalink/spprz_dl.c + * \brief Datalink using Galois Embedded Crypto + */ + +#include "gec_dl.h" +#include "modules/datalink/gec/gec.h" +#include "subsystems/datalink/datalink.h" +#include "pprzlink/messages.h" +#include // for memset() + +#if PPRZLINK_DEFAULT_VER != 2 +#error "Secure link is only for Pprzlink v 2.0" +#endif + +struct gec_transport gec_tp; + +#if PERIODIC_TELEMETRY +#include "subsystems/datalink/telemetry.h" + +static void send_secure_link_info(struct transport_tx *trans, struct link_device *dev) +{ + pprz_msg_send_SECURE_LINK_STATUS(trans, dev, AC_ID, + (uint8_t*)&gec_tp.sts.protocol_stage, + (uint8_t*)&gec_tp.sts.last_error, + &gec_tp.sts.counter_err, + &gec_tp.sts.decrypt_err, + &gec_tp.sts.encrypt_err); +} + +#endif + + +static struct gec_transport * get_trans(struct pprzlink_msg *msg) +{ + return (struct gec_transport *)(msg->trans->impl); +} + +static inline void insert_byte(struct gec_transport *t, const uint8_t byte) { + t->tx_msg[t->tx_msg_idx] = byte; + t->tx_msg_idx++; +} + + +static void put_bytes(struct pprzlink_msg *msg, + long fd __attribute__((unused)), + enum TransportDataType type __attribute__((unused)), + enum TransportDataFormat format __attribute__((unused)), + const void *bytes, uint16_t len) +{ + const uint8_t *b = (const uint8_t *) bytes; + int i; + for (i = 0; i < len; i++) { + insert_byte(get_trans(msg), b[i]); + } +} + + +static void put_named_byte(struct pprzlink_msg *msg, + long fd __attribute__((unused)), + enum TransportDataType type __attribute__((unused)), + enum TransportDataFormat format __attribute__((unused)), + uint8_t byte, const char *name __attribute__((unused))) +{ + insert_byte(get_trans(msg), byte); +} + + +/** + * TODO + */ +static uint8_t size_of(struct pprzlink_msg *msg, uint8_t len) +{ + // message length: payload + crypto overhead + return len + get_trans(msg)->pprz_tp.trans_tx.size_of(msg, len); + // + ???; TODO depends on crypto header size +} + + +/** + * TODO + */ +static void start_message(struct pprzlink_msg *msg, + long fd __attribute__((unused)), + uint8_t payload_len) +{ + PPRZ_MUTEX_LOCK(get_trans(msg)->mtx_tx); // lock mutex + memset(get_trans(msg)->tx_msg, _FD, TRANSPORT_PAYLOAD_LEN); // erase message data + get_trans(msg)->tx_msg_idx = 0; // reset index + // TODO add crypto header to buffer if needed +} + +/** + * TODO + */ +static void end_message(struct pprzlink_msg *msg, long fd) +{ + // TODO apply crypto on buffer here, or a part of it + + // if valid + // encapsulated encrypted data with pprz + get_trans(msg)->pprz_tp.trans_tx.start_message(msg, fd, get_trans(msg)->tx_msg_idx); + for (int i = 0; i < get_trans(msg)->tx_msg_idx; i++) { + // add byte one by one for now + get_trans(msg)->pprz_tp.trans_tx.put_bytes(msg, _FD, DL_TYPE_UINT8, DL_FORMAT_SCALAR, + &(get_trans(msg)->tx_msg[i]), 1); + // TODO: replace with one put_bytes() call + } + get_trans(msg)->pprz_tp.trans_tx.end_message(msg, fd); + + // unlock mutex + PPRZ_MUTEX_UNLOCK(get_trans(msg)->mtx_tx); +} + + +/** + * TODO + */ +static void overrun(struct pprzlink_msg *msg) +{ + get_trans(msg)->pprz_tp.trans_tx.overrun(msg); +} + +static void count_bytes(struct pprzlink_msg *msg, uint8_t bytes) +{ + get_trans(msg)->pprz_tp.trans_tx.count_bytes(msg, bytes); +} + +static int check_available_space(struct pprzlink_msg *msg, long *fd, uint16_t bytes) +{ + return get_trans(msg)->pprz_tp.trans_tx.check_available_space(msg, fd, bytes); +} + + +// Init pprz transport structure +static void gec_transport_init(struct gec_transport *t) +{ + t->trans_rx.msg_received = false; + t->trans_tx.size_of = (size_of_t) size_of; + t->trans_tx.check_available_space = (check_available_space_t) check_available_space; + t->trans_tx.put_bytes = (put_bytes_t) put_bytes; + t->trans_tx.put_named_byte = (put_named_byte_t) put_named_byte; + t->trans_tx.start_message = (start_message_t) start_message; + t->trans_tx.end_message = (end_message_t) end_message; + t->trans_tx.overrun = (overrun_t) overrun; + t->trans_tx.count_bytes = (count_bytes_t) count_bytes; + t->trans_tx.impl = (void *)(t); + PPRZ_MUTEX_INIT(t->mtx_tx); // init mutex, check if correct pointer +} + + + + + + +void gec_process_sts_msg(struct link_device *dev, struct gec_transport *trans, uint8_t *buf) +{ + uint8_t sender_id = SenderIdOfPprzMsg(buf); + uint8_t msg_id = IdOfPprzMsg(buf); + + if (sender_id != 0) { + // process only messages from GCS + // log an error + return; + } + + uint8_t msg_type = DL_KEY_EXCHANGE_GCS_msg_type(buf); + + switch (msg_id) { + case DL_KEY_EXCHANGE_GCS: + switch (trans->sts.protocol_stage) { + case WAIT_MSG1: + if (msg_type == P_AE) { + respond_sts(dev, trans, buf); + } else { + // log an error + trans->sts.last_error = UNEXPECTED_MSG_TYPE_ERROR; + } + break; + case WAIT_MSG3: + if (msg_type == SIG) { + finish_sts(dev, trans, buf); + } else { + // log an error + trans->sts.last_error = UNEXPECTED_MSG_TYPE_ERROR; + } + break; + default: + // INIT, WAIT_MSG2, CRYPTO_OK + // do nothing or log an error + trans->sts.last_error = UNEXPECTED_STS_STAGE_ERROR; + break; + } + break; + default: + // process only KEY_EXCHANGE for now + // log an error + trans->sts.last_error = UNEXPECTED_MSG_ERROR; + break; + } +} + + +void gec_dl_init(void) +{ + // init pprz transport + pprz_transport_init(&gec_tp.pprz_tp); + + // init crypto transport + gec_transport_init(&gec_tp); + + // initialize keys + gec_sts_init(&gec_tp.sts); + +#if PERIODIC_TELEMETRY + register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_SECURE_LINK_STATUS, send_secure_link_info); +#endif +} + + +void gec_dl_event(void) +{ + pprz_check_and_parse(&DOWNLINK_DEVICE.device, &gec_tp.pprz_tp, gec_tp.trans_rx.payload, + (bool*) &gec_tp.trans_rx.msg_received); + if (gec_tp.trans_rx.msg_received) { + if (gec_tp.sts.protocol_stage != CRYPTO_OK) { + // message is in plaintext, treat is as such + // TODO: check for decryption + // don't pass through + gec_process_sts_msg(&DOWNLINK_DEVICE.device, &gec_tp, gec_tp.trans_rx.payload); + + } else { + // attempt decryption, pass data if all good + // process decrypted message if needed + // store in dl_buffer (see macro in datalink.h) + DatalinkFillDlBuffer(gec_tp.trans_rx.payload, TRANSPORT_PAYLOAD_LEN); + + // pass to datalink + DlCheckAndParse(&DOWNLINK_DEVICE.device, &gec_tp.trans_tx, dl_buffer, &dl_msg_available); + } + // reset flag + gec_tp.trans_rx.msg_received = false; + } +} + + +/** + * message1 = [ Pae (32 bytes) ] + * 2. B generates ephemeral curve25519 key pair (Pbe, Qbe). + * 3. B computes the shared secret: z = scalar_multiplication(Qbe, Pae) + * 4. B uses the key derivation function kdf(z,1) to compute Kb || Sb, + * kdf(z,0) to compute Ka || Sa, and kdf(z,2) to compute Kclient || Sclient. + * 5. B computes the ed25519 signature: sig = signQb(Pbe || Pae) + * 6. B computes and sends the message Pbe || Ekey=Kb,IV=Sb||zero(sig) + * + * Sends: message2 = [ Pbe (32 bytes) | Encrypted Signature (64 bytes) ] + MAC (16bytes) + */ +void respond_sts(struct link_device *dev, struct gec_transport *trans, uint8_t *buf) { + // TODO: check message length + + // copy P_ae over + memcpy(trans->sts.theirPublicKeyEphemeral.pub, DL_KEY_EXCHANGE_GCS_msg_data(buf), sizeof(struct gec_pubkey)); + + // 2. B generates ephemeral curve25519 key pair (Pbe, Qbe). + generate_ephemeral_keys(&trans->sts.myPrivateKeyEphemeral); + + // 3. B computes the shared secret: z = scalar_multiplication(Qbe, Pae) + uint8_t z[32] = {0}; + Hacl_Curve25519_crypto_scalarmult(z, trans->sts.myPrivateKeyEphemeral.priv, trans->sts.theirPublicKeyEphemeral.pub); + + // 4. B uses the key derivation function kdf(z,1) to compute Kb || Sb, + // kdf(z,0) to compute Ka || Sa, and kdf(z,2) to compute Kclient || Sclient. + derive_key_material(&trans->sts, z); + + // 5. B computes the ed25519 signature: sig = signQb(Pbe || Pae) + uint8_t sig[PPRZ_SIGN_LEN] = {0}; + uint8_t pbe_concat_p_ae[PPRZ_KEY_LEN*2] = {0}; + memcpy(pbe_concat_p_ae, trans->sts.myPrivateKeyEphemeral.pub, PPRZ_KEY_LEN); + memcpy(&pbe_concat_p_ae[PPRZ_KEY_LEN], trans->sts.theirPublicKeyEphemeral.pub, PPRZ_KEY_LEN); + Hacl_Ed25519_sign(sig, trans->sts.myPrivateKey.priv, pbe_concat_p_ae, PPRZ_KEY_LEN*2); + + // 6. B computes and sends the message Pbe || Ekey=Kb,IV=Sb||zero(sig) + uint8_t msg_data[PPRZ_KEY_LEN + PPRZ_SIGN_LEN + PPRZ_MAC_LEN] = {0}; + memcpy(msg_data, &trans->sts.myPrivateKeyEphemeral.pub, PPRZ_KEY_LEN); + if (gec_encrypt(&trans->sts.mySymmetricKey, &msg_data[PPRZ_KEY_LEN], sig, PPRZ_SIGN_LEN, &msg_data[PPRZ_KEY_LEN + PPRZ_SIGN_LEN])) { + // log error here and return + trans->sts.last_error = MSG1_ENCRYPT_ERROR; + return; + } + // all good, send message and increment status + uint8_t msg_type = P_BE; + uint8_t nb_msg_data = PPRZ_KEY_LEN + PPRZ_SIGN_LEN + PPRZ_MAC_LEN; + + + pprz_msg_send_KEY_EXCHANGE_UAV(&trans->trans_tx, dev, AC_ID, &msg_type, nb_msg_data, msg_data); + + // update protocol stage + trans->sts.protocol_stage = WAIT_MSG3; + // TODO: add timeout +} + + + +/** + * Finalize STS exchange + * + * message3 = [ Encrypted Signature (64 bytes) ] + MAC(16 bytes) = Ekey=Ka,IV=Sa||zero(sig) + * where sig = signQa(Pae || Pbe) + * 13. B decrypts the message and verifies the signature. + */ +void finish_sts(struct link_device *dev __attribute__((__unused__)), + struct gec_transport *trans, + uint8_t *buf){ + // TODO: check message length + + // decrypt + uint8_t sign[PPRZ_SIGN_LEN] = {0}; + if (gec_decrypt(&trans->sts.theirSymmetricKey, sign, DL_KEY_EXCHANGE_GCS_msg_data(buf), + PPRZ_SIGN_LEN, DL_KEY_EXCHANGE_GCS_msg_data(buf)+PPRZ_SIGN_LEN)) { + // log error and return + // MSG3 decrypt error + trans->sts.last_error = MSG3_DECRYPT_ERROR; + return; + } + + // verify + uint8_t pbe_concat_p_ae[PPRZ_KEY_LEN*2] = {0}; + memcpy(pbe_concat_p_ae, trans->sts.myPrivateKeyEphemeral.pub, PPRZ_KEY_LEN); + memcpy(&pbe_concat_p_ae[PPRZ_KEY_LEN], trans->sts.theirPublicKeyEphemeral.pub, PPRZ_KEY_LEN); + // returns true if verified properly + if (!Hacl_Ed25519_verify(trans->sts.theirPublicKey.pub, pbe_concat_p_ae, PPRZ_SIGN_LEN, sign)) { + // log error and return + // MSG3 sign verify error + trans->sts.last_error = MSG3_SIGNVERIFY_ERROR; + return; + } + + /* + // update the transport + // tx key => my symm key + memcpy(trans->tx_key, sts.mySymmetricKey.key, PPRZ_KEY_LEN); + // tx nonce (12 bytes, first 4 bytes will be overwritten by the counter) + memcpy(trans->tx_nonce, sts.mySymmetricKey.nonce, PPRZ_NONCE_LEN); + + // rx key => their symm key + memcpy(trans->rx_key, sts.theirSymmetricKey.key, PPRZ_KEY_LEN); + // rx nonce (12 bytes, first 4 bytes will be overwritten by the counter) + memcpy(trans->rx_nonce, sts.theirSymmetricKey.nonce, PPRZ_NONCE_LEN); +*/ + // if everything went OK, proceed to CRYPTO_OK status + trans->sts.protocol_stage = CRYPTO_OK; +} diff --git a/sw/airborne/modules/datalink/gec_dl.h b/sw/airborne/modules/datalink/gec_dl.h new file mode 100644 index 0000000000..48d5545992 --- /dev/null +++ b/sw/airborne/modules/datalink/gec_dl.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 Michal Podhradsky + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, see + * . + * + */ + +/** \file modules/datalink/gec_dl.h + * \brief Datalink using Galois Embedded Crypto + */ + +#ifndef GEC_DL_H +#define GEC_DL_H + +#include "pprzlink/pprzlink_transport.h" +#include "pprzlink/pprz_transport.h" +#include "modules/datalink/gec/gec.h" +#include "pprz_mutex.h" + +#include "mcu_periph/uart.h" +#if USE_USB_SERIAL +#include "mcu_periph/usb_serial.h" +#endif +#if USE_UDP +#include "mcu_periph/udp.h" +#endif + +struct gec_transport { + // pprz encapsulation layer + struct pprz_transport pprz_tp; + + // generic reception interface + struct transport_rx trans_rx; + + // generic transmission interface + struct transport_tx trans_tx; + // buffered tx message + uint8_t tx_msg[TRANSPORT_PAYLOAD_LEN]; + volatile uint8_t tx_msg_idx; + + // ecnryption primitives + struct gec_sts_ctx sts; + + PPRZ_MUTEX(mtx_tx); // optional mutex +}; + + +/** PPRZ transport structure */ +extern struct gec_transport gec_tp; + +/** Init function */ +extern void gec_dl_init(void); + +/** Datalink Event */ +extern void gec_dl_event(void); + +/** Parsing a frame data and copy the payload to the datalink buffer */ +void gec_check_and_parse(struct link_device *dev, struct gec_transport *trans, + uint8_t *buf, bool *msg_available); + +void gec_process_sts_msg(struct link_device *dev, struct gec_transport *trans, uint8_t *buf); + +void respond_sts(struct link_device *dev, struct gec_transport *trans, uint8_t *buf); + +void finish_sts(struct link_device *dev, struct gec_transport *trans, uint8_t *buf); + +#endif /* GEC_DL_H */ + diff --git a/sw/airborne/modules/datalink/spprz_dl.c b/sw/airborne/modules/datalink/spprz_dl.c deleted file mode 100644 index 21c8848c74..0000000000 --- a/sw/airborne/modules/datalink/spprz_dl.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 Michal Podhradsky - * - * This file is part of paparazzi. - * - * paparazzi is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * paparazzi is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with paparazzi; see the file COPYING. If not, see - * . - * - */ - -/** \file modules/datalink/spprz_dl.c - * \brief Datalink using secure PPRZ protocol - */ - -#include "modules/datalink/spprz_dl.h" -#include "subsystems/datalink/datalink.h" - -struct spprz_transport spprz_tp; - -void spprz_dl_init(void) -{ - spprz_transport_init(&spprz_tp); -} - -void spprz_dl_event(void) -{ - spprz_check_and_parse(&DOWNLINK_DEVICE.device, &spprz_tp, dl_buffer, &dl_msg_available); - DlCheckAndParse(&DOWNLINK_DEVICE.device, &spprz_tp.trans_tx, dl_buffer, &dl_msg_available); -} diff --git a/sw/airborne/modules/datalink/spprz_dl.h b/sw/airborne/modules/datalink/spprz_dl.h deleted file mode 100644 index 017fd19f09..0000000000 --- a/sw/airborne/modules/datalink/spprz_dl.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2017 Michal Podhradsky - * - * This file is part of paparazzi. - * - * paparazzi is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * paparazzi is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with paparazzi; see the file COPYING. If not, see - * . - * - */ - -/** \file modules/datalink/spprz_dl.h - * \brief Datalink using secure PPRZ protocol - */ - -#ifndef SPPRZ_DL_H -#define SPPRZ_DL_H - -#include "pprzlink/secure_pprz_transport.h" - -#include "mcu_periph/uart.h" -#if USE_USB_SERIAL -#include "mcu_periph/usb_serial.h" -#endif -#if USE_UDP -#include "mcu_periph/udp.h" -#endif - -/** PPRZ transport structure */ -extern struct spprz_transport spprz_tp; - -/** Init function */ -extern void spprz_dl_init(void); - -/** Datalink Event */ -extern void spprz_dl_event(void); - -#endif /* SPPRZ_DL_H */ - diff --git a/sw/airborne/subsystems/datalink/datalink.h b/sw/airborne/subsystems/datalink/datalink.h index 35323d96b6..48c1eddcd0 100644 --- a/sw/airborne/subsystems/datalink/datalink.h +++ b/sw/airborne/subsystems/datalink/datalink.h @@ -67,7 +67,7 @@ EXTERN bool datalink_enabled; /** Convenience macro to fill dl_buffer */ #define DatalinkFillDlBuffer(_buf, _len) { \ - uint8_t _i = 0; \ + uint16_t _i = 0; \ for (_i = 0; _i < _len; _i++) { \ dl_buffer[_i] = _buf[_i]; \ } \ diff --git a/sw/ext/Makefile b/sw/ext/Makefile index d26011e2f7..811d23eef6 100644 --- a/sw/ext/Makefile +++ b/sw/ext/Makefile @@ -33,7 +33,7 @@ EXT_DIR=$(PAPARAZZI_SRC)/sw/ext include $(PAPARAZZI_SRC)/conf/Makefile.arm-embedded-toolchain -all: libopencm3 luftboot chibios fatfs libsbp mavlink TRICAL hacl-c +all: libopencm3 luftboot chibios fatfs libsbp mavlink TRICAL hacl-c key_generator # update (and init if needed) all submodules update_submodules: @@ -67,6 +67,8 @@ luftboot_flash: luftboot.build hacl-c: hacl-c.update +key_generator: key_generator.update + chibios: chibios.update TRICAL: TRICAL.update diff --git a/sw/ext/key_generator b/sw/ext/key_generator new file mode 160000 index 0000000000..fa85320305 --- /dev/null +++ b/sw/ext/key_generator @@ -0,0 +1 @@ +Subproject commit fa8532030517b143a7ee241b8c5e5d0a5ab350b6 diff --git a/sw/ext/pprzlink b/sw/ext/pprzlink index a41a07156f..e4a4bf3d5c 160000 --- a/sw/ext/pprzlink +++ b/sw/ext/pprzlink @@ -1 +1 @@ -Subproject commit a41a07156f4d253936759bb5863023e9ff5a844f +Subproject commit e4a4bf3d5c10d848d541a963e9ac103e17541ca1 diff --git a/sw/ext/rustlink b/sw/ext/rustlink new file mode 160000 index 0000000000..1e70ae72d4 --- /dev/null +++ b/sw/ext/rustlink @@ -0,0 +1 @@ +Subproject commit 1e70ae72d456feee980aa1db04dc5728e225b0e2