#!/usr/bin/env perl
# xfrm2shark - feed IPSec keys from Linux xfrm to Wireshark
# (c) 2016 Mantas Mikulėnas <grawity@gmail.com>
# Released under the MIT License (dist/LICENSE.mit)
#
# 1. Provide the keys:
#
#    sudo ip xfrm state | ./xfrm2shark.pl > ~/.wireshark/esp_sa
#
# 2. Capture:
#
#    tshark -o "esp.enable_encryption_decode: TRUE" -n -i eth0 -f "host 1.2.3.4"
#
# TODO: Update from strongswan's src/libcharon/plugins/save_keys/save_keys_listener.c

my %ENC_ALGOS = (
	"enc cbc(aes)"			=> "AES-CBC [RFC3602]",
	"enc rfc3686(ctr(aes))"		=> "AES-CTR [RFC3686]",
	"aead rfc4106(gcm(aes)) 128" 	=> "AES-GCM with 16 octet ICV [RFC4106]",
	# "TripleDES-CBC [RFC2451]",
	# "DES-CBC [RFC2405]",
	# "CAST5-CBC [RFC2144]",
	# "BLOWFISH-CBC [RFC2451]",
	# "TWOFISH-CBC", # sic
	# "AES-GCM [RFC4106]",
	# "AES-GCM with 8 octet ICV [RFC4106]",
	# "AES-GCM with 12 octet ICV [RFC4106]",
);

my %AUTH_ALGOS = (
	""				=> "NULL",
	"auth-trunc hmac(sha1) 96"	=> "HMAC-SHA-1-96 [RFC2404]",
	"auth-trunc hmac(sha256) 128"	=> "HMAC-SHA-256-128 [RFC4868]",
	# "HMAC-SHA-256-96 [draft-ietf-ipsec-ciph-sha-256-00]",
	# "HMAC-SHA-384-192 [RFC4868]",
	# "HMAC-SHA-512-256 [RFC4868]",
	# "HMAC-MD5-96 [RFC2403]",
	# "MAC-RIPEMD-160-96 [RFC2857]",
);

sub out {
	my (%cur) = @_;
	my $family = ($cur{src} =~ /:/) ? "IPv6" : "IPv4";
	my $enc_algo = $ENC_ALGOS{$cur{enc_algo}}
		// die "error: unmapped enc algorithm '$cur{enc_algo}'\n";
	my $auth_algo = $AUTH_ALGOS{$cur{auth_algo}}
		// die "error: unmapped auth algorithm '$cur{auth_algo}'\n";
	my @row = (
		$family, $cur{src}, $cur{dst}, $cur{spi},
		$enc_algo, $cur{enc_key},
		$auth_algo, $cur{auth_key},
	);
	print join(",", map {"\"$_\""} @row)."\n";
	return ();
}

my %cur;

while (<>) {
	if (/^src (\S+) dst (\S+)$/) {
		%cur = out(%cur) if %cur;
		$cur{src} = $1;
		$cur{dst} = $2;
	}
	elsif (/^\s+proto esp spi (0x\w+) /) {
		$cur{spi} = $1;
	}
	elsif (/^\s+(aead \S+) (0x\w+) (.+)$/) {
		$cur{enc_algo} = $1." ".$3;
		$cur{enc_key} = $2;
		$cur{auth_algo} = "";
		$cur{auth_key} = "";
	}
	elsif (/^\s+(auth-trunc \S+) (0x\w+) (.+)$/) {
		$cur{auth_algo} = $1." ".$3;
		$cur{auth_key} = $2;
	}
	elsif (/^\s+(enc \S+) (0x\w+)$/) {
		$cur{enc_algo} = $1;
		$cur{enc_key} = $2;
	}
	elsif (/^\s+(socket)/) {
		die "error: expected 'ip xfrm state', not 'ip xfrm policy'\n";
	}
}

out(%cur) if %cur;
