aboutsummaryrefslogtreecommitdiffstats
path: root/include/zot.php
diff options
context:
space:
mode:
Diffstat (limited to 'include/zot.php')
-rw-r--r--include/zot.php235
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