Skip to content

Commit

Permalink
Merge pull request snabbco#5 from eugeneia/vita-ske1-impl-g
Browse files Browse the repository at this point in the history
New key negotation protocol and implementation

Resolves snabbco#1 (KexExchange is generally broken and needs replacement)

Includes snabbco#4 (Route inbound packets via SPI)
  • Loading branch information
eugeneia authored Jan 16, 2018
2 parents 0d0840e + 18d9085 commit ec8613a
Show file tree
Hide file tree
Showing 17 changed files with 1,383 additions and 305 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ while Vita is running, without affecting unrelated routes.

- Runs on commodity hardware
- Implements IPsec for IPv4, specifically
*IP Encapsulating Security Payload* (ESP) in tunnel mode
*IP Encapsulating Security Payload* (ESP) in tunnel mode (audit needed)
- Uses optimized AES-GCM 128-bit encryption based on a reference
implementation by *Intel* for their AVX2 (generation-4) processors
- Suitable for 1-Gigabit, 10-Gigabit (and beyond?) Ethernet
- Automated key exchange and rotation (work in progress!)
- Automated key exchange and rotation, with perfect forward secrecy (PFS)
(audit needed)
- Dynamic reconfiguration (update routes while running)
- Strong observability: access relevant statistics of a running Vita node

Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.vita-test
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SRCPATTERN = '\([^/\#\.]*\|\(core/\|arch/\|jit/\|syscall/\|lib/lua/\|lib/protocol/\|lib/checksum\|lib/ipsec/\|lib/yang/\|lib/xsd_regexp\|lib/maxpc\|lib/ctable\|lib/binary_search\|lib/multi_copy\|lib/hash/\|lib/cltable\|lib/lpm/\|lib/interlink\|lib/hardware/\|lib/macaddress\|lib/numa\|apps/basic/\|apps/test/\|apps/packet_filter/\|pf\|apps/pcap/\|lib/pcap/\|apps/interlink/\|apps/intel_mp/\|lib/sodium\|program/snsh\|program/vita\)[^\#]*\)'
SRCPATTERN = '\([^/\#\.]*\|\(core/\|arch/\|jit/\|syscall/\|lib/lua/\|lib/protocol/\|lib/checksum\|lib/ipsec/\|lib/yang/\|lib/xsd_regexp\|lib/maxpc\|lib/ctable\|lib/binary_search\|lib/multi_copy\|lib/hash/\|lib/cltable\|lib/lpm/\|lib/interlink\|lib/hardware/\|lib/macaddress\|lib/numa\|apps/basic/\|apps/test/\|apps/packet_filter/\|pf\|apps/pcap/\|lib/pcap/\|apps/interlink/\|apps/intel_mp/\|program/snsh\|program/vita\)[^\#]*\)'

all:
SRCPATTERN=$(SRCPATTERN) $(MAKE)
Expand Down
8 changes: 3 additions & 5 deletions src/lib/ipsec/esp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,13 @@ function decrypt:new (conf)
end

function decrypt:decrypt_payload (ptr, length)
if not self.esp:new_from_mem(ptr, length)
or self.esp:spi() ~= self.spi
then return nil end

-- NB: bounds check is performed by caller
local esp = self.esp:new_from_mem(ptr, esp:sizeof())
local iv_start = ptr + ESP_SIZE
local ctext_start = ptr + self.CTEXT_OFFSET
local ctext_length = length - self.PLAIN_OVERHEAD

local seq_low = self.esp:seq_no()
local seq_low = esp:seq_no()
local seq_high = tonumber(
C.check_seq_no(seq_low, self.seq.no, self.window, self.window_size)
)
Expand Down
30 changes: 0 additions & 30 deletions src/lib/sodium.h

This file was deleted.

23 changes: 15 additions & 8 deletions src/program/vita/README.config
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ CONFIGURATION SYNTAX
net_cidr4 <ipv4prefix>;
gw_ip4 <ipv4addr>;
preshared_key <string>;
spi <spi>;

NOTES

Expand All @@ -40,15 +41,19 @@ NOTES
configure an Ethernet address.

