Skip to content

Commit

Permalink
protocol: adapt new SPAO header format (netsec-ethz#41)
Browse files Browse the repository at this point in the history
Update the SPAO header format to the new version. 
This includes the timestamp now being relative to the DRKey epoch. The
DRKey to be used by the receiver of the packet is now identified by this
timestamp.

Resolves netsec-ethz#30.

---------

Co-authored-by: abojarski <abojarski@student.ethz.ch>
  • Loading branch information
aaronbojarski and abojarski authored Feb 28, 2024
1 parent be1bbea commit b7a99d4
Show file tree
Hide file tree
Showing 19 changed files with 617 additions and 192 deletions.
3 changes: 1 addition & 2 deletions docs/IP LF.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,8 @@ The LF header has the following format:
8-bit protocol number of the payload following the LF header. The protocol number corresponds to the protocol number in the IP header before the packet has been encapsulated.

**Timestamp**
64-bit unsigned Unix timestamp in nanoseconds.
64-bit unsigned timestamp in nanoseconds. The timestamp provides a relative offset to the start of the DRKey epoch in nanoseconds.
The timestamp must be unique for packets from the same source AS.
> The timestamp format might change in the future.

**Payload Hash**
20-byte SHA-1 hash of the payload.
Expand Down
9 changes: 3 additions & 6 deletions docs/SCION LF.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This chapter presents, in more detail, how LightningFilter uses the SCION DRKey

## SCION DRKey Infrastructure

The SCION DRKey infrastructure documentation can be found [here](https://scion.docs.anapaya.net/en/latest/cryptography/drkey.html).
The SCION DRKey infrastructure documentation can be found [here](https://docs.scion.org/en/latest/cryptography/drkey.html).

LightingFilter keeps a table of Level 1 DRKeys for communicating with peers defined in the configuration.
The SCION DRKey infrastructure provides the Level 1 DRKeys to LightningFilter.
Expand All @@ -22,7 +22,7 @@ $K_{A:HA->B:HB} = PRF_{K_{A->B}}(HA||HB)$
## SCION Packet Authenticator Option (SPAO)

The SPAO header documentation can be found [here](https://scion.docs.anapaya.net/en/latest/protocols/authenticator-option.html).
The SPAO header documentation can be found [here](https://docs.scion.org/en/latest/protocols/authenticator-option.html).

```
0 1 2 3
Expand All @@ -42,7 +42,4 @@ The SPAO header documentation can be found [here](https://scion.docs.anapaya.net
```

LightningFilter utilizes the SPAO header as documented with the SHA1-AES-CBS algorithm and DRKey format of the security parameter index.

> The timestamp in the SPAO header provides a relative offset to the timestamp in the SCION path in nanoseconds. Therefore, the SPAO header is not defined for packets which use the "Empty" path type. This is the case for AS-local traffic. Hence, LightningFilter can currently not be used for AS-local traffic.
Nevertheless, the issue is discussed online ([issue](https://github.com/scionproto/scion/issues/4062), [pull request](https://github.com/scionproto/scion/pull/4300)).
Important: The PR is likely to change the format of the SPAO.
The timestamp in the SPAO header provides a relative offset to the start of the DRKey epoch in nanoseconds. The corresponding DRKey epoch selection is described [here](https://docs.scion.org/en/latest/protocols/authenticator-option.html#absolute-time-and-drkey-selection).
4 changes: 2 additions & 2 deletions docs/SCION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

## Installation

Some information to install SCION so that it can be run locally can be found online on the documentation website of Anapaya:
https://scion.docs.anapaya.net/en/latest/build/setup.html
Some information to install SCION so that it can be run locally can be found online on the documentation website:
https://docs.scion.org/en/latest/dev/run.html

The following instructions describe a relatively minimal installation, which allows running LightningFilter and its tests.

Expand Down
5 changes: 5 additions & 0 deletions docs/implementation/Keymanager.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,8 @@ After each worker has passed through the quiescent state, the removed entries ar
Updates to the DRKey manager are synchronized with a management lock.
The hash table provides a lock-free RW implementation (RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF), such that concurrent writes and reads are possible.
The freeing of old data is synchronized through the worker's RCU mechanism.


### Key epoch selection
With the new SPAO design the DRKey epoch has to be identified through the relative timestamp in the header.
![Image](./drkey_selection.drawio.svg "icon")
445 changes: 445 additions & 0 deletions docs/implementation/drkey_selection.drawio

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs/implementation/drkey_selection.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 2 additions & 4 deletions src/CMakeOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,14 @@ if(LF_WORKER_IGNORE_CHECKS)
set(LF_WORKER_IGNORE_TIMESTAMP_CHECK ON)
set(LF_WORKER_IGNORE_DUPLICATE_CHECK ON)
set(LF_WORKER_IGNORE_HASH_CHECK ON)
set(LF_WORKER_IGNORE_PATH_TIMESTAMP_CHECK ON)
set(LF_WORKER_IGNORE_KEY_VALIDITY_CHECK ON)
set(LF_WORKER_IGNORE_DRKEY_TIMESTAMP_CHECK ON)
endif()

option_compile_definition(LF_WORKER_IGNORE_MAC_CHECK "Ignore MAC check result" OFF)
option_compile_definition(LF_WORKER_IGNORE_TIMESTAMP_CHECK "Ignore timestamp check result" OFF)
option_compile_definition(LF_WORKER_IGNORE_DUPLICATE_CHECK "Ignore duplicate check result" OFF)
option_compile_definition(LF_WORKER_IGNORE_HASH_CHECK "Ignore hash check result" OFF)
option_compile_definition(LF_WORKER_IGNORE_PATH_TIMESTAMP_CHECK "Ignore path timestamp check result" OFF)
option_compile_definition(LF_WORKER_IGNORE_KEY_VALIDITY_CHECK "Ignore the validity of the key and just use it" OFF)
option_compile_definition(LF_WORKER_IGNORE_DRKEY_TIMESTAMP_CHECK "Ignore DRKey timestamp check result" OFF)

# Compiler Options
option(NO_UNUSED "Disable compiler warnings for unused variables" OFF)
Expand Down
4 changes: 4 additions & 0 deletions src/drkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
*/
#define LF_DRKEY_GRACE_PERIOD (2 * LF_TIME_NS_IN_S) /* in nanoseconds */
#define LF_DRKEY_PREFETCHING_PERIOD (1 * LF_TIME_NS_IN_S) /* in nanoseconds */
#define LF_DRKEY_MINIMUM_VALIDITY_PERIOD_NS \
(6 * 60 * LF_TIME_NS_IN_S) /* in nanoseconds */
#define LF_DRKEY_ACCEPTANCE_WINDOW_SIZE_NS \
(LF_DRKEY_MINIMUM_VALIDITY_PERIOD_NS / 2) /* in nanoseconds */

/*
* DRKey derivation types.
Expand Down
10 changes: 4 additions & 6 deletions src/keyfetcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ lf_keyfetcher_derive_shared_key(struct lf_crypto_drkey_ctx *drkey_ctx,
struct lf_keymanager_key_container *key)
{
struct lf_keyfetcher_sv_container *secret = NULL;
uint64_t ms_valid;
ms_valid = ns_valid / LF_TIME_NS_IN_MS;

// Find the correct shared secret to be used for current timestamp.
for (int i = 0; i < LF_CONFIG_SV_MAX; i++) {
Expand All @@ -82,11 +80,11 @@ lf_keyfetcher_derive_shared_key(struct lf_crypto_drkey_ctx *drkey_ctx,
if (secret == NULL) {
LF_KEYFETCHER_LOG(ERR,
"Could not find shared secret for: src_as " PRIISDAS
", dst_as " PRIISDAS ", drkey_protocol %u, ms_valid %" PRIu64
", dst_as " PRIISDAS ", drkey_protocol %u, ns_valid %" PRIu64
"\n",
PRIISDAS_VAL(rte_be_to_cpu_64(src_ia)),
PRIISDAS_VAL(rte_be_to_cpu_64(dst_ia)),
rte_be_to_cpu_16(drkey_protocol), ms_valid);
rte_be_to_cpu_16(drkey_protocol), ns_valid);
return -1;
}

Expand All @@ -111,12 +109,12 @@ lf_keyfetcher_derive_shared_key(struct lf_crypto_drkey_ctx *drkey_ctx,

LF_KEYFETCHER_LOG(INFO,
"Derived shared AS AS Key: src_as " PRIISDAS ", dst_as " PRIISDAS
", drkey_protocol %u, ms_valid %" PRIu64
", drkey_protocol %u, ns_valid %" PRIu64
", validity_not_before_ms %" PRIu64
", validity_not_after_ms %" PRIu64 "\n",
PRIISDAS_VAL(rte_be_to_cpu_64(src_ia)),
PRIISDAS_VAL(rte_be_to_cpu_64(dst_ia)),
rte_be_to_cpu_16(drkey_protocol), ms_valid,
rte_be_to_cpu_16(drkey_protocol), ns_valid,
(validity_not_before_ns / LF_TIME_NS_IN_MS),
(validity_not_after_ns / LF_TIME_NS_IN_MS));

Expand Down
88 changes: 57 additions & 31 deletions src/keymanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,22 @@ struct lf_keymanager {
* Check if DRKey is valid at the requested time.
*
* @param drkey: DRKey to be checked.
* @param ns_valid: Unix timestamp in nanoseconds, at which the requested key
* @param ns_now: Unix timestamp in nanoseconds, at which the requested key
* must be valid.
* @return Returns 0 if the requested time is within the DRKey's epoch. Returns
* 1 if the DRKey is valid only due to the grace period.
*/
static inline int
lf_keymanager_check_drkey_validity(struct lf_keymanager_key_container *drkey,
uint64_t ns_valid)
uint64_t ns_now)
{
if (likely(ns_valid >= drkey->validity_not_before &&
ns_valid < drkey->validity_not_after)) {
if (likely(ns_now >= drkey->validity_not_before &&
ns_now < drkey->validity_not_after)) {
return 0;
}

if (likely(ns_valid >= drkey->validity_not_before &&
ns_valid < drkey->validity_not_after + LF_DRKEY_GRACE_PERIOD)) {
if (likely(ns_now >= drkey->validity_not_before &&
ns_now < drkey->validity_not_after + LF_DRKEY_GRACE_PERIOD)) {
return 1;
}

Expand All @@ -123,17 +123,19 @@ lf_keymanager_check_drkey_validity(struct lf_keymanager_key_container *drkey,
* @param backend_addr: Packet's destination address (network byte
* order).
* @param drkey_protocol: (network byte order).
* @param ns_valid: Unix timestamp in nanoseconds, at which the requested key
* @param ns_now: Unix timestamp in nanoseconds, at which the requested key
* must be valid.
* @param grace_period: Indicator that the DRKey is in the grace period.
* @param ns_rel_time: Relative timestamp in nanoseconds to uniquely identify
* the epoch for the key that should be used.
* @param drkey: Memory to write DRKey to.
* @return 0 if success. Otherwise, < 0.
*/
static inline int
lf_keymanager_worker_inbound_get_drkey(struct lf_keymanager_worker *kmw,
uint64_t peer_as, const struct lf_host_addr *peer_addr,
const struct lf_host_addr *backend_addr, uint16_t drkey_protocol,
uint64_t ns_valid, bool grace_period, struct lf_crypto_drkey *drkey)
uint64_t ns_now, uint64_t ns_rel_time, uint64_t *ns_drkey_epoch_start,
struct lf_crypto_drkey *drkey)
{
int res;
int key_id;
Expand All @@ -149,28 +151,53 @@ lf_keymanager_worker_inbound_get_drkey(struct lf_keymanager_worker *kmw,
return -1;
}

/* Check if the new key is valid and and is in the grace period if
* requested. */
res = lf_keymanager_check_drkey_validity(&dict_node->inbound_key, ns_valid);
#if LF_WORKER_IGNORE_KEY_VALIDITY_CHECK
res = 0;
grace_period = 0;
#endif
if (res >= 0 && (res == grace_period)) {
/* NOTE: Different from the SCION documentation we only check for two
* possible DRKeys here instead of the proposed three (i-1, i, i+1). This is
* mainly done to save memory. It works since new DRKeys (i+1) are fetched
* before they are valid and after the old keys (i-1) grace period is over.
* Therefore the two stored keys are the only ones that make sense at any
* given moment. Further information can be found in the LF documentation.
*/

/* Check if the start time of the new key with offset ns_rel_time is whithin
* the acceptance window around ns_now. */
if ((dict_node->inbound_key.validity_not_before + ns_rel_time <
ns_now + LF_DRKEY_ACCEPTANCE_WINDOW_SIZE_NS) &&
(dict_node->inbound_key.validity_not_before + ns_rel_time >=
ns_now - LF_DRKEY_ACCEPTANCE_WINDOW_SIZE_NS)) {
/* A key with offet ns_rel_time could still be within the acceptance
* window but not be valid anymore. Therefore the validity has to be
* checked explicitly. */
res = lf_keymanager_check_drkey_validity(&dict_node->inbound_key,
ns_now);
if (res < 0) {
return -3;
}
lf_drkey_derive_host_host_from_as_as(&kmw->drkey_ctx,
&dict_node->inbound_key.key, backend_addr, peer_addr,
drkey_protocol, drkey);
*ns_drkey_epoch_start = dict_node->inbound_key.validity_not_before;
return 0;
}

/* Check if the new key is valid and and is in the grace period if
* requested. */
res = lf_keymanager_check_drkey_validity(&dict_node->old_inbound_key,
ns_valid);
if (res >= 0 && (res == grace_period)) {
/* Check if the start time of the old key with offset ns_rel_time is whithin
* the acceptance window around ns_now. */
if ((dict_node->old_inbound_key.validity_not_before + ns_rel_time <
ns_now + LF_DRKEY_ACCEPTANCE_WINDOW_SIZE_NS) &&
(dict_node->old_inbound_key.validity_not_before + ns_rel_time >=
ns_now - LF_DRKEY_ACCEPTANCE_WINDOW_SIZE_NS)) {
/* A key with offet ns_rel_time could still be within the acceptance
* window but not be valid anymore. Therefore the validity has to be
* checked explicitly. */
res = lf_keymanager_check_drkey_validity(&dict_node->old_inbound_key,
ns_now);
if (res < 0) {
return -4;
}
lf_drkey_derive_host_host_from_as_as(&kmw->drkey_ctx,
&dict_node->old_inbound_key.key, backend_addr, peer_addr,
drkey_protocol, drkey);
*ns_drkey_epoch_start = dict_node->old_inbound_key.validity_not_before;
return 0;
}

Expand All @@ -186,7 +213,7 @@ lf_keymanager_worker_inbound_get_drkey(struct lf_keymanager_worker *kmw,
* @param peer_addr: Packet's destination address (network byte order).
* @param backend_addr: Packet's source address (network byte order).
* @param drkey_protocol: (network byte order).
* @param ns_valid: Unix timestamp in nanoseconds, at which the requested key
* @param ns_now: Unix timestamp in nanoseconds, at which the requested key
* must be valid.
* @param drkey: Memory to write DRKey to.
* @return Returns 0 if a DRKey has been found which is valid for the requested
Expand All @@ -197,7 +224,8 @@ static inline int
lf_keymanager_worker_outbound_get_drkey(struct lf_keymanager_worker *kmw,
uint64_t peer_as, const struct lf_host_addr *peer_addr,
const struct lf_host_addr *backend_addr, uint16_t drkey_protocol,
uint64_t ns_valid, struct lf_crypto_drkey *drkey)
uint64_t ns_now, uint64_t *ns_drkey_epoch_start,
struct lf_crypto_drkey *drkey)
{
int res;
int key_id;
Expand All @@ -213,26 +241,24 @@ lf_keymanager_worker_outbound_get_drkey(struct lf_keymanager_worker *kmw,
return -1;
}

/* Check first if the new key is valid */
res = lf_keymanager_check_drkey_validity(&dict_node->outbound_key,
ns_valid);
#if LF_WORKER_IGNORE_KEY_VALIDITY_CHECK
res = 0;
#endif
/* Check if the new key is valid. */
res = lf_keymanager_check_drkey_validity(&dict_node->outbound_key, ns_now);
if (likely(res == 0 || res == 1)) {
lf_drkey_derive_host_host_from_as_as(&kmw->drkey_ctx,
&dict_node->outbound_key.key, peer_addr, backend_addr,
drkey_protocol, drkey);
*ns_drkey_epoch_start = dict_node->outbound_key.validity_not_before;
return res;
}

/* Check if the old key is valid */
res = lf_keymanager_check_drkey_validity(&dict_node->old_outbound_key,
ns_valid);
ns_now);
if (likely(res == 0 || res == 1)) {
lf_drkey_derive_host_host_from_as_as(&kmw->drkey_ctx,
&dict_node->old_outbound_key.key, peer_addr, backend_addr,
drkey_protocol, drkey);
*ns_drkey_epoch_start = dict_node->old_outbound_key.validity_not_before;
return res;
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/json-parser/lf_json_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ lf_json_parse_byte_buffer(const json_value *json_val, int len, uint8_t val[])

/**
* Parse timestamp from iso string.
* @param val result timestamp in unix time
* @param val result timestamp (nanoseconds) in unix time
*/
static inline int
lf_json_parse_timestamp(const json_value *json_val, uint64_t *val)
Expand Down
52 changes: 2 additions & 50 deletions src/lib/scion/scion.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,13 @@ struct scion_packet_authenticator_opt {
uint8_t spi_drkey_zero0;
#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
uint8_t spi_drkey_zero1: 3;
uint8_t spi_drkey_rr: 2;
uint8_t spi_drkey_rrr: 3;
uint8_t spi_drkey_t: 1;
uint8_t spi_drkey_d: 1;
uint8_t spi_drkey_e: 1;
#elif RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
uint8_t spi_drkey_e: 1;
uint8_t spi_drkey_d: 1;
uint8_t spi_drkey_t: 1;
uint8_t spi_drkey_rr: 2;
uint8_t spi_drkey_rrr: 3;
uint8_t spi_drkey_zero1: 3;
#endif
uint16_t spi_drkey_protocol_id;
Expand Down Expand Up @@ -390,52 +388,6 @@ scion_path_hdr_length(const struct rte_mbuf *m, unsigned int offset,
}
}

/**
* Obtain the timestamp in the path. This function assumes that the complete
* SCION header is in the same buffer.
* @return Returns 0 on success. Returns 1 if the header does not contain any
* timestamp. Returns -1 if the header parsing failed.
*/
static inline int
scion_get_path_timestamp(uint8_t path_type, void *path_hdr,
uint32_t *path_timestamp)
{
uint32_t seg0_len;
struct scion_path_meta_hdr *scion_path_meta_hdr;
struct scion_path_info_hdr *scion_path_info_hdr;

switch (path_type) {
case SCION_PATH_TYPE_EMPTY:
// nothing to do here
return 1;
break;
case SCION_PATH_TYPE_SCION:
scion_path_meta_hdr = (struct scion_path_meta_hdr *)path_hdr;
seg0_len = ((scion_path_meta_hdr->seg_len[0] & 0x03) << 4) |
((scion_path_meta_hdr->seg_len[1] & 0xF0) >> 4);
if (seg0_len == 0) {
/*
* Empty Path:
* Because the following segments are not allowed to have to contain
* any hop fields, the SCION path must be empty.
*/
return 1;
}

scion_path_info_hdr =
(struct scion_path_info_hdr *)(scion_path_meta_hdr + 1);
*path_timestamp = rte_be_to_cpu_32(scion_path_info_hdr->timestamp);
return 0;
case SCION_PATH_TYPE_ONEHOP:
scion_path_info_hdr = (struct scion_path_info_hdr *)path_hdr;
*path_timestamp = rte_be_to_cpu_32(scion_path_info_hdr->timestamp);
return 0;
default:
return -1;
}
}


/*
* SCION Extension Header Operations
*/
Expand Down
3 changes: 1 addition & 2 deletions src/lib/utils/lf_ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
struct lf_ip_hdr {
uint64_t src_as;
uint16_t drkey_protocol; /* DRKey protocol (or payload length) */
uint8_t drkey_e: 1; /* grace period indicator */
uint8_t rsv: 7; /* reserved authenticated fields */
uint8_t rsv; /* reserved authenticated fields */
uint8_t next_proto_id; /* payload proto identifier */
uint64_t timestamp; /* timestamp in nanoseconds */
uint8_t hash[20]; /* payload hash */
Expand Down
Loading

0 comments on commit b7a99d4

Please sign in to comment.