set_date(); } if ( $setPublicKey ){ $outgoing->set_public_key(); } $response = self::check_response( $outgoing->send( $omitSignature ? FALSE : NULL ) ); if ( ! is_object( $response ) ){ self::$lastError = $response['error']; self::$lastResponse = $response['response']; return FALSE; } self::$lastResponse = $response; return $response; } static function check_response( $response ){ $error = ''; if ( $response['status'] != 200 ){ $error = "{$response['status']} {$response['message']}"; goto FINISH; } $msg = new ParTCP_Incoming_Message( $response['body'] ); if ( $msg->parseError ){ $error = "Server response could not be parsed\n\n{$response})"; goto FINISH; } if ( $msg->get('Message-Type') == 'failure-notice' ){ $error = $msg->get('Failure-Description'); goto FINISH; } if ( $msg->get('Message-Type') == 'rejection-notice' ){ $error = "{$msg->get('Rejection-Code')} {$msg->get('Rejection-Reason')}"; goto FINISH; } FINISH: if ( $error ){ return [ 'error' => $error, 'response' => $response['body'], 'request' => $msg->get('Original-Message') ]; } return $msg; } static function ping( $recipient, $sender = NULL, $cryptTest = NULL ){ $msg = [ 'To' => $recipient, 'Message-Type' => 'ping' ]; if ( $sender ){ $msg['From'] = $sender; if ( $cryptTest ){ $msg['Encryption-Request'] = $cryptTest; $msg['Decryption-Request~~'] = $cryptTest; } } return self::send( $msg ); } static function check_connection( $recipient, $sender ){ $bytes = base64_encode( random_bytes( 32 ) ); $msg = [ 'To' => $recipient, 'From' => $sender, 'Message-Type' => 'ping', 'Encryption-Request' => $bytes, 'Decryption-Request~~' => $bytes, ]; $response = self::send( $msg ); if ( ! $response ){ echo self::$lastError; return FALSE; } $result['remote_verification'] = $response->get('Verification-Result') == 'Signature verified successfully'; $result['remote_decryption'] = $response->get('Decryption-Result') == $bytes; $result['local_verification'] = $response->get_signature_status(); $result['local_decryption'] = $response->get('Encryption-Result') == $bytes; return $result; } static function lotcode_deposit( $eventServer, $sender, $eventId, $participantIds ){ if ( empty( $participantIds ) || ! is_array( $participantIds ) ){ return FALSE; } // group participants by server foreach ( $participantIds as $ptcpId ){ list ( $name, $server ) = explode( '@', $ptcpId, 2 ) + [ '', $eventServer ]; $participants[ $server ][] = $name; } // get public keys for participants foreach ( $participants as $server => $list ){ $msg = new ParTCP_Outgoing_Message( $server, $sender, 'key-list-request' ); $msg->set( 'Participants', $list ); $response = self::check_response( $msg->send() ); if ( ! is_object( $response ) ){ return $response; } $keys[ $server ] = $response->get('Keys'); } // split participants with and without pubkey $missing = []; $complete = []; foreach ( $keys as $server => $list ){ foreach ( $list as $key ){ if ( empty( $key['public_key'] ) ){ $missing[] = "{$key['id']}@{$server}"; } else { $complete[ "{$key['id']}@{$server}" ] = $key['public_key']; } } } if ( ! $complete ){ return [ 'deposited' => [], 'missing' => $missing, 'errors' => [], 'invalidation_errors' => [] ]; } // generate lotcodes $msg = new ParTCP_Outgoing_Message( $eventServer, $sender, 'multi-registration' ); $msg->set( 'Event-Id', $eventId ); $msg->set( 'Count', count( $complete ) ); $response = self::check_response( $msg->send() ); if ( ! is_object( $response ) ){ return $response; } $lotcodes = $response->get('Lot-Codes'); // deposit lotcodes $messages = []; reset( $complete ); reset( $lotcodes ); foreach ( $complete as $ptcpId => $pubkey ){ ParTCP_Crypto::set_remote_pubkey( $pubkey ); $lotcode = ParTCP_Crypto::encrypt( current( $lotcodes ) ); $msg = new ParTCP_Outgoing_Message( $eventServer, $sender, 'lot-code-deposit' ); $msg->set( 'Event-Id', $eventId ); $msg->set( 'Participant-Id', $ptcpId ); $msg->set( 'Lot-Code', $lotcode ); $msg->set_date(); $msg->set_public_key(); $messages[] = $msg->dump(); next( $lotcodes ); } $msg = new ParTCP_Outgoing_Message( $eventServer, $sender, 'envelope' ); $msg->set( 'Content', implode( "\n---\n", $messages ), TRUE ); $response = self::check_response( $msg->send() ); if ( ! is_object( $response ) ){ // Lotcodes invalidieren $msg = new ParTCP_Outgoing_Message( $eventServer, $sender, 'lot-invalidation' ); $msg->set( 'Event-Id', $eventId ); $msg->set( 'Lot-Codes', $lotcodes ); $msg->set_date(); $response = self::check_response( $msg->send() ); if ( ! is_object( $response ) ){ $response['invalidation_errors'] = $lotcodes; } return $response; } // check individual rejections and invalidate lots, if neccessary reset( $lotcodes ); reset( $complete ); $success = []; $errors = []; $invalidate = []; $messages = array_filter( explode( "\n---\n", $response->get('Content') ) ); foreach ( $messages as $response ){ $response = yaml_parse( $response ); if ( empty( $response['Message-Type'] ) || $response['Message-Type'] != 'lot-code-deposit-confirmation' ){ $errors[] = key( $complete ); $invalidate[] = current( $lotcodes ); } else { $success[] = key( $complete ); } next( $lotcodes ); next( $complete ); } $invalidationErrors = []; if ( $invalidate ){ $msg = new ParTCP_Outgoing_Message( $eventServer, $sender, 'lot-invalidation' ); $msg->set( 'Event-Id', $eventId ); $msg->set( 'Lot-Codes', $invalidate ); $msg->set_date(); $response = self::check_response( $msg->send() ); if ( ! is_object( $response ) ){ $invalidationErrors = array_merge( $invalidationErrors, $invalidate ); } } return [ 'deposited' => $success, 'missing' => $missing, 'errors' => $errors, 'invalidation_errors' => $invalidationErrors ]; } } // end of file ptcp_tools.class.php