Each route is given a human readable identifier that can be used for
documentation purposes. This identifier has no other bearing. The destination
IPv4 subnet and gateway are specified with an IPv4 prefix in CIDR notation,
and an IPv4 address respectively. Finally, each route is assigned a unique,
preshared 256-bit key, encoded as a hexadecimal string (two digits for each
octet, most significant digit first).
documentation purposes. This identifier has no other bearing. The route’s
destination IPv4 subnet and gateway are specified with an IPv4 prefix in CIDR
notation, and an IPv4 address respectively. For authentication, each route is
assigned a unique, pre-shared 256-bit key, encoded as a hexadecimal string
(two digits for each octet, most significant digit first). Finally, a unique
Security Parameter Index (SPI), which must be a positive integer equal or
greater than 256, is specified for tagging and associating encrypted traffic
for a given route. Like the pre-shared key, the SPI must be the same for both
ends of a route.

While the default configuration should be generally applicable, negotiation
timeout and the lifetime of security associations can be specified in
seconds.
While the default configuration should be generally applicable, the
negotiation timeout and lifetime of Security Associations (SA) can be
specified in seconds.

EXAMPLE

Expand All @@ -66,13 +71,15 @@ EXAMPLE
net_cidr4 192.168.20.0/24;
gw_ip4 203.0.113.2;
preshared_key 91440DA06376A668AC4959A840A125D75FB544E8AA25A08B813E49F0A4B2E270;
spi 1001;
}

route {
id testroute2;
net_cidr4 192.168.30.0/24;
gw_ip4 203.0.113.3;
preshared_key CF0BDD7A058BE55C12B7F2AA30D23FF01BDF8BE6571F2019ED7F7EBD3DA97B47;
spi 1002;
}

sa_ttl 86400;
89 changes: 89 additions & 0 deletions src/program/vita/README.exchange
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
VITA: SIMPLE KEY EXCHANGE (vita-ske, version 1g)

A simple key negotiation protocol based on pre-shared symmetric keys that
provides authentication, perfect forward secrecy, and is immune to replay
attacks.

Primitives (from libsodium 1.0.15):

• HMAC: crypto_auth_hmacsha512256 (key is 256‑bits, output is 256‑bits)
• DH: crypto_scalarmult_curve25519 (keys are 256‑bits, output is 256‑bits)
• HASH: crypto_generichash_blake2b (output is choosen to be 160‑bits)

Notational Conventions:

→ m
Denotes that we receive the message m.

← m
Denotes that we send the message m.

a ‖ b
Denotes a concatenated with b.

Description:

Let k be a pre-shared symmetric key. Let spi be a “Security Parameter Index”
(SPI). Let n1 and n2 be random 256‑bit nonces, where n1 is chosen by us.

← n1
→ n2

Let (p1, s1) and (p2, s2) be random, ephemeral, asymmetric key pairs, where
(p1, s1) is choosen by us. Let h1 = HMAC(k, spi ‖ n1 ‖ n2 ‖ p1).

← p1 ‖ h1
→ p2 ‖ h2

Ensure that h2 = HMAC(k, spi ‖ n2 ‖ n1 ‖ p2). Let q = DH(s1, p2), and ensure
that p2 is a valid argument. Let rx = HASH(q ‖ p1 ‖ p2) and
tx = HASH(q ‖ p2 ‖ p1) be key material. Assign (spi, rx) to the incoming
“Security Association” (SA), and (spi, tx) to the outgoing SA.

The description above illustrates the perspective of an active party adhering
to the protocol, i.e. the exchange is initiated by us. An opposite, passive
party adhering to the protocol, i.e. one that is merely responding to a key
exchange initiated by an active party, must ensure that the tuple
(spi, p2, h2) was received and authenticated before computing and sending its
response tuple (spi, p1, h1). For a passive party the order of messages during
the exchange is reversed:

→ n2
← n1
→ p2 ‖ h2 (ensure h2 is verified before we reply)
← p1 ‖ h1

Note that there might be no passive party in an exchange if both parties have
initiated the exchange before receiving an initial nonce message.

