Another night's work - move to ECDH + AES256 from RSA pubkey
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ pass-1/wrapper
|
||||
pass-1/unwrapper
|
||||
pass-1/hide-eid
|
||||
pass-1/example-keys
|
||||
README.html
|
||||
|
92
README.md
92
README.md
@@ -6,7 +6,6 @@ participants in the Internet.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
As the Location/Identity Separation Protocol people have noted, IPv4 and IPv6
|
||||
both use IP addresses (Endpoint IDs, in the lingo) to make routing decisions.
|
||||
Focusing on this from the point of view of routing table efficiency / features,
|
||||
@@ -60,6 +59,7 @@ the scheme over onion routing, such as tor; no significant latency is added.
|
||||
When received by the destination ISP, it can use its private key to decrypt the
|
||||
encapsulated packet, and send that decrypted packet on to its final hop.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
Pass 1 now exists, in a rudimentary form. Here's how to put together a couple of
|
||||
@@ -76,12 +76,12 @@ maintained by this project - they can even be RFC1918 space, as long as there
|
||||
are no overlaps within this registry. Remember, EIDs aren't used to make routing
|
||||
decisions across the Internet..
|
||||
|
||||
Generate some RSA private keys, and their public components, in PEM format:
|
||||
Generate some ECC private keys, and their public components, in PEM format:
|
||||
|
||||
$ openssl genrsa -out rloc1.private.pem 4096
|
||||
$ openssl rsa -in rloc1.private.pem -pubout -out rloc1.public.pem
|
||||
$ openssl genrsa -out rloc2.private.pem 4096
|
||||
$ openssl rsa -in rloc2.private.pem -pubout -out rloc2.public.pem
|
||||
$ openssl ecparam -genkey -out rloc1.private.pem -name secp160r2
|
||||
$ openssl ec -in rloc1.private.pem -pubout -out rloc1.public.pem
|
||||
$ openssl ecparam -genkey -out rloc2.private.pem -name secp160r2
|
||||
$ openssl ec -in rloc2.private.pem -pubout -out rloc2.public.pem
|
||||
|
||||
Add entries to the rloc-registry.json file to reflect your mappings. You need to
|
||||
put an entry (a JSON object) to the "eid_rloc_map" array, like this:
|
||||
@@ -124,8 +124,58 @@ vice-versa, and get an ICMP echo reply back. You can also run TCP or UDP
|
||||
servers on one of the IPs, and connect to them from the other IP. If you run
|
||||
wireshark or tcpdump on an intermediate machine (or just one of the hosts, if
|
||||
you focus on the egress/ingress traffic) you'll see obscure IP packets with
|
||||
just the RLOC addresses as source and destination, with no visible UDP/TCP
|
||||
headers.
|
||||
just the RLOC addresses as source and destination, and no visible UDP/TCP
|
||||
headers. IP Protocol is set to 99 - "any private encryption scheme".
|
||||
|
||||
|
||||
Encryption
|
||||
----------
|
||||
Encryption scheme is really the only novel portion of this project; the rest is
|
||||
covered in the L/ISP RFCs. This code is all about slapping together a basic
|
||||
L/ISP router (badly), and implementing cryptography for the encapsulated IP
|
||||
packets, for the sake of experimenting. Crypto is hard, and experimentation is
|
||||
key (ha ha).
|
||||
|
||||
Current scheme:
|
||||
~~~~~~~~~~~~~~~
|
||||
This seems less stupid.
|
||||
|
||||
* EC public keys in central repository
|
||||
* Each participant knows only their private key
|
||||
* Generate ECDH secret for each peer using their public + your private key
|
||||
* pseudo-random 128-bit IV per-packet, put at the start of encrypted data
|
||||
* Use as256 symmetric encryption with sha256( ecdh ) to encrypt / decrypt
|
||||
|
||||
Main point is that routers don't need to communicate with each other to
|
||||
negotiate a shared key - they can independently derive the same asymmetric key
|
||||
as long as they share some common assumptions, have their own private key, and
|
||||
the peer's public key.
|
||||
|
||||
Asymmetric key size is smaller, and we're moving to a symmetric cipher for the
|
||||
actual packet encryption, so hopefully this will be much faster than scheme 0.
|
||||
|
||||
Which curve should we be using? No clue. What size of key should we be using?
|
||||
No clue. Is this kind of shared key appropriate when we're passing considerable
|
||||
traffic? No clue.
|
||||
|
||||
Scheme 0:
|
||||
~~~~~~~~~
|
||||
This was stupid.
|
||||
|
||||
* RSA public keys in central repository
|
||||
* Just use public key to directly encrypt packet data
|
||||
* Use private key to decrypt packets addressed to you.
|
||||
|
||||
This is slow, and you can only encrypt data that's smaller than the key modulus,
|
||||
or something like that.
|
||||
|
||||
First result: rtt increased from 37ms to 80ms.
|
||||
|
||||
For access<->hosting, that kind of latency increase is bad, but bearable. For
|
||||
hosting<->hosting, it's completely unacceptable.
|
||||
|
||||
Not all of it may be crypto-related - worth implementing a no-op branch that
|
||||
just encapsulates, and checking the difference.
|
||||
|
||||
|
||||
Limitations
|
||||
@@ -136,30 +186,18 @@ Certainly for access ISPs, even with the best will in the world, the
|
||||
infrastructure between them and their layer 1/2 service providers may be bugged.
|
||||
This is not protected against by this scheme; if you suspect this is happening
|
||||
to your ISP without their knowledge, you can run IPSec over the link and allow
|
||||
them to terminate it just before the box that wraps the packets. If you suspect
|
||||
it is happening with their knowledge, the best you can do is change ISP. If we
|
||||
run out of good ISPs, this scheme adds nothing.
|
||||
them to terminate it just before (or on) the box that wraps the packets. If you
|
||||
suspect it is happening with their knowledge, the best you can do is change ISP.
|
||||
If we run out of good ISPs, this scheme adds nothing. You can always start a VPN
|
||||
ISP.
|
||||
|
||||
If the other side of the link is complicit, this scheme does nothing. It isn't
|
||||
going to stop Facebook from handing all their records of your accesses to them
|
||||
over to the NSA. Stop using Facebook.
|
||||
|
||||
Public-key encryption is relatively slow compard to block ciphers; making this
|
||||
scale is going to be a challenge. Hopefully not impossible - but if it's too
|
||||
expensive, uptake will be low or zero. If it's too unreliable, uptake will be
|
||||
low or zero.
|
||||
|
||||
First result: rtt increased from 37ms to 80ms. There are four cryptographic
|
||||
operations in each trip - encrypt outgoing packet, decrypt outgoing packet,
|
||||
encrypt return packet, decrypt return packet. This is on a laptop and VM with
|
||||
no hardware crypto support of any kind, of course. but it's also with nothing
|
||||
else to do at all.
|
||||
|
||||
For access<->hosting, that kind of latency increase is bad, but bearable. For
|
||||
hosting<->hosting, it's completely unacceptable.
|
||||
|
||||
Not all of it may be crypto-related - worth implementing a no-op branch that
|
||||
just encapsulates, and checking the difference.
|
||||
There are four cryptographic operations in each trip - encrypt outgoing packet,
|
||||
decrypt outgoing packet, encrypt return packet, decrypt return packet. This is
|
||||
going to be slower than no crypto. Too slow?
|
||||
|
||||
May break ICMP and other responses from intermediate ISPs. Path MTU discovery
|
||||
breaks, for instance, with a naive implementation of this scheme, as does
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/make -f
|
||||
CFLAGS := $(CFLAGS) -Wall -Werror --std=c99
|
||||
|
||||
LDFLAGS := $(LDFLAGS) -ljson-c -lssl -lcrypto
|
||||
CFLAGS := $(CFLAGS) -Wall -Werror --std=c99
|
||||
LDFLAGS := $(LDFLAGS) -ljson-c -lssl -lcrypto
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
|
@@ -108,7 +108,11 @@ int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* o
|
||||
unsigned short *pkt_enc_size = (unsigned short *) scratch;
|
||||
scratch += 2;
|
||||
|
||||
enc_size = rloc_encrypt( d_rloc, (unsigned char *)&pkt->hdr, bytes_to_encrypt, scratch, enc_max_len - 2 );
|
||||
enc_size = rlocs_encrypt(
|
||||
reg, s_rloc, d_rloc,
|
||||
(unsigned char *)&pkt->hdr, bytes_to_encrypt, scratch, enc_max_len - 2
|
||||
);
|
||||
|
||||
if ( enc_size < 0 ) {
|
||||
warn( "failed to encrypt, dropping packet" );
|
||||
return 0;
|
||||
@@ -118,8 +122,6 @@ int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* o
|
||||
enc_size += 2;
|
||||
scratch = (unsigned char*) pkt_enc_size;
|
||||
|
||||
|
||||
|
||||
warn( "Encrypted size: 2 + %zu", enc_size - 2);
|
||||
|
||||
out->iovs[1].iov_base = scratch;
|
||||
@@ -163,12 +165,19 @@ int unwrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data*
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We need to know destination rloc to decrypt the packet
|
||||
struct rloc* rloc;
|
||||
// We need to know source and destination rloc to decrypt the packet
|
||||
struct rloc *s_rloc, *d_rloc;
|
||||
struct in_addr tmp;
|
||||
|
||||
tmp.s_addr = pkt->hdr.ip.saddr;
|
||||
if ( ( s_rloc = rloc_find_by_address( reg, &tmp, NULL ) ) == NULL ) {
|
||||
warn( "Couldn't find rloc from source IP, dropping packet" );
|
||||
// TODO: we should be able to specify we need it to have a private key
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp.s_addr = pkt->hdr.ip.daddr;
|
||||
if ( ( rloc = rloc_find_by_address( reg, &tmp, NULL ) ) == NULL ) {
|
||||
if ( ( d_rloc = rloc_find_by_address( reg, &tmp, NULL ) ) == NULL ) {
|
||||
warn( "Couldn't find rloc from destination IP, dropping packet" );
|
||||
// TODO: we should be able to specify we need it to have a private key
|
||||
return 0;
|
||||
@@ -182,7 +191,10 @@ int unwrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data*
|
||||
unsigned char *encrypted_data = ((unsigned char *)pkt) + hdr_size + 2;
|
||||
unsigned char *scratch = &out->scratch[0];
|
||||
|
||||
int decrypted_size = rloc_decrypt( rloc, encrypted_data, encrypted_size, scratch, IP_MAXPACKET );
|
||||
int decrypted_size = rlocs_decrypt(
|
||||
reg, d_rloc, s_rloc,
|
||||
encrypted_data, encrypted_size, scratch, IP_MAXPACKET
|
||||
);
|
||||
|
||||
if ( decrypted_size < 0 ) {
|
||||
warn( "Failed to decrypt packet!" );
|
||||
|
@@ -2,77 +2,25 @@
|
||||
"type": "rloc-registry",
|
||||
|
||||
"eid_rloc_map": [
|
||||
{ "family":"ipv4", "network":"172.16.10.0", "netmask":24, "rloc":"172.16.10.2"},
|
||||
{ "family":"ipv4", "network":"172.16.11.0", "netmask":24, "rloc":"172.16.10.2"},
|
||||
{ "family":"ipv4", "network":"172.16.12.0", "netmask":24, "rloc":"172.16.12.2"},
|
||||
{ "family":"ipv6", "network":"fc00:1::", "netmask":64, "rloc":"fc00::2" },
|
||||
{ "family":"ipv6", "network":"fc00:2::", "netmask":64, "rloc":"fc00:2::2" }
|
||||
{ "family":"ipv4", "network":"172.16.10.0", "netmask":24, "rloc":"81.187.47.40"},
|
||||
{ "family":"ipv4", "network":"172.16.11.0", "netmask":24, "rloc":"213.138.102.147"}
|
||||
|
||||
],
|
||||
|
||||
"keys": {
|
||||
"172.16.10.2":
|
||||
"-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0FxctMUibDqkVhChIvJg
|
||||
6GhzKzaMEKza+9MhO1H0VEnvgMsvZ6S15iSxHVxiO5KpjwYAakW4ELLK7We+exsH
|
||||
zOSAioh1Po3uxTLga7PrEvLkv7hI/2vxWPYk2j2j++WHKXjyNoAKpdC5nrauRPT2
|
||||
DAlbRcRu9gnhLIIDu/HCLLxT1qQgWOaVZBJVxxcPYwrBPSjDAvKn5GKbN8wh4k1h
|
||||
WIkedhHIjzM7mYQfXTaJttP2+9Gco5sOntKVldy2nGWWtaPDtlMmmCdclgrBpxT9
|
||||
EPEZ+H9ApPWHAMEgVvLn9nKPNJ/06jPFEcA5ZEtu6N+lPjkjRU9JfuMPt/jz3otY
|
||||
1iA/Utx1tQQqEzote5V5zxHnBcYtqtftf7PKANVfPnLQxQ9msKmrcBb0bNwn3KdX
|
||||
pavbsPp5W4WGFalt499u3CGmd6AQ9/tZfafTEsAPCpNQrfZ4v0AVIiT6ZuQQ1Vip
|
||||
q14pHlpN3xepxwHZtCJTyU7kHaWKvImFZRY6dg0dRkvWl2bO6xJsYYXTaYHre+Sx
|
||||
ivtEZcM7VPEZlQyBlwTYYHmnllBlnXMJpx20p26Sy4iPX6hzn9sT3UQHE6NAlfON
|
||||
He5Cw+4ma5DA2jQvvIBfYA1ipVfGKD8LyrPrxuD9qErYpPBP23EdJwT0ZL+jKjyT
|
||||
t39smrDydkWUQWYPiiUKKM0CAwEAAQ==
|
||||
-----END PUBLIC KEY-----",
|
||||
|
||||
"172.16.12.2":
|
||||
"81.187.47.40":
|
||||
"-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvYljSQd/jHZtSTc6Z2E1
|
||||
6D37s/MDGmWux/0ulRLsnGRnIj/cd8b3wV2AKbucjEOZ70MH5mU3szz/eZwD5z3A
|
||||
hU+2ok9GV3vL1COg692RRV9ydizxc/8PduYyojeBiOgwBQvCS0PM6IcEDbNvgtfY
|
||||
yzcnMf56Iadqukp5ovCZU/xl4zPMNwtVonMoeOgpdPBd9POBAhXI5zvZDEIF+PM3
|
||||
Lfuy+ybn5alJKDQwWE2tjWnNWx3e34JKTulN7xC9Cw4xw+zmoG5GYua5RtM7AYsJ
|
||||
cCBmF4V3XhpPnxgGhO5S3tKWyke317FjNBqbG1Gj30ADYliHZI1KEQbaXCHxB8lQ
|
||||
bII6OEz9liz4QNSDd2iTInA7yv2djOIdEoPCgtY7oXQZi5Ch8q2M9sAVGAyyF2sR
|
||||
2SlUiP0fuH2S5CviI/5KKTHMA8HNg2rH6gPeTDiLDluw+oL3tIy3XEmhUJS3ZSzG
|
||||
PhN1So0n3XwsnAUWvm33ujTtvAGTuUwBKsG8nIb3iwr7Vh8O617yuOl7ab68Dxym
|
||||
fJZZE5wzjzYbNxup8E9VSfHKSCUHptg9cB/1kF5HoshZCZwA62eAtQgaamr64NrT
|
||||
Uuyz1Ng3KTiD1cObXeirB88yyYd1W21deRfGaGZlWpJTAMjy/bXjR6ZAA+xTXllO
|
||||
QFqgpMLJRNxWj7UUOWm3xjECAwEAAQ==
|
||||
-----END PUBLIC KEY-----",
|
||||
MD4wEAYHKoZIzj0CAQYFK4EEAB4DKgAEdlvMHCK1rsACyk53Oo5PEfmj/ZON3oKt
|
||||
wXgyHsydIb6JMt2tD3bFZA==
|
||||
-----END PUBLIC KEY-----",
|
||||
|
||||
"fc00::2":
|
||||
"213.138.102.147":
|
||||
"-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnQhQ9S190q/+t0/QqplD
|
||||
+aRHLrVm8oWFHKqfaE11zZ+7adse4NFgzEWMRN2bUIl47W/VuTHn3NtsF3myYjtX
|
||||
n9bXW/fibVj1xFXk5qpsHvC1xgdUTP3msqiEXigHIgJKgaTCDOaYykfBaFKNP334
|
||||
Dz0mZ4xTmMwmp/9MEiaj8ieFWBTJ5+QFJZCwYUCNGI3hWaXrmlNoKBIj4vfpnFv1
|
||||
MUSCcvc3yl4+KBnIqq0rNfQqlvwNl1NfI2P80eGKW6urQSIjxqb966pOilVePoj8
|
||||
iY8LUtT4WknCacM1oi9rVE0sjtZxeLUs5EuToFn6O2L576SJvNg9GvGA4YmqPTkS
|
||||
fkxunarY+I7frdZyTWg/Bgr5rWXP7QJ5VY11VbsTo7vGblWQ6MqYH00owGuKXFqg
|
||||
7Fl+dr+Ko3uLLnWXF92f4xvP5//0YmRADDGNu1Elgr7NxEoIe7iWFuaga2tV3BPf
|
||||
//3nneHhPv3d4OYnImMQLih+OF25hfYFI8V1ef5dpKXuSNQ01nxIexRHTd0sQnTS
|
||||
Agq3TFQzB5U54k554plWUjPvHFvUDmCyAdTAEn2/0xJQZ3X1RqFZfzjW+rcARjnt
|
||||
lxtMZOyE1S1kAg0HS0uEGLYrIp0K+moEKhm3Upvv3ad3FP1695tJUBec0KH8/PbX
|
||||
XlTdlmviw5Fo15QnPOLcsW8CAwEAAQ==
|
||||
-----END PUBLIC KEY-----",
|
||||
MD4wEAYHKoZIzj0CAQYFK4EEAB4DKgAEaDuTFicM87FKVqEHUMGW4BaFz9GAwjAC
|
||||
+pnmMUh00yFgDEyi6ozUrw==
|
||||
-----END PUBLIC KEY-----"
|
||||
|
||||
"fc00:2::2":
|
||||
"-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt/rGbJ46DIciiRHaknkV
|
||||
pQ0ulKtZl5UApOaKTP8SxuEoDDrUZ7w3FnItPYkDh41RisVf7hBAKe+BwCRxodo0
|
||||
++MeMcbrWG16rT/2AVi7V5te936L3O/WiF/BFgWXgJ1+bgXlZlIsJWLqMT5ozcaM
|
||||
XRIosgoYMkvvS4tBYLwQIx3ov+PVfZELOmqtoesv5e70Xg0iXNVb57wTnsnBPHo0
|
||||
u4Pw1oSsqDNM4oblDwXtfJEUMcBA+q12L5lqBEnXGlFQgeViCFnpaR03oc0btn0S
|
||||
PZtAgZ88WPdnf+jAeSeEnVvTmhQtNxmnZXtEy/T0emD+163/lpcr7vIK/iFXvZsx
|
||||
Bf2xdRs7EU/6M+YaE8rhI/uTNgMpR2RGAIyQn8aGCydGf/FCxXMBg99mk5R0h2cD
|
||||
BkGyVbmFzXrsoitw5Foz/JC5svvoR/z16w/+lIbRRx2OMSaMtoRqYO6At2GC30TZ
|
||||
DSDQ5nNOo4hmySZnrEWq+t4BJjJ1wu7eAJpsCVXkyRZUtNt6c98VaAuJQv7FHW/O
|
||||
75XaEHewVAAGTmQEjI/cTY72bHkbtE3vpi0aK8Wc7K4g7slUld0HUsh8xszIhdxu
|
||||
8Y+yuP72oOpicPK8LCDyI28V0HCz44bo1TDrDUC0YZ1fDr8Pk2A+jrlt4/yMcgkn
|
||||
PBcctUqN/b2cIfCA8UDAMZ0CAwEAAQ==
|
||||
-----END PUBLIC KEY-----"
|
||||
|
||||
}
|
||||
|
||||
|
246
pass-1/rlocs.c
246
pass-1/rlocs.c
@@ -17,30 +17,41 @@
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
|
||||
#include <json/json_tokener.h>
|
||||
#include <json/linkhash.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "util.h"
|
||||
//#include "util.h"
|
||||
#include "rlocs.h"
|
||||
|
||||
#define JSON_TYPE "rloc-registry"
|
||||
|
||||
void show_ssl_errors(void)
|
||||
{
|
||||
ERR_print_errors_fp(stderr);
|
||||
return;
|
||||
}
|
||||
|
||||
void rlocs_init(void)
|
||||
{
|
||||
uint32_t seed;
|
||||
SSL_load_error_strings(); /* readable error messages */
|
||||
SSL_library_init(); /* initialize library */
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
// set the random seed to something good
|
||||
if ( RAND_bytes( (unsigned char*)&seed, sizeof( uint32_t ) ) != 1 ) {
|
||||
warn( "Failed to initialize random seed" );
|
||||
show_ssl_errors();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
srand( seed );
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,7 +61,8 @@ int rlocs_rloc_from_json(struct rlocs* reg, const char* key, json_object* val )
|
||||
ssize_t pem_data_size;
|
||||
const char* pem_data;
|
||||
struct rloc *rloc;
|
||||
RSA *rsa_key, *result;
|
||||
EVP_PKEY *evp_key = NULL;
|
||||
EC_KEY *ec_key = NULL;
|
||||
BIO *bp;
|
||||
|
||||
if ( !json_object_is_type( val, json_type_string ) ) {
|
||||
@@ -80,20 +92,47 @@ int rlocs_rloc_from_json(struct rlocs* reg, const char* key, json_object* val )
|
||||
pem_data = json_object_get_string( val );
|
||||
pem_data_size = json_object_get_string_len( val );
|
||||
|
||||
rsa_key = RSA_new();
|
||||
bp = BIO_new_mem_buf( (void*)pem_data, pem_data_size );
|
||||
result = PEM_read_bio_RSA_PUBKEY( bp, &rsa_key, NULL, NULL );
|
||||
BIO_vfree( bp );
|
||||
|
||||
if ( result == NULL ) {
|
||||
RSA_free( rsa_key );
|
||||
warn( "Couldn't parse key for %s as an RSA public key", key );
|
||||
return 0;
|
||||
if ( ( evp_key = EVP_PKEY_new() ) == NULL ) {
|
||||
warn( "couldn't allocate new EVP_pkey for rloc %s", key );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rloc->key = rsa_key;
|
||||
if ( !( bp = BIO_new_mem_buf( (void*)pem_data, pem_data_size ) ) ) {
|
||||
warn( "couldn't allocate new BIO to interpret key for rloc %s", key );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ec_key = PEM_read_bio_EC_PUBKEY( bp, &ec_key, NULL, NULL );
|
||||
BIO_vfree( bp );
|
||||
|
||||
if ( ec_key == NULL ) {
|
||||
warn( "Couldn't parse key for rloc %s as an EC secp160r2 public key", key );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// assign so we don't have to free the EC_KEY object any more
|
||||
if ( !EVP_PKEY_assign_EC_KEY( evp_key, ec_key ) ) {
|
||||
warn( "couldn't assign key for rloc %s to container", key );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rloc->key = evp_key;
|
||||
rloc->context_id = reg->num_entries;
|
||||
reg->num_entries++;
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
show_ssl_errors();
|
||||
|
||||
if ( evp_key != NULL ) {
|
||||
EVP_PKEY_free( evp_key );
|
||||
}
|
||||
|
||||
if ( ec_key != NULL ) {
|
||||
EC_KEY_free( ec_key );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rlocs_eid_from_json( struct rlocs *reg, json_object *map)
|
||||
@@ -410,53 +449,182 @@ struct rloc *rloc_find_by_address( struct rlocs *reg, struct in_addr *ipv4, stru
|
||||
*/
|
||||
int rloc_add_private_key( struct rloc *rloc, char *filename )
|
||||
{
|
||||
BIO *key_data = BIO_new_file( filename, "r" );
|
||||
|
||||
RSA* key = PEM_read_bio_RSAPrivateKey( key_data, NULL, NULL, NULL );
|
||||
BIO *key_data = BIO_new_file( filename, "r" );
|
||||
EC_KEY* key = PEM_read_bio_ECPrivateKey( key_data, NULL, NULL, NULL );
|
||||
|
||||
if ( key == NULL ) {
|
||||
warn( "Failed to add private key %s", filename );
|
||||
return 0;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
RSA_free( rloc->key );
|
||||
rloc->key = key;
|
||||
if ( !EVP_PKEY_assign_EC_KEY( rloc->key, key ) ) {
|
||||
warn( "Failed to assign private key in %s to rloc", filename );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
show_ssl_errors();
|
||||
|
||||
if ( key != NULL ) {
|
||||
EC_KEY_free( key );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void show_ssl_errors(void)
|
||||
int rlocs_update_key_context(struct rlocs *reg, struct rloc *x, struct rloc *y)
|
||||
{
|
||||
long err;
|
||||
char msg[256];
|
||||
struct key_context *entry = ®->key_contexts[x->context_id][y->context_id];
|
||||
|
||||
unsigned char secret[1024]; // Should be enough buffer space
|
||||
size_t secret_len = 1024;
|
||||
|
||||
while ( ( err = ERR_get_error() ) ) {
|
||||
ERR_error_string( err, &msg[0] );
|
||||
warn( "Error %lu in crypto: %s", err, &msg[0] );
|
||||
unsigned char secret_hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
int result;
|
||||
|
||||
/* Create the context for the shared secret derivation */
|
||||
if( ( ctx = EVP_PKEY_CTX_new( x->key, NULL ) ) == NULL) {
|
||||
warn( "EVP_PKEY_CTX_new failed" );
|
||||
goto fail;
|
||||
|
||||
}
|
||||
|
||||
/* Initialise */
|
||||
if( ( result = EVP_PKEY_derive_init( ctx ) ) != 1 ) {
|
||||
warn( "EVP_PKEY_derive_init failed: %i", result );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Provide the peer public key */
|
||||
if( ( result = EVP_PKEY_derive_set_peer( ctx, y->key ) ) != 1 ) {
|
||||
warn( "EV_PKEY_derive_set_peer failed: %i", result );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Derive the shared secret */
|
||||
if( ( result = EVP_PKEY_derive( ctx, &secret[0], &secret_len ) ) != 1 ) {
|
||||
warn( "EVP_PKEY_derive failed: %i", result );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* SHA256 hash of the shared secret. TODO: Add a salt if we want one... */
|
||||
if ( !sha256sum( &secret[0], secret_len, secret_hash ) ) {
|
||||
warn( "SHA256( secret ) failed!" );
|
||||
goto fail;
|
||||
}
|
||||
memcpy( &entry->secret[0], &secret_hash[0], SHA256_DIGEST_LENGTH );
|
||||
|
||||
EVP_PKEY_CTX_free( ctx );
|
||||
|
||||
/* Now we finally have our secret key, we can set up an AES256
|
||||
* symmetric key cipher with it, which is what will be used to encrypt /
|
||||
* decrypt packet data. */
|
||||
|
||||
EVP_CIPHER_CTX_init( &entry->ctx );
|
||||
entry->in_use = 1;
|
||||
|
||||
return 1;
|
||||
|
||||
|
||||
fail:
|
||||
show_ssl_errors();
|
||||
if ( ctx != NULL) {
|
||||
EVP_PKEY_CTX_free( ctx );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t rloc_encrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
|
||||
{
|
||||
ssize_t result = RSA_public_encrypt( data_len, data, dest, rloc->key, RSA_PKCS1_OAEP_PADDING );
|
||||
|
||||
if ( result < 0 ) {
|
||||
show_ssl_errors();
|
||||
ssize_t rlocs_encrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
|
||||
{
|
||||
struct key_context *entry = ®->key_contexts[x->context_id][y->context_id];
|
||||
|
||||
if ( !entry->in_use && !rlocs_update_key_context( reg, x, y ) ) {
|
||||
warn( "Couldn't build an encryption context for these rlocs" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
// FIXME: I don't know that this is good. It could be terrible.
|
||||
// We use a PRNG to generate an IV per message, and put it at the start of
|
||||
// the encrypted blob. Since we're using aes256 (hardcoded), IV is 128 bits
|
||||
if ( dest_len < 48 ) {
|
||||
warn( "dest_len must be at least 48 bytes, maybe more");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
unsigned char *iv = dest;
|
||||
unsigned char *secret = (unsigned char *)&entry->secret[0];
|
||||
size_t written = 16;
|
||||
int outl = dest_len - written;
|
||||
|
||||
RAND_pseudo_bytes( iv, 16 );
|
||||
|
||||
if ( !EVP_EncryptInit_ex( &entry->ctx, EVP_aes_256_cbc(), NULL, secret, iv ) ) {
|
||||
warn( "EVP_EncryptInit_ex() failed" );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ( !EVP_EncryptUpdate( &entry->ctx, dest + written, &outl, data, data_len ) ) {
|
||||
warn( "EVP_EncryptUpdate() failed" );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
written += outl;
|
||||
outl = dest_len - written;
|
||||
|
||||
if ( !EVP_EncryptFinal_ex( &entry->ctx, dest + written, &outl ) ) {
|
||||
warn( "EVP_EncryptFinal_ex() failed" );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return written + outl;
|
||||
|
||||
fail:
|
||||
show_ssl_errors();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t rloc_decrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
|
||||
ssize_t rlocs_decrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
|
||||
{
|
||||
ssize_t result = RSA_private_decrypt( data_len, data, dest, rloc->key, RSA_PKCS1_OAEP_PADDING );
|
||||
|
||||
if ( result < 0 ) {
|
||||
show_ssl_errors();
|
||||
struct key_context *entry = ®->key_contexts[x->context_id][y->context_id];
|
||||
|
||||
if ( !entry->in_use && !rlocs_update_key_context( reg, x, y ) ) {
|
||||
warn( "Couldn't build a decryption context for these rlocs" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
unsigned char *iv = data;
|
||||
unsigned char *secret = (unsigned char *)&entry->secret[0];
|
||||
size_t written = 0;
|
||||
int outl = dest_len;
|
||||
|
||||
if ( !EVP_DecryptInit_ex( &entry->ctx, EVP_aes_256_cbc(), NULL, secret, iv ) ) {
|
||||
warn( "EVP_DecrypttInit_ex() failed" );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ( !EVP_DecryptUpdate( &entry->ctx, dest, &outl, data, data_len ) ) {
|
||||
warn( "EVP_DecryptUpdate() failed" );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
written += outl;
|
||||
outl = dest_len - written;
|
||||
|
||||
if ( !EVP_DecryptFinal_ex( &entry->ctx, dest + written, &outl ) ) {
|
||||
warn( "EVP_DecryptFinal_ex() failed" );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return written + outl;
|
||||
|
||||
fail:
|
||||
show_ssl_errors();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -497,7 +665,7 @@ void rlocs_free( struct rlocs* registry )
|
||||
|
||||
for( i = 0; i < registry->num_entries; i++ ) {
|
||||
if ( registry->entries[i].key != NULL ) {
|
||||
RSA_free( registry->entries[i].key );
|
||||
EVP_PKEY_free( registry->entries[i].key );
|
||||
}
|
||||
}
|
||||
// No need to do json_object_put() here.
|
||||
|
@@ -1,15 +1,30 @@
|
||||
#ifndef _RLOCS_H_
|
||||
#define _RLOCS_H_
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include <json/json_object.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
// For now. We can dynamically allocate later.
|
||||
#define MAX_RLOCS 64
|
||||
#define MAX_EID_MAPPINGS 256
|
||||
|
||||
|
||||
struct key_context {
|
||||
int in_use;
|
||||
char secret[SHA256_DIGEST_LENGTH];
|
||||
EVP_CIPHER_CTX ctx;
|
||||
|
||||
/* Probably don't need these
|
||||
struct rloc *rloc_x;
|
||||
struct rloc *rloc_y;
|
||||
*/
|
||||
};
|
||||
|
||||
struct rloc {
|
||||
short family;
|
||||
union {
|
||||
@@ -17,7 +32,9 @@ struct rloc {
|
||||
struct in6_addr ip6;
|
||||
} addr;
|
||||
|
||||
RSA* key;
|
||||
EVP_PKEY *key;
|
||||
// We use this to index our rloc for shared keys
|
||||
int context_id;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +53,7 @@ struct ip6_eid_map_entry {
|
||||
};
|
||||
|
||||
struct rlocs {
|
||||
json_object* config;
|
||||
json_object *config;
|
||||
|
||||
size_t num_entries;
|
||||
struct rloc entries[MAX_RLOCS];
|
||||
@@ -46,6 +63,16 @@ struct rlocs {
|
||||
|
||||
size_t num_ip6_map_entries;
|
||||
struct ip6_eid_map_entry ip6_mappings[MAX_EID_MAPPINGS];
|
||||
|
||||
/* Don't do this, kids.
|
||||
* 2D array - [wrapping_rloc->id][unwrapping_rloc->id]
|
||||
* Obviously, half of the contexts would be identical. So some rules:
|
||||
* - if you're wrapping a packet, you are x. they are y
|
||||
* - if you're unwrapping a packet, you are y. they are x.
|
||||
* Half of the allocated memory goes unused, but we can worry about dynamic
|
||||
* allocation at the same time as MAX_RLOCS and MAX_EID_MAPPINGS
|
||||
*/
|
||||
struct key_context key_contexts[MAX_RLOCS][MAX_RLOCS];
|
||||
};
|
||||
|
||||
|
||||
@@ -62,8 +89,8 @@ int rloc_add_private_key( struct rloc *rloc, char *filename );
|
||||
void rlocs_debug_output( struct rlocs *reg );
|
||||
|
||||
/* Returns -1 on error, or number of bytes written */
|
||||
ssize_t rloc_encrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
|
||||
ssize_t rloc_decrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
|
||||
ssize_t rlocs_encrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
|
||||
ssize_t rlocs_decrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
|
||||
|
||||
void rlocs_free( struct rlocs *registry );
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#include "util.h"
|
||||
#include "rlocs.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
@@ -187,4 +186,22 @@ void session_teardown( struct session *session )
|
||||
if ( session->output_if >= 0 && !session->same_if ) {
|
||||
close( session->output_if );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: we can speed this one up, if necessary, by re-using the context.
|
||||
// TODO: some error-checking
|
||||
int sha256sum( unsigned char *src, size_t src_len, unsigned char dst[SHA256_DIGEST_LENGTH] )
|
||||
{
|
||||
unsigned int size = SHA256_DIGEST_LENGTH;
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
|
||||
EVP_DigestInit_ex( ctx, EVP_sha256(), NULL );
|
||||
|
||||
|
||||
EVP_DigestUpdate( ctx, src, src_len );
|
||||
EVP_DigestFinal_ex( ctx, &dst[0], &size );
|
||||
EVP_MD_CTX_destroy( ctx );
|
||||
|
||||
|
||||
return size == SHA256_DIGEST_LENGTH;
|
||||
}
|
@@ -1,10 +1,14 @@
|
||||
#ifndef _UTIL_H_
|
||||
#define _UTIL_H
|
||||
#define _UTIL_H_
|
||||
|
||||
#include "rlocs.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#define info(msg, ...) { fprintf( stdout, msg, ##__VA_ARGS__ ) ; fprintf( stdout, "\n" ); }
|
||||
#define warn(msg, ...) { fprintf( stderr, msg, ##__VA_ARGS__ ) ; fprintf( stderr, "\n" ); }
|
||||
|
||||
@@ -31,6 +35,8 @@ int session_setup( struct session *session, char *config_file, char *listen_if,
|
||||
int session_upgrade_rlocs( struct session *session, int argc, char** args );
|
||||
void session_teardown( struct session *session );
|
||||
|
||||
int sha256sum( unsigned char *src, size_t src_len, unsigned char dst[SHA256_DIGEST_LENGTH] );
|
||||
|
||||
|
||||
|
||||
#endif
|
@@ -23,8 +23,8 @@ int main(int argc, char** argv)
|
||||
ssize_t count;
|
||||
int result;
|
||||
|
||||
if ( argc < 4 ) {
|
||||
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname>", argv[0] );
|
||||
if ( argc < 6 ) {
|
||||
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname> <rloc> <keyfile> [<rloc> <keyfile>]n", argv[0] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user