static int send_handshake_message( int connection, int msg_type, const unsigned char *message, int message_len, TLSParameters *parameters ) { Handshake record; short send_buffer_size; unsigned char *send_buffer; int response; record.msg_type = msg_type; record.length = htons( message_len ) << 8; // To deal with 24-bits... send_buffer_size = message_len + 4; // space for the handshake header send_buffer = ( unsigned char * ) malloc( send_buffer_size ); send_buffer[ 0 ] = record.msg_type; memcpy( send_buffer + 1, &record.length, 3 ); memcpy( send_buffer + 4, message, message_len ); update_digest( ¶meters->md5_handshake_digest, send_buffer, send_buffer_size ); update_digest( ¶meters->sha1_handshake_digest, send_buffer, send_buffer_size ); response = send_message( connection, content_handshake, send_buffer, send_buffer_size, ¶meters->active_send_parameters ); free( send_buffer ); return response; }
static void update_digest_line(HASH_TYPE hash, bool pass, digest_line_t *dline, uint32_t crc, int c) { if (!c) dline->count[0]++; dline->chksum[c][0] = update_digest(hash, dline->chksum[c][0], crc); if (pass) { if (!c) dline->count[1]++; dline->chksum[c][1] = update_digest(hash, dline->chksum[c][1], crc); } }
/* Calculates the digest of a file, if needed. */ static int get_file_digest(File* file) { FILE* stream; size_t size; char buffer[BUFFER_SIZE]; if (file->status == HASHED) return 0; init_digest(); if (file->status == SAMPLED && file->size <= SAMPLE_SIZE) update_digest(file->sample, file->size); else if (file->size > 0) { stream = fopen(file->path, "rb"); if (!stream) { if (!quiet_flag) warning("%s: %s", file->path, strerror(errno)); file->status = INVALID; return -1; } for (;;) { size = fread(buffer, 1, sizeof(buffer), stream); if (ferror(stream)) { if (!quiet_flag) warning("%s: %s", file->path, strerror(errno)); fclose(stream); file->status = INVALID; return -1; } if (size == 0) break; update_digest(buffer, size); } fclose(stream); } file->digest = malloc(get_digest_size()); finish_digest(file->digest); file->status = HASHED; return 0; }
int parse_pkcs8_private_key( rsa_key *privkey, const unsigned char *buffer, int buffer_length, const unsigned char *passphrase ) { struct asn1struct pkcs8_key; struct asn1struct private_key; struct asn1struct *encryptionId; struct asn1struct *salt; struct asn1struct *iteration_count; struct asn1struct *encrypted_key; struct asn1struct *key_type_oid; struct asn1struct *priv_key_data; digest_ctx initial_hash; int counter; unsigned char passphrase_hash_in[ MD5_RESULT_SIZE * sizeof( int ) ]; unsigned char passphrase_hash_out[ MD5_RESULT_SIZE * sizeof( int ) ]; unsigned char *decrypted_key; asn1parse( buffer, buffer_length, &pkcs8_key ); encryptionId = pkcs8_key.children->children; if ( memcmp( OID_pbeWithMD5andDES_CBC, encryptionId->data, encryptionId->length ) ) { fprintf( stderr, "Unsupported key encryption algorithm\n" ); asn1free( &pkcs8_key ); return 1; } // TODO support more algorithms salt = encryptionId->next->children; iteration_count = salt->next; encrypted_key = pkcs8_key.children->next; // ugly typecasting counter = ntohs( *iteration_count->data ); new_md5_digest( &initial_hash ); update_digest( &initial_hash, passphrase, strlen( passphrase ) ); update_digest( &initial_hash, salt->data, salt->length ); finalize_digest( &initial_hash ); memcpy( passphrase_hash_out, initial_hash.hash, initial_hash.hash_len * sizeof( int ) ); while ( --counter ) { memcpy( passphrase_hash_in, passphrase_hash_out, sizeof( int ) * MD5_RESULT_SIZE ); md5_hash( passphrase_hash_in, sizeof( int ) * MD5_RESULT_SIZE, ( unsigned int * ) passphrase_hash_out ); } decrypted_key = ( unsigned char * ) malloc( encrypted_key->length ); des_decrypt( encrypted_key->data, encrypted_key->length, decrypted_key, ( unsigned char * ) passphrase_hash_out + DES_KEY_SIZE, ( unsigned char * ) passphrase_hash_out ); // sanity check if ( decrypted_key[ encrypted_key->length - 1 ] > 8 ) { fprintf( stderr, "Decryption error, bad padding\n"); asn1free( &pkcs8_key ); free( decrypted_key ); return 1; } asn1parse( decrypted_key, encrypted_key->length - decrypted_key[ encrypted_key->length - 1 ], &private_key ); free( decrypted_key ); key_type_oid = private_key.children->next->children; if ( memcmp( OID_RSAPrivateKey, key_type_oid->data, key_type_oid->length ) ) { fprintf( stderr, "Unsupported private key type" ); asn1free( &pkcs8_key ); asn1free( &private_key ); } priv_key_data = private_key.children->next->next; parse_pkcs8_private_key( privkey, priv_key_data->data, priv_key_data->length, "password" ); asn1free( &pkcs8_key ); asn1free( &private_key ); return 0; }
/** * Read a TLS packet off of the connection (assuming there's one waiting) and try * to update the security parameters based on the type of message received. If * the read times out, or if an alert is received, return an error code; return 0 * on success. * TODO - assert that the message received is of the type expected (for example, * if a server hello is expected but not received, this is a fatal error per * section 7.3). returns -1 if an error occurred (this routine will have sent an * appropriate alert). Otherwise, return the number of bytes read if the packet * includes application data; 0 if the packet was a handshake. -1 also indicates * that an alert was received. */ static int receive_tls_msg( int connection, char *buffer, int bufsz, TLSParameters *parameters ) { TLSPlaintext message; unsigned char *read_pos, *msg_buf, *decrypted_message, *encrypted_message; unsigned char header[ 5 ]; // size of TLSPlaintext int bytes_read, accum_bytes; int decrypted_length; // STEP 1 - read off the TLS Record layer // First, check to see if there's any data left over from a previous read. // If there is, pass that back up. // This means that if the caller isn't quick about reading available data, // TLS alerts can be missed. if ( parameters->unread_buffer != NULL ) { decrypted_message = parameters->unread_buffer; decrypted_length = parameters->unread_length; parameters->unread_buffer = NULL; parameters->unread_length = 0; message.type = content_application_data; } else { if ( recv( connection, header, 5, 0 ) <= 0 ) { // No data available; it's up to the caller whether this is an error or not. return -1; } message.type = header[ 0 ]; message.version.major = header[ 1 ]; message.version.minor = header[ 2 ]; memcpy( &message.length, header + 3, 2 ); message.length = htons( message.length ); encrypted_message = ( char * ) malloc( message.length ); // keep looping & appending until all bytes are accounted for accum_bytes = 0; msg_buf = encrypted_message; while ( accum_bytes < message.length ) { if ( ( bytes_read = recv( connection, ( void * ) msg_buf, message.length - accum_bytes, 0 ) ) <= 0 ) { int status; perror( "While reading a TLS packet" ); if ( ( status = send_alert_message( connection, illegal_parameter, ¶meters->active_send_parameters ) ) ) { free( msg_buf ); return status; } return -1; } accum_bytes += bytes_read; msg_buf += bytes_read; } // If a cipherspec is active, all of "encrypted_message" will be encrypted. // Must decrypt it before continuing. This will change the message length // in all cases, since decrypting also involves verifying a MAC (unless the // active cipher spec is NULL_WITH_NULL_NULL). decrypted_message = NULL; decrypted_length = tls_decrypt( header, encrypted_message, message.length, &decrypted_message, ¶meters->active_recv_parameters ); free( encrypted_message ); if ( decrypted_length < 0 ) { send_alert_message( connection, bad_record_mac, ¶meters->active_send_parameters ); return -1; } parameters->active_recv_parameters.seq_num++; } read_pos = decrypted_message; if ( message.type == content_handshake ) { while ( ( read_pos - decrypted_message ) < decrypted_length ) { Handshake handshake; const unsigned char *handshake_msg_start = read_pos; // Now, read the handshake type and length of the next packet // TODO - this fails if the read, above, only got part of the message read_pos = read_buffer( ( void * ) &handshake.msg_type, ( void * ) read_pos, 1 ); handshake.length = read_pos[ 0 ] << 16 | read_pos[ 1 ] << 8 | read_pos[ 2 ]; read_pos += 3; // TODO check for negative or unreasonably long length // Now, depending on the type, read in and process the packet itself. switch ( handshake.msg_type ) { // Client-side messages case server_hello: read_pos = parse_server_hello( read_pos, handshake.length, parameters ); if ( read_pos == NULL ) /* error occurred */ { free( msg_buf ); send_alert_message( connection, illegal_parameter, ¶meters->active_send_parameters ); return -1; } break; case certificate: read_pos = parse_x509_chain( read_pos, handshake.length, ¶meters->server_public_key ); if ( read_pos == NULL ) { printf( "Rejected, bad certificate\n" ); send_alert_message( connection, bad_certificate, ¶meters->active_send_parameters ); return -1; } break; case server_hello_done: parameters->server_hello_done = 1; break; case finished: { read_pos = parse_finished( read_pos, handshake.length, parameters ); if ( read_pos == NULL ) { send_alert_message( connection, illegal_parameter, ¶meters->active_send_parameters ); return -1; } } break; default: printf( "Ignoring unrecognized handshake message %d\n", handshake.msg_type ); // Silently ignore any unrecognized types per section 6 // TODO However, out-of-order messages should result in a fatal alert // per section 7.4 read_pos += handshake.length; break; } update_digest( ¶meters->md5_handshake_digest, handshake_msg_start, handshake.length + 4 ); update_digest( ¶meters->sha1_handshake_digest, handshake_msg_start, handshake.length + 4 ); } } else if ( message.type == content_alert ) { while ( ( read_pos - decrypted_message ) < decrypted_length ) { Alert alert; read_pos = read_buffer( ( void * ) &alert.level, ( void * ) read_pos, 1 ); read_pos = read_buffer( ( void * ) &alert.description, ( void * ) read_pos, 1 ); report_alert( &alert ); if ( alert.level == fatal ) { return -1; } } } else if ( message.type == content_change_cipher_spec ) { while ( ( read_pos - decrypted_message ) < decrypted_length ) { unsigned char change_cipher_spec_type; read_pos = read_buffer( ( void * ) &change_cipher_spec_type, ( void * ) read_pos, 1 ); if ( change_cipher_spec_type != 1 ) { printf( "Error - received message ChangeCipherSpec, but type != 1\n" ); exit( 0 ); } else { parameters->pending_recv_parameters.seq_num = 0; memcpy( ¶meters->active_recv_parameters, ¶meters->pending_recv_parameters, sizeof( ProtectionParameters ) ); init_protection_parameters( ¶meters->pending_recv_parameters ); } } } else if ( message.type == content_application_data ) { if ( decrypted_length <= bufsz ) { memcpy( buffer, decrypted_message, decrypted_length ); } else { // Need to hang on to a buffer of data here and pass it back for the // next call memcpy( buffer, decrypted_message, bufsz ); parameters->unread_length = decrypted_length - bufsz; parameters->unread_buffer = malloc( parameters->unread_length ); memcpy( parameters->unread_buffer, decrypted_message + bufsz, parameters->unread_length ); decrypted_length = bufsz; } } else { // Ignore content types not understood, per section 6 of the RFC. printf( "Ignoring non-recognized content type %d\n", message.type ); } free( decrypted_message ); return decrypted_length; }