diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2000-04-10 10:16:38 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2000-04-10 10:16:38 +0000 |
commit | 25f9f4858cc2fe31f36f18a1bfb88513e17e15d4 (patch) | |
tree | 82fd093607960714db117a6ca1cda98facd5df6a /lib/gnutls_dh.c | |
parent | 78975f75889530809b9bf5da5614eeff1214a15d (diff) | |
download | gnutls-25f9f4858cc2fe31f36f18a1bfb88513e17e15d4.tar.gz |
Added the basics for key exchange.
Diffstat (limited to 'lib/gnutls_dh.c')
-rw-r--r-- | lib/gnutls_dh.c | 618 |
1 files changed, 16 insertions, 602 deletions
diff --git a/lib/gnutls_dh.c b/lib/gnutls_dh.c index 754f363dc7..a593b1a669 100644 --- a/lib/gnutls_dh.c +++ b/lib/gnutls_dh.c @@ -1,7 +1,7 @@ #include <defines.h> #include <gcrypt.h> -static const byte diffie_hellman_group1_prime[130] = { 0x04, 0x00, +static const uint8 diffie_hellman_group1_prime[130] = { 0x04, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, @@ -14,18 +14,29 @@ static const byte diffie_hellman_group1_prime[130] = { 0x04, 0x00, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +#if 0 + --Example-- + you: X = g^x mod p; + peer: Y = g^y mod p; + + g = mpi_set_ui( NULL, 2 ); + /* generate our secret and the public value for it */ + X = calc_dh_secret( &x ); + /* now we can calculate the shared secret */ + key = calc_dh_key( Y, x ); + mpi_release( x ); +#endif /**************** * Choose a random value x and calculate e = g^x mod p. * Return: e and if ret_x is not NULL x. */ -static MPI -calc_dh_secret( MPI *ret_x ) +MPI calc_dh_secret( MPI *ret_x ) { MPI e, g, x, prime; size_t n = sizeof diffie_hellman_group1_prime; - if( gcry_mpi_scan( &prime, GCRYMPI_FMT_STD, + if( gcry_mpi_scan( &prime, GCRYMPI_FMT_STD, diffie_hellman_group1_prime, &n ) ) abort(); /*dump_mpi(stderr, "prime=", prime );*/ @@ -48,38 +59,7 @@ calc_dh_secret( MPI *ret_x ) } - -static void -hash_mpi( GCRY_MD_HD md, MPI a ) -{ - char buf[400]; - size_t n = sizeof buf; - - if( gcry_mpi_print( GCRYMPI_FMT_SSH, buf, &n, a ) ) - fprintf(stderr,"Oops: MPI too large for hashing\n"); - else - gcry_md_write( md, buf, n ); -} - - -static void -hash_bstring( GCRY_MD_HD md, BSTRING a ) -{ - byte buf[4]; - size_t n = a->len; - - buf[0] = n >> 24; - buf[1] = n >> 16; - buf[2] = n >> 8; - buf[3] = n; - gcry_md_write( md, buf, 4 ); - gcry_md_write( md, a->d, n ); -} - - - -static MPI -calc_dh_key( MPI f, MPI x ) +MPI calc_dh_key( MPI f, MPI x ) { MPI k, prime; size_t n = sizeof diffie_hellman_group1_prime; @@ -94,569 +74,3 @@ calc_dh_key( MPI f, MPI x ) return k; } - - - -/**************** - * calculate the exchange hash value and put it into the handle. - */ -static int -calc_exchange_hash( GSTIHD hd, BSTRING i_c, BSTRING i_s, - BSTRING k_s, MPI e, MPI f ) -{ - GCRY_MD_HD md; - int algo = GCRY_MD_SHA1; - - md = gcry_md_open( algo, 0 ); - if( !md ) - return map_gcry_rc( gcry_errno() ); - - if( hd->we_are_server ) { - BSTRING pp; - hash_bstring( md, hd->peer_version_string ); - pp = make_bstring( host_version_string, strlen(host_version_string) ); - hash_bstring( md, pp ); - gsti_free(pp); - } - else { - BSTRING pp; - pp = make_bstring( host_version_string, strlen(host_version_string) ); - hash_bstring( md, pp ); - gsti_free(pp); - hash_bstring( md, hd->peer_version_string ); - } - hash_bstring( md, i_c ); - hash_bstring( md, i_s ); - hash_bstring( md, k_s ); - hash_mpi( md, e ); - hash_mpi( md, f ); - hash_mpi( md, hd->kex.k ); - - hd->kex.h = make_bstring( gcry_md_read( md, algo ), - gcry_md_get_algo_dlen(algo) ); - if( !hd->session_id ) /* initialize the session id the first time */ - hd->session_id = make_bstring( gcry_md_read( md, algo ), - gcry_md_get_algo_dlen(algo)); - gcry_md_close( md ); -/* dump_hexbuf( stderr, "SesID=", hd->session_id->d, hd->session_id->len );*/ - return 0; -} - - -/* Hmm. We need to have a new_kex structure so that the old - * kex data can be used untip we have send the NEWKEYs msg - * Well, doesn't matter for now. - */ -static BSTRING -construct_one_key( GSTIHD hd, GCRY_MD_HD md1, int algo, - const byte *letter, size_t size ) -{ - BSTRING hash; - GCRY_MD_HD md = gcry_md_copy( md1 ); - size_t n, n1; - - hash = make_bstring( NULL, size ); - gcry_md_write( md, letter, 1 ); - gcry_md_write( md, hd->session_id->d, hd->session_id->len ); - n = gcry_md_get_algo_dlen(algo); - if( n > size ) - n = size; - memcpy( hash->d, gcry_md_read(md, algo), n ); - while( n < size ) { - gcry_md_close( md ); - md = gcry_md_copy( md1 ); - gcry_md_write( md, hash->d, n ); - n1 = gcry_md_get_algo_dlen(algo); - if( n1 > size-n ) - n1 = size-n; - memcpy( hash->d+n, gcry_md_read(md, algo), n1 ); - n += n1; - } - gcry_md_close( md ); - - return hash; -} - - -static int -construct_keys( GSTIHD hd ) -{ - GCRY_MD_HD md; - int algo = GCRY_MD_SHA1; - - if( hd->kex.key_a ) - return 0; /* already constructed */ - - md = gcry_md_open( algo, 0 ); - if( !md ) - return map_gcry_rc( gcry_errno() ); - - hash_mpi( md, hd->kex.k ); - gcry_md_write( md, hd->kex.h->d, hd->kex.h->len ); - - hd->kex.key_a = construct_one_key( hd, md, algo, "\x41", 8 ); - hd->kex.key_b = construct_one_key( hd, md, algo, "\x42", 8 ); - hd->kex.key_c = construct_one_key( hd, md, algo, "\x43", 24 ); /* des*/ - hd->kex.key_d = construct_one_key( hd, md, algo, "\x44", 24 ); - hd->kex.key_e = construct_one_key( hd, md, algo, "\x45", 20 ); - hd->kex.key_f = construct_one_key( hd, md, algo, "\x46", 20 ); - gcry_md_close( md ); - #if 0 - dump_hexbuf( stderr, "key A=", hd->kex.key_a->d, hd->kex.key_a->len ); - dump_hexbuf( stderr, "key B=", hd->kex.key_b->d, hd->kex.key_b->len ); - dump_hexbuf( stderr, "key C=", hd->kex.key_c->d, hd->kex.key_c->len ); - dump_hexbuf( stderr, "key D=", hd->kex.key_d->d, hd->kex.key_d->len ); - dump_hexbuf( stderr, "key E=", hd->kex.key_e->d, hd->kex.key_e->len ); - dump_hexbuf( stderr, "key F=", hd->kex.key_f->d, hd->kex.key_f->len ); - #endif - return 0; -} - - -static int -prepare_hmac_pad( GCRY_MD_HD *rhd, BSTRING key, int pad ) -{ - int algo = GCRY_MD_SHA1; - int i; - byte buf[64]; /* fixme */ - - assert( key->len <= sizeof buf ); - *rhd = gcry_md_open( algo, 0 ); - if( !*rhd ) - return map_gcry_rc( gcry_errno() ); - - memset( buf, 0, sizeof buf ); - memcpy( buf, key->d, key->len ); - for(i=0; i < sizeof buf; i++ ) - buf[i] ^= pad; - gcry_md_write( *rhd, buf, sizeof buf ); - return 0; -} - -/**************** - * Prepare a HMAC - */ -static int -prepare_mac( GCRY_MD_HD *inner_hd, GCRY_MD_HD *outer_hd, BSTRING key ) -{ - int rc; - - rc = prepare_hmac_pad( inner_hd, key, '\x36'); - if( !rc ) - rc = prepare_hmac_pad( outer_hd, key, '\x5c'); - return rc; -} - - -/* fixme: must release some data in case of error or when at end */ -int -kex_send_init_packet( GSTIHD hd ) -{ - MSG_kexinit kex; - int rc=0; - - /* first send our kexinit packet */ - memset( &kex, 0, sizeof kex ); - memset( kex.cookie, 'w', 16 ); /* fixme: send a random one */ - kex.kex_algorithm = insert_strlist( NULL, "diffie-hellman-group1-sha1" ); - kex.server_host_key_algorithms = insert_strlist( NULL, "ssh-dss"); - kex.encryption_algorithms_client_to_server = insert_strlist( NULL, "3des-cbc"); - kex.encryption_algorithms_server_to_client = insert_strlist( NULL, "3des-cbc"); - kex.mac_algorithms_client_to_server = insert_strlist( NULL, "hmac-sha1"); - kex.mac_algorithms_server_to_client = insert_strlist( NULL, "hmac-sha1"); - kex.compression_algorithms_client_to_server = insert_strlist( NULL, "none"); - kex.compression_algorithms_server_to_client = insert_strlist( NULL, "none"); - rc = build_msg_kexinit( &kex, &hd->pkt ); - if( rc ) - return rc; - rc = write_packet( hd ); - if( rc ) - return rc; - /* must do it here because write_packet fills in the packet type */ - hd->host_kexinit_data = make_bstring( hd->pkt.payload, hd->pkt.payload_len ); - rc = flush_packet( hd ); - return rc; -} - - -/**************** - * Process a received keyinit packet. - */ -int -kex_proc_init_packet( GSTIHD hd ) -{ - MSG_kexinit kex; - int rc; - - if( hd->pkt.type != SSH_MSG_KEXINIT ) - return GSTI_BUG; /* oops */ - rc = parse_msg_kexinit( &kex, hd->pkt.payload, hd->pkt.payload_len ); - if( rc ) - return rc; - /* make a copy of the received payload which we will need later */ - hd->peer_kexinit_data = make_bstring( hd->pkt.payload, - hd->pkt.payload_len ); - - dump_msg_kexinit( &kex ); - return 0; -} - - -/**************** - * Send a KEX init packet (we are in the client role) - */ -int -kex_send_kexdh_init( GSTIHD hd ) -{ - MSG_kexdh_init kexdh; - int rc=0; - - memset( &kexdh, 0, sizeof kexdh ); - /* FIXME: move secret_x it to secure memory */ - kexdh.e = hd->kexdh_e = calc_dh_secret( &hd->secret_x ); - rc = build_msg_kexdh_init( &kexdh, &hd->pkt ); - if( rc ) - return rc; - rc = write_packet( hd ); - if( rc ) - return rc; - rc = flush_packet( hd ); - return rc; -} - - -/**************** - * Process the received DH init (we are in the server role) - */ -int -kex_proc_kexdh_init( GSTIHD hd ) -{ - int rc; - MSG_kexdh_init kexdh; - - if( hd->pkt.type != SSH_MSG_KEXDH_INIT ) - return GSTI_BUG; /* oops */ - - rc = parse_msg_kexdh_init( &kexdh, hd->pkt.payload, hd->pkt.payload_len ); - if( rc ) - return rc; - - /* we need the received e later */ - hd->kexdh_e = kexdh.e; - - dump_msg_kexdh_init( &kexdh ); - return 0; -} - - -/**************** - * Send a DH init packet (we are in the server role) - */ -int -kex_send_kexdh_reply( GSTIHD hd ) -{ - MSG_kexdh_reply dhr; - int rc; - MPI y; - - memset( &dhr, 0, sizeof dhr ); - dhr.k_s = make_bstring( "servers-pk", 10 ); /* fixme: Get from a DB */ - - /* generate our secret and the public value for it */ - dhr.f = calc_dh_secret( &y ); - /* now we can calculate the shared secret */ - hd->kex.k = calc_dh_key( hd->kexdh_e, y ); - mpi_release( y ); - /* and the hash */ - rc = calc_exchange_hash( hd, hd->host_kexinit_data, hd->peer_kexinit_data, - dhr.k_s, hd->kexdh_e, dhr.f ); - mpi_release( hd->kexdh_e ); - if( rc ) - return rc; - dhr.sig_h = make_bstring("signature_of_H", 14 ); /*FIXME*/ - - rc = build_msg_kexdh_reply( &dhr, &hd->pkt ); - if( !rc ) - dump_msg_kexdh_reply( &dhr ); - if( !rc ) - rc = write_packet( hd ); - if( !rc ) - rc = flush_packet( hd ); - return rc; -} - -/**************** - * Process the received DH value and take the encryption kes into use. - * (we are in the client role) - */ -int -kex_proc_kexdh_reply( GSTIHD hd ) -{ - int rc; - MSG_kexdh_reply dhr; - - if( hd->pkt.type != SSH_MSG_KEXDH_REPLY ) - return GSTI_BUG; /* oops */ - - rc = parse_msg_kexdh_reply( &dhr, hd->pkt.payload, hd->pkt.payload_len ); - if( rc ) - return rc; - - dump_msg_kexdh_reply( &dhr ); - - hd->kex.k = calc_dh_key( dhr.f, hd->secret_x ); - mpi_release( hd->secret_x ); - - rc = calc_exchange_hash( hd, hd->host_kexinit_data, hd->peer_kexinit_data, - dhr.k_s, hd->kexdh_e, dhr.f ); - mpi_release( hd->kexdh_e ); - if( rc ) - return rc; - - /* FIXME: check that this is the real host */ - - return rc; -} - - -int -kex_send_newkeys( GSTIHD hd ) -{ - int rc; - - rc = construct_keys( hd ); - if( rc ) - return rc; - - hd->pkt.type = SSH_MSG_NEWKEYS; - hd->pkt.payload_len = 1; - rc = write_packet( hd ); - if( !rc ) - rc = flush_packet( hd ); - if( rc ) - return rc; - - /* now we have to take the encryption keys into use */ - hd->encrypt_hd = gcry_cipher_open( GCRY_CIPHER_3DES, - GCRY_CIPHER_MODE_CBC, 0 ); - if( !hd->encrypt_hd ) - rc = map_gcry_rc( gcry_errno() ); - else if( hd->we_are_server ) { - if( !rc ) - rc = gcry_cipher_setkey( hd->encrypt_hd, hd->kex.key_d->d, - hd->kex.key_d->len ); - if( !rc ) - rc = gcry_cipher_setiv( hd->encrypt_hd, hd->kex.key_b->d, - hd->kex.key_b->len ); - rc = map_gcry_rc(rc); - if( !rc ) - rc = prepare_mac( &hd->send_mac_inner_hd, &hd->send_mac_outer_hd, - hd->kex.key_f ); - } - else { - if( !rc ) - rc = gcry_cipher_setkey( hd->encrypt_hd, hd->kex.key_c->d, - hd->kex.key_c->len ); - if( !rc ) - rc = gcry_cipher_setiv( hd->encrypt_hd, hd->kex.key_a->d, - hd->kex.key_a->len ); - rc = map_gcry_rc(rc); - if( !rc ) - rc = prepare_mac( &hd->send_mac_inner_hd, &hd->send_mac_outer_hd, - hd->kex.key_e ); - } - if( rc ) - return debug_rc(rc,"setup encryption keys failed"); - return rc; -} - -/**************** - * Process a received newkeys message and take the decryption keys in use - */ -int -kex_proc_newkeys( GSTIHD hd ) -{ - int rc; - - if( hd->pkt.type != SSH_MSG_NEWKEYS ) - return GSTI_BUG; /* ooops */ - - rc = construct_keys( hd ); - if( rc ) - return rc; - - hd->decrypt_hd = gcry_cipher_open( GCRY_CIPHER_3DES, - GCRY_CIPHER_MODE_CBC, 0 ); - if( !hd->encrypt_hd ) - rc = map_gcry_rc( gcry_errno() ); - else if( hd->we_are_server ) { - if( !rc ) - rc = gcry_cipher_setkey( hd->decrypt_hd, hd->kex.key_c->d, - hd->kex.key_c->len ); - if( !rc ) - rc = gcry_cipher_setiv( hd->decrypt_hd, hd->kex.key_a->d, - hd->kex.key_a->len ); - rc = map_gcry_rc(rc); - if( !rc ) - rc = prepare_mac( &hd->recv_mac_inner_hd, &hd->recv_mac_outer_hd, - hd->kex.key_e ); - } - else { - if( !rc ) - rc = gcry_cipher_setkey( hd->decrypt_hd, hd->kex.key_d->d, - hd->kex.key_d->len ); - if( !rc ) - rc = gcry_cipher_setiv( hd->decrypt_hd, hd->kex.key_b->d, - hd->kex.key_b->len ); - rc = map_gcry_rc(rc); - if( !rc ) - rc = prepare_mac( &hd->recv_mac_inner_hd, &hd->recv_mac_outer_hd, - hd->kex.key_f ); - } - - if( rc ) - return debug_rc(rc,"setup decryption keys failed"); - return rc; -} - - - -/**************** - * Parse a SSH_MSG_SERVICE_{ACCEPT,REQUEST} and return the service name. - * Returns: 0 on success or an errorcode. - */ -static int -parse_msg_service( BSTRING *svcname, const byte *msg, size_t msglen, int type ) -{ - size_t n; - int rc = 0; - - *svcname = NULL; - if( msglen < (1+4) ) - return GSTI_TOO_SHORT; - if( *msg != type ) - return GSTI_BUG; - msg++; msglen--; - - n = msglen; - *svcname = parse_bstring( msg, &n ); - if( !*svcname ) - return GSTI_TOO_SHORT; - msg += n; msglen -= n; - - /* make sure the msg length matches */ - if( msglen ) { - fprintf(stderr,"parse_msg_service: hmmm, %lu bytes remaining\n", - (ulong)msglen ); - } - return rc; -} - -/**************** - * Build a SERVICE_{Accept,REQUEST} packet. - */ -static int -build_msg_service( BSTRING svcname, struct packet_buffer_s *pkt, int type ) -{ - size_t n; - byte *p = pkt->payload; - size_t length = pkt->size; - - assert( pkt->size > 100 ); - if( !svcname ) { - fprintf(stderr,"build_msg_service: no service name\n"); - return GSTI_BUG; - } - - pkt->type = type; - p++; pkt->payload_len = 1; - - n = build_bstring( p, length, svcname ); - if( !n ) - return GSTI_TOO_SHORT; /* provided buffer is too short */ - p += n; length -= n; pkt->payload_len += n; - return 0; -} - - - -int -kex_send_service_request( GSTIHD hd, const char *name ) -{ - int rc; - - hd->service_name = make_bstring( name, strlen(name) ); - rc = build_msg_service( hd->service_name, &hd->pkt,SSH_MSG_SERVICE_REQUEST); - if( !rc ) - rc = write_packet( hd ); - if( !rc ) - rc = flush_packet( hd ); - - if( rc ) { - gsti_free( hd->service_name ); - hd->service_name = NULL; - } - return rc; -} - -int -kex_proc_service_request( GSTIHD hd ) -{ - int rc; - BSTRING svcname; - - if( hd->pkt.type != SSH_MSG_SERVICE_REQUEST ) - return GSTI_BUG; /* oops */ - rc = parse_msg_service( &svcname, hd->pkt.payload, hd->pkt.payload_len, - SSH_MSG_SERVICE_REQUEST ); - if( rc ) - return rc; - - /* FIXME: here we should whether we provide this service */ - - /* store the servicename, so that it can later be answered */ - if( hd->service_name ) - return debug_rc( GSTI_BUG, "a service is already in use"); - - hd->service_name = svcname; - return rc; -} - - - -int -kex_send_service_accept( GSTIHD hd ) -{ - int rc; - - rc = build_msg_service( hd->service_name, &hd->pkt, SSH_MSG_SERVICE_ACCEPT); - if( !rc ) - rc = write_packet( hd ); - if( !rc ) - rc = flush_packet( hd ); - return rc; -} - -int -kex_proc_service_accept( GSTIHD hd ) -{ - int rc; - BSTRING svcname; - - if( hd->pkt.type != SSH_MSG_SERVICE_ACCEPT ) - return GSTI_BUG; /* oops */ - rc = parse_msg_service( &svcname, hd->pkt.payload, hd->pkt.payload_len, - SSH_MSG_SERVICE_ACCEPT ); - if( rc ) - return rc; - - if( !hd->service_name ) - return debug_rc( GSTI_BUG, "no service request sent"); - rc = cmp_bstring( hd->service_name, svcname ); - gsti_free( svcname ); - if( rc ) - return debug_rc( GSTI_PROT_VIOL, - "service name does not match requested one" ); - return 0; -} - |