# HG changeset patch # User Ivo Smits # Date 1416183545 -3600 # Node ID 5c8c4fa95803d7ea0b61eb60bd7fcb1440ea0d61 # Parent f6954b464d2fc2454267a7274dd156ee911ed17f Added support for transfer chaining and some bugfixes diff -r f6954b464d2f -r 5c8c4fa95803 marccore.php --- a/marccore.php Fri Nov 14 00:08:24 2014 +0100 +++ b/marccore.php Mon Nov 17 01:19:05 2014 +0100 @@ -11,6 +11,7 @@ case 'label': return $this->_label; case 'expiration': return (isset($this->_extensions[4]) && strlen($this->_extensions[4]) >= 4) ? marc_decode_int32be($this->_extensions[4]) : NULL; case 'transfer': return isset($this->_extensions[1]) ? $this->_extensions[1] : NULL; + case 'transferchain': return isset($this->_extensions[5]) ? $this->_extensions[5] : NULL; case 'value': if ($this->_value === FALSE) $this->_value = self::DecodeValue($this->_message, $this->_valueoffset); return $this->_value; @@ -30,6 +31,7 @@ case 'version': case 'key': case 'serial': case 'label': case 'value': case 'updatemessage': case 'tags': return TRUE; case 'expiration': return isset($this->_extensions[4]); case 'transfer': return isset($this->_extensions[1]); + case 'transferchain': return isset($this->_extensions[5]); default: return FALSE; } } @@ -57,7 +59,7 @@ if (!$l || $l < $i+4+1+1) return FALSE; $upd->_serial = marc_decode_int32be($data, $i); $i += 4; $labellen = ord($data[$i++]); - if ($l < $i+$labellen+4) return FALSE; + if ($l < $i+$labellen+1) return FALSE; $upd->_label = substr($data, $i, $labellen); $i += $labellen; $upd->_extensions = array(); for ($numext = ord($data[$i++]); $numext > 0; $numext--) { @@ -87,6 +89,7 @@ if (!isset($upd['key'])) $upd['key'] = substr($seckey, 32, 32); if ($upd['key'] != substr($seckey, 32, 32)) throw new Exception('Resource key is not valid'); if (!isset($upd['label'])) throw new Exception('Resource label not set'); + $upd['label'] = (string)$upd['label']; if (strlen($upd['label']) > 255) throw new Exception('Resource label too long'); if (!isset($upd['serial'])) $upd['serial'] = time(); if ($current) { @@ -94,12 +97,35 @@ if (!self::CanImport($upd, $current)) throw new Exception('Can not update resource'); } if (isset($upd['transfer']) && (strlen($upd['transfer']) != 0 && strlen($upd['transfer']) != NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES)) throw new Exception('Transfer recipient key is not valid'); + if ($current) { + unset($upd['transferchain']); + if (isset($current['transferchain']) && ($chain = self::Decode($current['transferchain'])) && $chain->Verify() && $chain->serial >= time() - 365*24*60*60 && isset($chain->transfer) && ($chain->transfer == $upd['key'] || ($upd['key'] == $current['key'] && !strlen($chain->transfer)))) { + $upd['transferchain'] = $current['transferchain']; + } elseif (isset($current['transfer']) && isset($current['updatemessage']) && $current['serial'] >= time() - 365*24*60*60 && isset($current['transfer']) && ($current['transfer'] == $upd['key'] || !strlen($current['transfer']))) { + $upd['transferchain'] = $current['updatemessage']; + } + } + if (isset($upd['transfer'])) { + if (isset($upd['transferchain'])) { + $chain = self::Decode($upd['transferchain']); + while ($chain && $chain->key == $upd['key']) $chain = ($chain->Verify() && $chain->serial >= time() - 365*24*60*60 && isset($chain->transferchain)) ? self::Decode($chain->transferchain) : NULL; + if ($chain && $chain->Verify() && $chain->serial >= time() - 365*24*60*60) $upd['transferchain'] = $chain->updatemessage; else unset($upd['transferchain']); + } + if (isset($upd['value']) && !is_null($upd['value'])) { + $chain = array('label' => $upd['label'], 'serial' => $upd['serial'], 'key' => $upd['key'], 'transfer' => $upd['transfer']); + if (isset($upd['expiration'])) $chain['expiration'] = $upd['expiration']; + if (isset($upd['transferchain'])) $chain['transferchain'] = $upd['transferchain']; + $chain = self::Create($chain, $seckey); + if ($chain && strlen($chain->updatemessage) <= 0xffff) $upd['transferchain'] = $chain->updatemessage; + } + } $data = marc_encode_int32be($upd['serial']); $data .= chr(strlen($upd['label'])).$upd['label']; $value = array(); if (isset($upd->_extensions)) foreach ($upd->_extensions as $identifier => $item) $value[$identifier] = $item; if (isset($upd['transfer'])) $value[1] = $upd['transfer']; if (isset($upd['expiration'])) $value[4] = marc_encode_int32be($upd['expiration']); + if (isset($upd['transferchain'])) $value[5] = $upd['transferchain']; if (count($value) > 0xff) throw new Exception('Too many extensions used'); $data .= chr(count($value)); foreach ($value as $identifier => $item) { @@ -118,6 +144,7 @@ $arr = array('version' => $this->version, 'key' => $this->key, 'serial' => $this->serial, 'label' => $this->label, 'value' => $this->value); if (isset($this->expiration)) $arr['expiration'] = $this->expiration; if (isset($this->transfer)) $arr['transfer'] = $this->transfer; + if (isset($this->transferchain)) $arr['transferchain'] = $this->transferchain; return $arr; } @@ -177,6 +204,7 @@ } public static function CanImport($nw, $cu = NULL) { if (!$nw || !isset($nw['label'])) return FALSE; + if ($nw['serial'] > time() + 7*24*60*60) return FALSE; if ($cu) { if ($nw['label'] != $cu['label']) return FALSE; if ($cu['serial'] >= $nw['serial']) return FALSE; @@ -184,12 +212,13 @@ if (isset($cu['expiration']) && $cu['expiration'] < time()) return TRUE; if ($cu['serial'] < time() - 365*24*60*60) return TRUE; if (isset($cu['transfer']) && (!strlen($cu['transfer']) || $cu['transfer'] == $nw['key'])) return TRUE; + if (isset($nw['transferchain']) && ($chain = MARCUpdate::Decode($nw['transferchain'])) && $chain->Verify() && self::CanImport($nw, $chain) && self::CanImport($chain, $cu)) return TRUE; return FALSE; } else { if ($nw['serial'] < time() - 365*24*60*60) return FALSE; if (isset($nw['expiration']) && $nw['expiration'] < time()) return FALSE; + return TRUE; } - return TRUE; } } abstract class MARCDatabase { @@ -202,25 +231,15 @@ if (is_string($resource)) $resource = MARCUpdate::Decode($resource); if (!$resource) return FALSE; $current = $this->GetResource($resource['label']); - if (!$force) foreach ($this->_importResourceFilterCallbacks as $callback) if (!call_user_func($callback, $this, $resource, $current)) return FALSE; - if (!$force && !MARCUpdate::CanImport($resource, $current)) return FALSE; + if (!$force && !$this->CanImportResource($resource, $current)) return FALSE; if (!$resource->Verify()) return FALSE; if (!$this->ImportInternal($resource)) return FALSE; $this->ResourceImported($resource); return $resource; } - public function UpdateResource($resource, $seckey, $force = FALSE) { - $res = MARCUpdate::Create($resource, $seckey, $force ? NULL : $this->GetResource($resource['label'])); - if (!$res) return FALSE; - return $this->Import($res, $force); - } - protected function CanImportResource(&$resource, $force = FALSE) { - if (is_string($resource)) $resource = MARCUpdate::Decode($resource); - if (!$resource) return FALSE; - $current = $this->GetResource($resource['label']); - if (!$force) foreach ($this->_importResourceFilterCallbacks as $callback) if (!call_user_func($callback, $this, $resource, $current)) return FALSE; - if (!$force && !MARCUpdate::CanImport($resource, $current)) return FALSE; - if (!$resource->Verify()) return FALSE; + protected function CanImportResource($resource, $current) { + foreach ($this->_importResourceFilterCallbacks as $callback) if (!call_user_func($callback, $this, $resource, $current)) return FALSE; + if (!MARCUpdate::CanImport($resource, $current)) return FALSE; return TRUE; } protected function ResourceImported($resource) { @@ -232,6 +251,13 @@ public function RegisterResourceImportCallback($callback) { $this->_importResourceCallbacks[] = $callback; } + public function UpdateResource($resource, $seckey, $force = FALSE) { + if (is_a($resource, 'MARCUpdate')) $resource = $resource->ToArray(); + if (!$force) unset($resource['serial'], $resource['key']); + $res = MARCUpdate::Create($resource, $seckey, $force ? NULL : $this->GetResource($resource['label'])); + if (!$res) return FALSE; + return $this->Import($res, $force); + } public function SyncHTTP($server, $options = array()) { $log = isset($options['log']) ? $options['log'] : TRUE; $result = array(); diff -r f6954b464d2f -r 5c8c4fa95803 marcus.php --- a/marcus.php Fri Nov 14 00:08:24 2014 +0100 +++ b/marcus.php Mon Nov 17 01:19:05 2014 +0100 @@ -81,8 +81,7 @@ break; case 'USE': $key = array('store' => FALSE, 'pk' => $resource['key']); - if (isset($resource['value']['seckey'])) $key['sk'] = $resource['value']['seckey']; - if (isset($resource['value']['seckeyenc'])) $key['locked'] = $resource['value']['seckeyenc']; + if (isset($resource['value']['seckey'])) $key['locked'] = $resource['value']['seckey']; break; case 'IMPORT': $key = array('store' => FALSE); @@ -91,10 +90,13 @@ break; case 'UNLOCK': if (!isset($key['locked'])) throw new Exception('The key is not locked'); - $ret = hash('sha512', $key['pk'].$argv[$argi++], TRUE); - $key['sk'] = ''; - for ($i = 0; $i < 32; $i++) $key['sk'] .= chr(ord($key['locked'][$i]) ^ ord($ret[$i])); - $ret = nacl_crypto_sign_ed25519_keypair($key['sk'], $key['sk']); + if (!is_array($key['locked']) || !isset($key['locked']['key'])) throw new Exception('The locked key is invalid'); + $rounds = isset($key['locked']['rounds']) ? intval($key['locked']['rounds']) : 0; + $ret = str_repeat(chr(0), 64); + for ($i = 0; $i < $rounds; $i++) $ret = hash('sha512', $ret.$argv[$argi].$key['pk'], TRUE); + $argi++; + $ret = substr($key['locked'] ^ $ret, 0, 32); + $ret = nacl_crypto_sign_ed25519_keypair($key['sk'], $ret); if ($ret != $key['pk']) throw new Exception('Key password is not valid'); echo 'Unlocked public key '.bin2hex($key['pk'])."\n"; break; @@ -138,9 +140,11 @@ break; case 'PWAUTH': if (!isset($key['sk'])) throw new Exception('The key is not available'); - $ret = hash('sha512', $key['pk'].$argv[$argi++], TRUE); - $key['locked'] = ''; - for ($i = 0; $i < 32; $i++) $key['locked'] .= chr(ord($key['sk'][$i]) ^ ord($ret[$i])); + $rounds = 5000; + $ret = str_repeat(chr(0), 64); + for ($i = 0; $i < $rounds; $i++) $ret = hash('sha512', $ret.$argv[$argi].$key['pk'], TRUE); + $argi++; + $key['locked'] = array('rounds' => $rounds, 'key' => substr($key['sk'] ^ $hash, 0, 32)); if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); $resource['value']['seckeyenc'] = $key['locked']; $reschanged = TRUE; diff -r f6954b464d2f -r 5c8c4fa95803 marns_import.php --- a/marns_import.php Fri Nov 14 00:08:24 2014 +0100 +++ b/marns_import.php Mon Nov 17 01:19:05 2014 +0100 @@ -91,7 +91,7 @@ if (file_exists($skey)) { $skey = file_get_contents($skey); } else if (strlen($skey) == 64) { - $skey = bin2hex($skey); + $skey = hex2bin($skey); } else if ($skey == '-') { nacl_crypto_sign_ed25519_keypair($skey); }