00001 /* 00002 * Diffie-Hellman-Merkle key exchange (client side) 00003 * 00004 * Copyright (C) 2006-2007 Christophe Devine 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License along 00017 * with this program; if not, write to the Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00019 */ 00020 00021 #ifndef _CRT_SECURE_NO_DEPRECATE 00022 #define _CRT_SECURE_NO_DEPRECATE 1 00023 #endif 00024 00025 #include <string.h> 00026 #include <stdio.h> 00027 00028 #include "xyssl/net.h" 00029 #include "xyssl/aes.h" 00030 #include "xyssl/dhm.h" 00031 #include "xyssl/rsa.h" 00032 #include "xyssl/sha1.h" 00033 #include "xyssl/havege.h" 00034 00035 #define SERVER_NAME "localhost" 00036 #define SERVER_PORT 11999 00037 00038 int main( void ) 00039 { 00040 FILE *f; 00041 00042 int ret, n, buflen; 00043 int server_fd = -1; 00044 00045 unsigned char *p, *end; 00046 unsigned char buf[1024]; 00047 unsigned char hash[20]; 00048 00049 havege_state hs; 00050 rsa_context rsa; 00051 dhm_context dhm; 00052 aes_context aes; 00053 00054 memset( &rsa, 0, sizeof( rsa ) ); 00055 memset( &dhm, 0, sizeof( dhm ) ); 00056 00057 /* 00058 * 1. Setup the RNG 00059 */ 00060 printf( "\n . Seeding the random number generator" ); 00061 fflush( stdout ); 00062 00063 havege_init( &hs ); 00064 00065 /* 00066 * 2. Read the server's public RSA key 00067 */ 00068 printf( "\n . Reading public key from rsa_pub.txt" ); 00069 fflush( stdout ); 00070 00071 if( ( f = fopen( "rsa_pub.txt", "rb" ) ) == NULL ) 00072 { 00073 ret = 1; 00074 printf( " failed\n ! Could not open rsa_pub.txt\n" \ 00075 " ! Please run rsa_genkey first\n\n" ); 00076 goto exit; 00077 } 00078 00079 rsa_init( &rsa, RSA_PKCS_V15, 0, NULL, NULL ); 00080 00081 if( ( ret = mpi_read_file( &rsa.N, 16, f ) ) != 0 || 00082 ( ret = mpi_read_file( &rsa.E, 16, f ) ) != 0 ) 00083 { 00084 printf( " failed\n ! mpi_read_file returned %d\n\n", ret ); 00085 goto exit; 00086 } 00087 00088 rsa.len = ( mpi_msb( &rsa.N ) + 7 ) >> 3; 00089 00090 fclose( f ); 00091 00092 /* 00093 * 3. Initiate the connection 00094 */ 00095 printf( "\n . Connecting to tcp/%s/%d", SERVER_NAME, 00096 SERVER_PORT ); 00097 fflush( stdout ); 00098 00099 if( ( ret = net_connect( &server_fd, SERVER_NAME, 00100 SERVER_PORT ) ) != 0 ) 00101 { 00102 printf( " failed\n ! net_connect returned %d\n\n", ret ); 00103 goto exit; 00104 } 00105 00106 /* 00107 * 4a. First get the buffer length 00108 */ 00109 printf( "\n . Receiving the server's DH parameters" ); 00110 fflush( stdout ); 00111 00112 memset( buf, 0, sizeof( buf ) ); 00113 00114 if( ( ret = net_recv( &server_fd, buf, 2 ) ) != 2 ) 00115 { 00116 printf( " failed\n ! net_recv returned %d\n\n", ret ); 00117 goto exit; 00118 } 00119 00120 n = buflen = ( buf[0] << 8 ) | buf[1]; 00121 if( buflen < 1 || buflen > (int) sizeof( buf ) ) 00122 { 00123 printf( " failed\n ! Got an invalid buffer length\n\n" ); 00124 goto exit; 00125 } 00126 00127 /* 00128 * 4b. Get the DHM parameters: P, G and Ys = G^Xs mod P 00129 */ 00130 memset( buf, 0, sizeof( buf ) ); 00131 00132 if( ( ret = net_recv( &server_fd, buf, n ) ) != n ) 00133 { 00134 printf( " failed\n ! net_recv returned %d\n\n", ret ); 00135 goto exit; 00136 } 00137 00138 p = buf, end = buf + buflen; 00139 00140 if( ( ret = dhm_read_params( &dhm, &p, end ) ) != 0 ) 00141 { 00142 printf( " failed\n ! dhm_read_params returned %d\n\n", ret ); 00143 goto exit; 00144 } 00145 00146 if( dhm.len < 64 || dhm.len > 256 ) 00147 { 00148 ret = 1; 00149 printf( " failed\n ! Invalid DHM modulus size\n\n" ); 00150 goto exit; 00151 } 00152 00153 /* 00154 * 5. Check that the server's RSA signature matches 00155 * the SHA-1 hash of (P,G,Ys) 00156 */ 00157 printf( "\n . Verifying the server's RSA signature" ); 00158 fflush( stdout ); 00159 00160 if( ( n = (int)( end - p ) ) != rsa.len ) 00161 { 00162 ret = 1; 00163 printf( " failed\n ! Invalid RSA signature size\n\n" ); 00164 goto exit; 00165 } 00166 00167 sha1( buf, (int)( p - 2 - buf ), hash ); 00168 00169 if( ( ret = rsa_pkcs1_verify( &rsa, RSA_PUBLIC, RSA_SHA1, 00170 0, hash, p ) ) != 0 ) 00171 { 00172 printf( " failed\n ! rsa_pkcs1_verify returned %d\n\n", ret ); 00173 goto exit; 00174 } 00175 00176 /* 00177 * 6. Send our public value: Yc = G ^ Xc mod P 00178 */ 00179 printf( "\n . Sending own public value to server" ); 00180 fflush( stdout ); 00181 00182 n = dhm.len; 00183 if( ( ret = dhm_make_public( &dhm, 256, buf, n, 00184 havege_rand, &hs ) ) != 0 ) 00185 { 00186 printf( " failed\n ! dhm_make_public returned %d\n\n", ret ); 00187 goto exit; 00188 } 00189 00190 if( ( ret = net_send( &server_fd, buf, n ) ) != n ) 00191 { 00192 printf( " failed\n ! net_send returned %d\n\n", ret ); 00193 goto exit; 00194 } 00195 00196 /* 00197 * 7. Derive the shared secret: K = Ys ^ Xc mod P 00198 */ 00199 printf( "\n . Shared secret: " ); 00200 fflush( stdout ); 00201 00202 n = dhm.len; 00203 if( ( ret = dhm_calc_secret( &dhm, buf, &n ) ) != 0 ) 00204 { 00205 printf( " failed\n ! dhm_calc_secret returned %d\n\n", ret ); 00206 goto exit; 00207 } 00208 00209 for( n = 0; n < 16; n++ ) 00210 printf( "%02x", buf[n] ); 00211 00212 /* 00213 * 8. Setup the AES-256 decryption key 00214 * 00215 * This is an overly simplified example; best practice is 00216 * to hash the shared secret with a random value to derive 00217 * the keying material for the encryption/decryption keys, 00218 * IVs and MACs. 00219 */ 00220 printf( "...\n . Receiving and decrypting the ciphertext" ); 00221 fflush( stdout ); 00222 00223 aes_setkey_dec( &aes, buf, 256 ); 00224 00225 memset( buf, 0, sizeof( buf ) ); 00226 00227 if( ( ret = net_recv( &server_fd, buf, 16 ) ) != 16 ) 00228 { 00229 printf( " failed\n ! net_recv returned %d\n\n", ret ); 00230 goto exit; 00231 } 00232 00233 aes_crypt_ecb( &aes, AES_DECRYPT, buf, buf ); 00234 buf[16] = '0円'; 00235 printf( "\n . Plaintext is \"%s\"\n\n", (char *) buf ); 00236 00237 exit: 00238 00239 net_close( server_fd ); 00240 rsa_free( &rsa ); 00241 dhm_free( &dhm ); 00242 00243 #ifdef WIN32 00244 printf( " + Press Enter to exit this program.\n" ); 00245 fflush( stdout ); getchar(); 00246 #endif 00247 00248 return( ret ); 00249 }