diff options
Diffstat (limited to 'include/zot.php')
-rw-r--r-- | include/zot.php | 235 |
1 files changed, 201 insertions, 34 deletions
diff --git a/include/zot.php b/include/zot.php index 8bbc4a969..75c37836d 100644 --- a/include/zot.php +++ b/include/zot.php @@ -31,9 +31,9 @@ require_once('include/perm_upgrade.php'); * @param string $channel_nick a unique nickname of controlling entity * @returns string */ + function zot_new_uid($channel_nick) { $rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand(); - return(base64url_encode(hash('whirlpool', $rawstr, true), true)); } @@ -49,6 +49,7 @@ function zot_new_uid($channel_nick) { * @param string $guid * @param string $guid_sig */ + function make_xchan_hash($guid, $guid_sig) { return base64url_encode(hash('whirlpool', $guid . $guid_sig, true)); } @@ -62,17 +63,17 @@ function make_xchan_hash($guid, $guid_sig) { * @param string $hash - xchan_hash * @returns array of hubloc (hub location structures) * * \b hubloc_id int - * * \b hubloc_guid char(255) + * * \b hubloc_guid char(191) * * \b hubloc_guid_sig text - * * \b hubloc_hash char(255) - * * \b hubloc_addr char(255) + * * \b hubloc_hash char(191) + * * \b hubloc_addr char(191) * * \b hubloc_flags int * * \b hubloc_status int - * * \b hubloc_url char(255) + * * \b hubloc_url char(191) * * \b hubloc_url_sig text - * * \b hubloc_host char(255) - * * \b hubloc_callback char(255) - * * \b hubloc_connect char(255) + * * \b hubloc_host char(191) + * * \b hubloc_callback char(191) + * * \b hubloc_connect char(191) * * \b hubloc_sitekey text * * \b hubloc_updated datetime * * \b hubloc_connected datetime @@ -97,7 +98,7 @@ function zot_get_hublocs($hash) { * @param array $channel * sender channel structure * @param string $type - * packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'force_refresh', 'notify', 'auth_check' + * packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check' * @param array $recipients * envelope information, array ( 'guid' => string, 'guid_sig' => string ); empty for public posts * @param string $remote_key @@ -111,18 +112,21 @@ function zot_get_hublocs($hash) { */ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remote_key = null, $methods = '', $secret = null, $extra = null) { + $sig_method = get_config('system','signature_algorithm','sha256'); + $data = [ 'type' => $type, 'sender' => [ 'guid' => $channel['channel_guid'], - 'guid_sig' => base64url_encode(rsa_sign($channel['channel_guid'],$channel['channel_prvkey'])), + 'guid_sig' => base64url_encode(rsa_sign($channel['channel_guid'],$channel['channel_prvkey'],$sig_method)), 'url' => z_root(), - 'url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])), + 'url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'],$sig_method)), 'sitekey' => get_config('system','pubkey') ], 'callback' => '/post', 'version' => ZOT_REVISION, - 'encryption' => crypto_methods() + 'encryption' => crypto_methods(), + 'signing' => signing_methods() ]; if ($recipients) { @@ -134,7 +138,7 @@ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remot if ($secret) { $data['secret'] = $secret; - $data['secret_sig'] = base64url_encode(rsa_sign($secret,$channel['channel_prvkey'])); + $data['secret_sig'] = base64url_encode(rsa_sign($secret,$channel['channel_prvkey'],$sig_method)); } if ($extra) { @@ -529,7 +533,7 @@ function zot_gethub($arr, $multiple = false) { } $limit = (($multiple) ? '' : ' limit 1 '); - $sitekey = ((array_key_exists('sitekey',$arr) && $arr['sitekey']) ? " and hubloc_sitekey = '" . protect_sprintf($arr['sitekey']) . "' " : ''); + $sitekey = ((array_key_exists('sitekey',$arr) && $arr['sitekey']) ? " and hubloc_sitekey = '" . dbesc(protect_sprintf($arr['sitekey'])) . "' " : ''); $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_guid = '%s' and hubloc_guid_sig = '%s' @@ -575,6 +579,8 @@ function zot_register_hub($arr) { if($arr['url'] && $arr['url_sig'] && $arr['guid'] && $arr['guid_sig']) { + $sig_methods = ((array_key_exists('signing',$arr) && is_array($arr['signing'])) ? $arr['signing'] : [ 'sha256' ]); + $guid_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']); $url = $arr['url'] . '/.well-known/zot-info/?f=&guid_hash=' . $guid_hash; @@ -594,17 +600,18 @@ function zot_register_hub($arr) { * our current communication. */ - if((rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$record['key'])) - && (rsa_verify($arr['url'],base64url_decode($arr['url_sig']),$record['key'])) + foreach($sig_methods as $method) { + if((rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$record['key'],$method)) + && (rsa_verify($arr['url'],base64url_decode($arr['url_sig']),$record['key'],$method)) && ($arr['guid'] === $record['guid']) && ($arr['guid_sig'] === $record['guid_sig'])) { - - $c = import_xchan($record); - if($c['success']) - $result['success'] = true; - } - else { - logger('zot_register_hub: failure to verify returned packet.'); + $c = import_xchan($record); + if($c['success']) + $result['success'] = true; + } + else { + logger('zot_register_hub: failure to verify returned packet using ' . $method); + } } } } @@ -657,8 +664,19 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { $import_photos = false; - if(! rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$arr['key'])) { - logger('import_xchan: Unable to verify channel signature for ' . $arr['address']); + $sig_methods = ((array_key_exists('signing',$arr) && is_array($arr['signing'])) ? $arr['signing'] : [ 'sha256' ]); + $verified = false; + + foreach($sig_methods as $method) { + if(! rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$arr['key'],$method)) { + logger('import_xchan: Unable to verify channel signature for ' . $arr['address'] . ' using ' . $method); + continue; + } + else { + $verified = true; + } + } + if(! $verified) { $ret['message'] = t('Unable to verify channel signature'); return $ret; } @@ -917,7 +935,7 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { } elseif(! $ud_flags) { // nothing changed but we still need to update the updates record - q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ", + q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d) > 0 ", intval(UPDATE_FLAGS_UPDATED), dbesc($address), intval(UPDATE_FLAGS_UPDATED) @@ -2948,6 +2966,11 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { if($packet) logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG); + $keychange = (($packet && array_key_exists('keychange',$packet)) ? true : false); + if($keychange) { + logger('keychange sync'); + } + if(! $uid) $uid = local_channel(); @@ -2961,6 +2984,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { return; $channel = $r[0]; + unset($channel['channel_password']); unset($channel['channel_salt']); @@ -2971,12 +2995,11 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { } } - if(intval($channel['channel_removed'])) return; $h = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash = '%s' and hubloc_deleted = 0", - dbesc($channel['channel_hash']) + dbesc(($keychange) ? $packet['keychange']['old_hash'] : $channel['channel_hash']) ); if(! $h) @@ -3031,7 +3054,15 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { // don't pass these elements, they should not be synchronised - $disallowed = array('channel_id','channel_account_id','channel_primary','channel_prvkey','channel_address','channel_deleted','channel_removed','channel_system'); + + $disallowed = [ + 'channel_id','channel_account_id','channel_primary','channel_address', + 'channel_deleted','channel_removed','channel_system' + ]; + + if(! $keychange) { + $disallowed[] = 'channel_prvkey'; + } if(in_array($k,$disallowed)) continue; @@ -3091,17 +3122,18 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { function process_channel_sync_delivery($sender, $arr, $deliveries) { - require_once('include/import.php'); /** @FIXME this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic. */ - $result = array(); + $result = []; + + $keychange = ((array_key_exists('keychange',$arr)) ? true : false); foreach ($deliveries as $d) { $r = q("select * from channel where channel_hash = '%s' limit 1", - dbesc($d['hash']) + dbesc(($keychange) ? $arr['keychange']['old_hash'] : $d['hash']) ); if (! $r) { @@ -3120,6 +3152,94 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { continue; } + if($keychange) { + // verify the keychange operation + if(! rsa_verify($arr['channel']['channel_pubkey'],base64url_decode($arr['keychange']['new_sig']),$channel['channel_prvkey'])) { + logger('sync keychange: verification failed'); + continue; + } + + $sig = base64url_encode(rsa_sign($channel['channel_guid'],$arr['channel']['channel_prvkey'])); + $hash = make_xchan_hash($channel['channel_guid'],$sig); + + + $r = q("update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s', + channel_hash = '%s' where channel_id = %d", + dbesc($arr['channel']['channel_prvkey']), + dbesc($arr['channel']['channel_pubkey']), + dbesc($sig), + dbesc($hash), + intval($channel['channel_id']) + ); + if(! $r) { + logger('keychange sync: channel update failed'); + continue; + } + + $r = q("select * from channel where channel_id = %d", + intval($channel['channel_id']) + ); + + if(! $r) { + logger('keychange sync: channel retrieve failed'); + continue; + } + + $channel = $r[0]; + + $h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ", + dbesc($arr['keychange']['old_hash']), + dbesc(z_root()) + ); + + if($h) { + foreach($h as $hv) { + $hv['hubloc_guid_sig'] = $sig; + $hv['hubloc_hash'] = $hash; + $hv['hubloc_url_sig'] = base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])); + hubloc_store_lowlevel($hv); + } + } + + $x = q("select * from xchan where xchan_hash = '%s' ", + dbesc($arr['keychange']['old_hash']) + ); + + $check = q("select * from xchan where xchan_hash = '%s'", + dbesc($hash) + ); + + if(($x) && (! $check)) { + $oldxchan = $x[0]; + foreach($x as $xv) { + $xv['xchan_guid_sig'] = $sig; + $xv['xchan_hash'] = $hash; + $xv['xchan_pubkey'] = $channel['channel_pubkey']; + xchan_store_lowlevel($xv); + $newxchan = $xv; + } + } + + $a = q("select * from abook where abook_xchan = '%s' and abook_self = 1", + dbesc($arr['keychange']['old_hash']) + ); + + if($a) { + q("update abook set abook_xchan = '%s' where abook_id = %d", + dbesc($hash), + intval($a[0]['abook_id']) + ); + } + + xchan_change_key($oldxchan,$newxchan,$arr['keychange']); + + // keychange operations can end up in a confused state if you try and sync anything else + // besides the channel keys, so ignore any other packets. + + continue; + } + + if(array_key_exists('config',$arr) && is_array($arr['config']) && count($arr['config'])) { foreach($arr['config'] as $cat => $k) { foreach($arr['config'][$cat] as $k => $v) @@ -3757,11 +3877,57 @@ function zot_reply_message_request($data) { json_return_and_die($ret); } +function zot_rekey_request($sender,$data) { + + $ret = array('success' => false); + + // newsig is newkey signed with oldkey + + // The original xchan will remain. In Zot/Receiver we will have imported the new xchan and hubloc to verify + // the packet authenticity. What we will do now is verify that the keychange operation was signed by the + // oldkey, and if so change all the abook, abconfig, group, and permission elements which reference the + // old xchan_hash. + + if((! $data['old_key']) && (! $data['new_key']) && (! $data['new_sig'])) + json_return_and_die($ret); + + $oldhash = make_xchan_hash($data['old_guid'],$data['old_guid_sig']); + + $r = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($oldhash) + ); + + if(! $r) { + json_return_and_die($ret); + } + + $xchan = $r[0]; + + if(! rsa_verify($data['new_key'],base64url_decode($data['new_sig']),$xchan['xchan_pubkey'])) { + json_return_and_die($ret); + } + + $newhash = make_xchan_hash($sender['guid'],$sender['guid_sig']); + + $r = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($newhash) + ); + + $newxchan = $r[0]; + + xchan_change_key($xchan,$newxchan,$data); + + $ret['success'] = true; + json_return_and_die($ret); +} + function zotinfo($arr) { $ret = array('success' => false); + $sig_method = get_config('system','signature_algorithm','sha256'); + $zhash = ((x($arr,'guid_hash')) ? $arr['guid_hash'] : ''); $zguid = ((x($arr,'guid')) ? $arr['guid'] : ''); $zguid_sig = ((x($arr,'guid_sig')) ? $arr['guid_sig'] : ''); @@ -3925,7 +4091,7 @@ function zotinfo($arr) { // Communication details if($token) - $ret['signed_token'] = base64url_encode(rsa_sign('token.' . $token,$e['channel_prvkey'])); + $ret['signed_token'] = base64url_encode(rsa_sign('token.' . $token,$e['channel_prvkey'],$sig_method)); $ret['guid'] = $e['xchan_guid']; @@ -3994,7 +4160,7 @@ function zotinfo($arr) { $ret['site'] = array(); $ret['site']['url'] = z_root(); - $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(),$e['channel_prvkey'])); + $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(),$e['channel_prvkey'],$sig_method)); $ret['site']['zot_auth'] = z_root() . '/magic'; $dirmode = get_config('system','directory_mode'); @@ -4012,6 +4178,7 @@ function zotinfo($arr) { $ret['site']['encryption'] = crypto_methods(); + $ret['site']['signing'] = signing_methods(); // hide detailed site information if you're off the grid |