Security Proof:

Assuming an attacker can not guess k, and n1 has enough entropy so that the
probability that the tuple (n1, p2) has occurred before is negligible, they
are unable to produce the value h2 = HMAC(k, spi ‖ n2 ‖ n1 ‖ p2), and thus are
unable to complete a key exchange.

Assuming an attacker can not guess s1 or s2, they are unable to produce
q = DH(s1, p2) or q = DH(s2, p1), and subsequently derive rx or tx, and thus
perfect forward secrecy is provided.

A party passively adhering to the protocol will not produce a tuple
(spi, p1, h1) unless it has previously authenticated its counterpart tuple
(spi, p2, h2), and thus can not be used as an oracle.

Notes:

• Future versions of this protocol may introduce the use of a key derivation
function (KDF), such as libsodium’s crypto_kdf_blake2b_derive_from_key, to
derive additional key material from rx or tx.

References:

• The spiped protocol:
https://github.com/Tarsnap/spiped/blob/d1e62a8/DESIGN.md#spiped-design
• Additional discussion of the spiped protocol:
https://github.com/Tarsnap/spiped/issues/151
• The Sodium crypto library (libsodium):
https://download.libsodium.org/doc/
• Security Architecture for the Internet Protocol:
https://tools.ietf.org/html/rfc4301
57 changes: 51 additions & 6 deletions src/program/vita/conftest.snabb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ local conf1 = {
loopback = {
net_cidr4 = "192.168.10.0/24",
gw_ip4 = "203.0.113.1",
preshared_key = string.rep("00", 32)
preshared_key = string.rep("00", 32),
spi = 1001
}
}
}
Expand All @@ -70,7 +71,8 @@ local conf2 = {
loopback = {
net_cidr4 = "192.168.10.0/24",
gw_ip4 = "203.0.113.1",
preshared_key = string.rep("FF", 32)
preshared_key = string.rep("FF", 32),
spi = 1001
}
}
}
Expand All @@ -89,7 +91,8 @@ local conf3 = {
loopback = {
net_cidr4 = "192.168.10.0/24",
gw_ip4 = "203.0.113.2",
preshared_key = string.rep("FF", 32)
preshared_key = string.rep("FF", 32),
spi = 1001
}
}
}
Expand All @@ -106,16 +109,58 @@ local conf4 = {
public_nexthop_ip4 = "203.0.113.2",
route = {
loopback = {
net_cidr4 = "192.168.10.0/24",
net_cidr4 = "192.168.10.0/16",
gw_ip4 = "203.0.113.2",
preshared_key = string.rep("FF", 32)
preshared_key = string.rep("FF", 32),
spi = 1001
}
}
}
print("Change route public_ip4...")
print("Change public_ip4 and route net_cidr4...")
commit_conf(conf4)
S.sleep(3)

local conf5 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.2",
private_nexthop_ip4 = "192.168.10.1",
public_nexthop_ip4 = "203.0.113.2",
route = {
loopback2 = {
net_cidr4 = "192.168.10.0/16",
gw_ip4 = "203.0.113.2",
preshared_key = string.rep("FF", 32),
spi = 1001
}
}
}
print("Change route id...")
commit_conf(conf5)
S.sleep(3)

local conf6 = {
private_interface = { macaddr = "52:54:00:00:00:00" },
public_interface = { macaddr = "52:54:00:00:00:FF" },
private_ip4 = "192.168.10.1",
public_ip4 = "203.0.113.2",
private_nexthop_ip4 = "192.168.10.1",
public_nexthop_ip4 = "203.0.113.2",
negotiation_ttl = 1,
route = {
loopback2 = {
net_cidr4 = "192.168.10.0/16",
gw_ip4 = "203.0.113.2",
preshared_key = string.rep("FF", 32),
spi = 1001
}
}
}
print("Change negotiation_ttl...")
commit_conf(conf6)
S.sleep(3)

print("Remove route...")
commit_conf(conf0)
S.sleep(3)
Loading

0 comments on commit ec8613a

Please sign in to comment.