Zero-trust networking for bare-metal systems, using Rust.
By: Nihal Pasham
Tags: Rust, Zero Trust, Identity, Networking, Protocol

I’ve been evaluating `TLS replacements` in constrained/embedded systems for a while now. Embedded systems have fewer (yet precise) security requirements, owing to available resources.

Machines, unlike people don’t need to talk to every other machine/device on the planet. They are designed to perform a specific set of functions. Functions that most likely require them to communicate with a predetermined set of peers/hosts.

The vast majority of embedded systems communicate with a `known or predetermined` set of peers.

However, clearly identifying a machine/device/entity is a non trivial undertaking, even today.

Right about now, you must be thinking — don’t we have TLS for this? Well, sort of.

Why not TLS:

TLS is not exactly a good fit in bare-metal environments for a couple of reasons.

It comes with a plethora of extensions, cipher-suites and options, most of which are simply not used (and not needed). In addition to this, TLS presents a set of challenges unique to devices limited in resources such as `compute, bandwidth or battery`. For example: large certificate sizes, relatively more-expensive compute operations (handshakes and extension usage) or subtle randomness and timing requirements.

In reality, this combination of `limited resources and way too many options` is in itself a pretty good source of complexity, resulting in buggy or high-maintenance implementations.

Put another way, this is a non-trivial undertaking, where even the most inconspicuous slip-up could prove to be a complete showstopper for everyone involved — the product teams/developers/integrators/maintenance engineers/mgmt.

Mutual TLS authentication is NOT the default and a serious pain to get right, especially in a constrained environment. TLS is not exactly lightweight (even with TLS 1.3) when you begin to account for extensions.

What we need: —is a simpler, easy-to-use, lightweight secure channel. A secure channel with just 2 pre-requisites. It must provide any 2 communicating parties the ability `to mutually authenticate each other and encrypt all data, end-end.`

More importantly, both pre-requisites must be the default and not tacked-on.

Enter HIPv2:

HIPv2 is an IETF standard [rfc7401] that offers

- Mutual authentication by default — the central idea is the creation and usage of unique, permanent, and cryptographically verifiable host/machine identities.

- End-End encryption — although not exactly a part of the HIPv2 standard, it is designed around ESP.

- Mobility and Multi-homing — This means you can do things like seamlessly switch-over to a backup `update-server` in the event of a failover etc.

The neat thing about HIPv2 is that it does NOT operate at the application-layer but rather can be a part of the host’s networking stack.

A quick and HIP(py) Overview:

One key component that binds `networking and security` is the concept of an IP address. An IP address provides

Location information to `network machines`

Identity information to `secure networks`

This duality of the `IP address` is why we can’t uniquely identify machines across different networks.

An IP address

can be spoofed,

can be dynamic,

is not a reliable piece of identification.

Yet every piece of networking equipment — from firewalls to access controllers to IoT devices, all rely on an `IP` for location and identification.

I spent some time understanding the concept of a location-identity split. HIP solves the problem of having to rely on overloaded IP addresses by splitting the 2 tasks i.e.

- machine/host identification and

- machine/host location

It proposes the use of 2 different markers to represent location and identification information. This simple change can help us build drastically different networks where secure internetworking is an inherent property of the system.

How it works -

HIP assigns a permanent, location-independent name to a host. HIP names are cryptographic identities that can be used to uniquely identify a host called host identity (it’s essentially a public key). As public keys are quite long, usually it is more convenient to use a 128-bit fingerprint of the HI, which is called the Host Identity Tag (HIT). The HIT resembles an IPv6 address, and it gives the host a permanent name.

The Internet Assigned Numbers Authority (IANA) allocated an IPv6 address prefix for HITs (2001:0010::/28)

Just to keep things simple, lets assume we have a device running an operating system (rather than a bare-metal networking stack).

In HIP, when you call the OS’s socket API, transport sockets (TCP/UDP) are bound to HITs rather than IP addresses. The networking stack translates the HITs to IP addresses before packet transmission on the wire. The reverse occurs on the host receiving HIP packets. When the host changes the network, the networking stack changes the IP address for the translation. The application doesn’t notice the change because it is using the constant HIT. This is how HIP solves the problem of host mobility (which is a bonus if you were just looking to add security).