From 1eeb3f65fc32a517ddea77c47401f9780b7d4a59 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 May 2003 19:42:51 +0000 Subject: [PATCH] Implement core of onion-skin-based handshake svn:r259 --- src/or/onion.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ src/or/test.c | 2 +- 2 files changed, 142 insertions(+), 1 deletion(-) diff --git a/src/or/onion.c b/src/or/onion.c index 3ccb99711b..4196676514 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -833,6 +833,147 @@ onion_unpack(onion_layer_t *dest, char *src) inet_ntoa(*((struct in_addr *)(src+3))), dest->expire); } +/*----------------------------------------------------------------------*/ + +/* Given a router's public key, generates a 208-byte encrypted DH pubkey, + * and stores it into onion_skin out. Stores the DH private key into + * handshake_state_out for later completion of the handshake. + * + * The encrypted pubkey is formed as follows: + * 16 bytes of symmetric key + * 192 bytes of g^x for DH. + * The first 128 bytes are RSA-encrypted with the server's public key, + * and the last 80 are encrypted with the symmetric key. + */ +int +onion_skin_create(crypto_pk_env_t *router_key, + crypto_dh_env_t **handshake_state_out, + char *onion_skin_out) /* Must be 208 bytes long */ +{ + char iv[16]; + char *pubkey = NULL; + crypto_dh_env_t *dh = NULL; + crypto_cipher_env_t *cipher = NULL; + int dhbytes, pkbytes; + + *handshake_state_out = NULL; + memset(onion_skin_out, 0, 208); + memset(iv, 0, 16); /* XXXX This can't be safe, can it? */ + + if (!(dh = crypto_dh_new())) + goto err; + + dhbytes = crypto_dh_get_bytes(dh); + pkbytes = crypto_pk_keysize(router_key); + assert(dhbytes+16 == 208); + if (!(pubkey = malloc(dhbytes))) + goto err; + + if (crypto_rand(16, pubkey)) + goto err; + + if (crypto_dh_get_public(dh, pubkey+16, dhbytes)) + goto err; + + if (crypto_pk_public_encrypt(router_key, pubkey, pkbytes, + onion_skin_out, RSA_NO_PADDING)) + goto err; + + cipher = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, pubkey, iv, 1); + + if (crypto_cipher_encrypt(cipher, pubkey+pkbytes, dhbytes-16-pkbytes, + onion_skin_out+pkbytes)) + goto err; + + free(pubkey); + crypto_free_cipher_env(cipher); + *handshake_state_out = dh; + + return 0; + err: + if (pubkey) free(pubkey); + if (dh) crypto_dh_free(dh); + if (cipher) crypto_free_cipher_env(cipher); + return -1; +} + +/* Given an encrypted DH public key as generated by onion_skin_create, + * and the private key for this onion router, generate the 192-byte DH + * reply, and key_out_len bytes of key material, stored in key_out. + */ +int +onion_skin_server_handshake(char *onion_skin, /* 208 bytes long */ + crypto_pk_env_t *private_key, + char *handshake_reply_out, /* 192 bytes long */ + char *key_out, + int key_out_len) +{ + char buf[208]; + char iv[16]; + crypto_dh_env_t *dh = NULL; + crypto_cipher_env_t *cipher = NULL; + int pkbytes; + + memset(iv, 0, 16); + pkbytes = crypto_pk_keysize(private_key); + + if (crypto_pk_private_decrypt(private_key, + onion_skin, pkbytes, + buf, RSA_NO_PADDING)) + goto err; + + cipher = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, buf, iv, 0); + + if (crypto_cipher_decrypt(cipher, onion_skin+pkbytes, 208-pkbytes, + buf+pkbytes)) + goto err; + + dh = crypto_dh_new(); + if (crypto_dh_get_public(dh, handshake_reply_out, 192)) + goto err; + + if (crypto_dh_compute_secret(dh, buf+16, 192, buf)) + goto err; + + memcpy(key_out, buf+192-key_out_len, key_out_len); + + crypto_free_cipher_env(cipher); + crypto_dh_free(dh); + return 0; + err: + if (cipher) crypto_free_cipher_env(cipher); + if (dh) crypto_dh_free(dh); + + return -1; +} + +/* Finish the client side of the DH handshake. + * Given the 192 byte DH reply as generated by onion_skin_server_handshake + * and the handshake state generated by onion_skin_create, generate + * key_out_len bytes of shared key material and store them in key_out. + * + * After the invocation, call crypto_dh_free on handshake_state. + */ +int +onion_skin_client_handshake(crypto_dh_env_t *handshake_state, + char *handshake_reply,/* Must be 192 bytes long*/ + char *key_out, + int key_out_len) +{ + char key_material[192]; + assert(crypto_dh_get_bytes(handshake_state) == 192); + + memset(key_material, 0, 192); + + if (crypto_dh_compute_secret(handshake_state, handshake_reply, 192, + key_material)) + return -1; + + memcpy(key_out, key_material+192-key_out_len, key_out_len); + + return 0; +} + /* Local Variables: mode:c diff --git a/src/or/test.c b/src/or/test.c index 17ad0ecf6b..81d205eb9d 100644 --- a/src/or/test.c +++ b/src/or/test.c @@ -475,7 +475,7 @@ main(int c, char**v) { test_buffers(); puts("========================== Crypto =========================="); test_crypto_dh(); - test_crypto(); /* this seg faults :( */ + test_crypto(); /* this seg faults :( */ /* Still? -NM 2003/04/30 */ puts("\n========================== Util ============================"); test_util(); puts("");