diff --git a/.gitignore b/.gitignore index 04e72fc..0d7843a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ pass-1/wrapper pass-1/unwrapper pass-1/hide-eid pass-1/example-keys +README.html diff --git a/README.md b/README.md index 96ad41d..9b2c929 100644 --- a/README.md +++ b/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 diff --git a/pass-1/Makefile b/pass-1/Makefile index 45b18c4..8d1a32f 100644 --- a/pass-1/Makefile +++ b/pass-1/Makefile @@ -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 diff --git a/pass-1/packet.c b/pass-1/packet.c index 05205f1..b43832e 100644 --- a/pass-1/packet.c +++ b/pass-1/packet.c @@ -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!" ); diff --git a/pass-1/rloc-registry.json b/pass-1/rloc-registry.json index 3073ad9..3995a19 100644 --- a/pass-1/rloc-registry.json +++ b/pass-1/rloc-registry.json @@ -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-----" } diff --git a/pass-1/rlocs.c b/pass-1/rlocs.c index 1e5f91c..75d1012 100644 --- a/pass-1/rlocs.c +++ b/pass-1/rlocs.c @@ -17,30 +17,41 @@ #include #include +#include +#include +#include + #include #include #include -#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. diff --git a/pass-1/rlocs.h b/pass-1/rlocs.h index 7d664b3..355dbcc 100644 --- a/pass-1/rlocs.h +++ b/pass-1/rlocs.h @@ -1,15 +1,30 @@ #ifndef _RLOCS_H_ #define _RLOCS_H_ +#include "util.h" + #include #include -#include +#include +#include // 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 ); diff --git a/pass-1/util.c b/pass-1/util.c index 7eb501f..82c6016 100644 --- a/pass-1/util.c +++ b/pass-1/util.c @@ -1,5 +1,4 @@ #include "util.h" -#include "rlocs.h" #include #include @@ -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; } \ No newline at end of file diff --git a/pass-1/util.h b/pass-1/util.h index 7bcfcd8..9248504 100644 --- a/pass-1/util.h +++ b/pass-1/util.h @@ -1,10 +1,14 @@ #ifndef _UTIL_H_ -#define _UTIL_H +#define _UTIL_H_ + +#include "rlocs.h" #include #include #include +#include + #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 \ No newline at end of file diff --git a/pass-1/wrapper.c b/pass-1/wrapper.c index f3a1f22..0724912 100644 --- a/pass-1/wrapper.c +++ b/pass-1/wrapper.c @@ -23,8 +23,8 @@ int main(int argc, char** argv) ssize_t count; int result; - if ( argc < 4 ) { - warn( "Usage: %s ", argv[0] ); + if ( argc < 6 ) { + warn( "Usage: %s [ ]n", argv[0] ); return 1; }