I've been evaluating TLS replacements
in constrained environments for a while now. Embedded systems have fewer (yet precise) security requirements, owing to available resources or by design.
Embedded systems, unlike people dont need to talk to every other machine on the planet. They are designed to perform a specific set of functions. Functions that most likely require them to communicate with other machines.
The key point here is that the vast majority of embedded systems communicate with a known or predetermined
set of peers. However, problem is that clearly identifying a networked machine (and its peers) is a non trivial undertaking, even today.
This repo implements the HIPv2
IETF standard which aims to solve this problem.
TLS is not exactly a good fit for a couple of reasons.
compute, bandwidth or battery
.limited resources and way too many options
is in itself a pretty good source of complexity, resulting in buggy or high-mainetnance implementations. What's needed: - 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.
update-server
in a failover scenario.The neat thing about HIPv2 is that it does NOT operate at the application-layer but rather is a part of a host's networking stack.
guarantee
that all parties possess unique cryptographically verifiable identities.Performance
has not been considered for now and but it can be optimized, when we have a working implementation.rustdhipv2
depends on smoltcp
and borrows many of its ideas (such as simplicity above all else). I'm a security consultant by profession. This is a first attempt at putting together a full fledged networking-related library. Please be feel free to chime-in if something's amiss.
smoltcp
but there are a couple of deviations in the crypto
department. One key component that binds networking and security
is the concept of an IP address. An IP address provides
network machines
secure networks
This duality of the IP address
is why we can't uniquely identify machines across different networks.
An IP address
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. For starters, HIP is an IETF standard that's been in the making for over 15 years,
machine/host identification
and machine/host location
This simple change can help us build drastically different networks where secure internetworking is an inherent property of the system.
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)
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 we were just looking for security).
HIP is a 2 round-trip, end-to-end Diffie-Hellman key-exchange protocol, called base-exchange with mobility updates and some additional messages. The networking stack triggers the base exchange automatically when an application tries to connect to an HIT.
During a base exchange, a client (initiator) and a server (responder) authenticate each other with their public keys and generate symmetric encryption keys. The data flow between them is encrypted by IPsec Encapsulating Security Payload (ESP) with the symmetric key set up by HIP. HIP introduces mechanisms, such as computational puzzles
, that protect HIP responders (servers) against DoS attacks. The initiator must solve a computational puzzle. The responder selects the difficulty of the puzzle according to its load. When the responder is busy or under DoS attack, the responder can increase the puzzle difficulty level to delay new connections. Applications simply need to use HITs instead of IP addresses. Application source code does not need to be modified.
We can describe this process as follows:
DNS Lookup |
---|
I --> DNS: lookup R |
I <-- DNS: return R's address and HI/HIT |
Base | Exchange | |
---|---|---|
I1 | I --> R | Hi, Here is my I1, let's talk with HIP |
R1 | R --> I | OK, Here is my R1, solve this HIP puzzle |
I2 | I --> R | Computing, here is my counter I2 |
R2 | R --> I | OK. Let's finish base exchange with my R2 |
Encrypted data |
---|
I --> R (ESP protected data) |
R --> I (ESP protected data) |
HMAC and AES keys
num-bigint-dig
crate).Note: This is a huge monolith for now. I do plan to re-factor this to make it more modular i.e. I'm thinking there should be a separate packet-processing logic block for each of the different packet-types. This should in theory make it easier to use rust's type-system to to enforce state-transition rules aka make invalid states un-representable.
The examples folder contains 2 examples
rawsockets
and is assigned
192.168.69.1
2001:2001:5731:32f2:ae0e:b28b:2c08:f623
192.168.69.2
2001:2001:843a:9626:0cb7:158b:7416:f652
You can test these examples via the cargo run
command. But before you do, you'll need to add a bridge and 2 tap interfaces
. You can do this via the following commands
ip tuntap add name tap0 mode tap user {$USER}
ip tuntap add name tap1 mode tap user {$USER}
brctl addbr br0
brctl addif br0 tap0 tap1
ip link set tap0 up
ip link set tap1 up
ip link set br0 up
check to see if everything's working via an ifconfig
call. Once done, run the following in separate terminals.
cargo run --example hip_responder
(make sure you run the server/responder first)cargo run --example hip_initiator
This should produce initiator and responder logs. Reference logs are available in the logs folder.
If you're using VSCode, pull up the task-list and run the bridge tap interfaces
task. This should run the above commands and set you up.
We can now design networks where devices talk to each other without having to navigate the complex landscape of network security.