Mercurial > hg > marc_php
comparison marccore.php @ 3:5c8c4fa95803 draft
Added support for transfer chaining and some bugfixes
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Mon, 17 Nov 2014 01:19:05 +0100 |
parents | 3ac7bd7495fd |
children | c642254dc9ee |
comparison
equal
deleted
inserted
replaced
2:f6954b464d2f | 3:5c8c4fa95803 |
---|---|
9 case 'key': return $this->_key; | 9 case 'key': return $this->_key; |
10 case 'serial': return $this->_serial; | 10 case 'serial': return $this->_serial; |
11 case 'label': return $this->_label; | 11 case 'label': return $this->_label; |
12 case 'expiration': return (isset($this->_extensions[4]) && strlen($this->_extensions[4]) >= 4) ? marc_decode_int32be($this->_extensions[4]) : NULL; | 12 case 'expiration': return (isset($this->_extensions[4]) && strlen($this->_extensions[4]) >= 4) ? marc_decode_int32be($this->_extensions[4]) : NULL; |
13 case 'transfer': return isset($this->_extensions[1]) ? $this->_extensions[1] : NULL; | 13 case 'transfer': return isset($this->_extensions[1]) ? $this->_extensions[1] : NULL; |
14 case 'transferchain': return isset($this->_extensions[5]) ? $this->_extensions[5] : NULL; | |
14 case 'value': | 15 case 'value': |
15 if ($this->_value === FALSE) $this->_value = self::DecodeValue($this->_message, $this->_valueoffset); | 16 if ($this->_value === FALSE) $this->_value = self::DecodeValue($this->_message, $this->_valueoffset); |
16 return $this->_value; | 17 return $this->_value; |
17 case 'updatemessage': return $this->_message; | 18 case 'updatemessage': return $this->_message; |
18 case 'tags': return $this->_tags; break; | 19 case 'tags': return $this->_tags; break; |
28 public function __isset($name) { | 29 public function __isset($name) { |
29 switch (strtolower($name)) { | 30 switch (strtolower($name)) { |
30 case 'version': case 'key': case 'serial': case 'label': case 'value': case 'updatemessage': case 'tags': return TRUE; | 31 case 'version': case 'key': case 'serial': case 'label': case 'value': case 'updatemessage': case 'tags': return TRUE; |
31 case 'expiration': return isset($this->_extensions[4]); | 32 case 'expiration': return isset($this->_extensions[4]); |
32 case 'transfer': return isset($this->_extensions[1]); | 33 case 'transfer': return isset($this->_extensions[1]); |
34 case 'transferchain': return isset($this->_extensions[5]); | |
33 default: return FALSE; | 35 default: return FALSE; |
34 } | 36 } |
35 } | 37 } |
36 public function __unset($name) { | 38 public function __unset($name) { |
37 switch (strtolower($name)) { | 39 switch (strtolower($name)) { |
55 $i = NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES + 64 + 1; | 57 $i = NACL_CRYPTO_SIGN_ed25519_PUBLICKEYBYTES + 64 + 1; |
56 $l = strlen($data); | 58 $l = strlen($data); |
57 if (!$l || $l < $i+4+1+1) return FALSE; | 59 if (!$l || $l < $i+4+1+1) return FALSE; |
58 $upd->_serial = marc_decode_int32be($data, $i); $i += 4; | 60 $upd->_serial = marc_decode_int32be($data, $i); $i += 4; |
59 $labellen = ord($data[$i++]); | 61 $labellen = ord($data[$i++]); |
60 if ($l < $i+$labellen+4) return FALSE; | 62 if ($l < $i+$labellen+1) return FALSE; |
61 $upd->_label = substr($data, $i, $labellen); $i += $labellen; | 63 $upd->_label = substr($data, $i, $labellen); $i += $labellen; |
62 $upd->_extensions = array(); | 64 $upd->_extensions = array(); |
63 for ($numext = ord($data[$i++]); $numext > 0; $numext--) { | 65 for ($numext = ord($data[$i++]); $numext > 0; $numext--) { |
64 if ($l < $i+1+2) return FALSE; | 66 if ($l < $i+1+2) return FALSE; |
65 $extid = ord($data[$i++]); | 67 $extid = ord($data[$i++]); |
85 if (strlen($seckey) == 32) nacl_crypto_sign_ed25519_keypair($seckey, $seckey); | 87 if (strlen($seckey) == 32) nacl_crypto_sign_ed25519_keypair($seckey, $seckey); |
86 if (strlen($seckey) < 64) throw new Exception('Signing key is not valid'); | 88 if (strlen($seckey) < 64) throw new Exception('Signing key is not valid'); |
87 if (!isset($upd['key'])) $upd['key'] = substr($seckey, 32, 32); | 89 if (!isset($upd['key'])) $upd['key'] = substr($seckey, 32, 32); |
88 if ($upd['key'] != substr($seckey, 32, 32)) throw new Exception('Resource key is not valid'); | 90 if ($upd['key'] != substr($seckey, 32, 32)) throw new Exception('Resource key is not valid'); |
89 if (!isset($upd['label'])) throw new Exception('Resource label not set'); | 91 if (!isset($upd['label'])) throw new Exception('Resource label not set'); |
92 $upd['label'] = (string)$upd['label']; | |
90 if (strlen($upd['label']) > 255) throw new Exception('Resource label too long'); | 93 if (strlen($upd['label']) > 255) throw new Exception('Resource label too long'); |
91 if (!isset($upd['serial'])) $upd['serial'] = time(); | 94 if (!isset($upd['serial'])) $upd['serial'] = time(); |
92 if ($current) { | 95 if ($current) { |
93 if ($upd['serial'] <= $current['serial']) $upd['serial'] = $current['serial'] + 1; | 96 if ($upd['serial'] <= $current['serial']) $upd['serial'] = $current['serial'] + 1; |
94 if (!self::CanImport($upd, $current)) throw new Exception('Can not update resource'); | 97 if (!self::CanImport($upd, $current)) throw new Exception('Can not update resource'); |
95 } | 98 } |
96 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'); | 99 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'); |
100 if ($current) { | |
101 unset($upd['transferchain']); | |
102 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)))) { | |
103 $upd['transferchain'] = $current['transferchain']; | |
104 } elseif (isset($current['transfer']) && isset($current['updatemessage']) && $current['serial'] >= time() - 365*24*60*60 && isset($current['transfer']) && ($current['transfer'] == $upd['key'] || !strlen($current['transfer']))) { | |
105 $upd['transferchain'] = $current['updatemessage']; | |
106 } | |
107 } | |
108 if (isset($upd['transfer'])) { | |
109 if (isset($upd['transferchain'])) { | |
110 $chain = self::Decode($upd['transferchain']); | |
111 while ($chain && $chain->key == $upd['key']) $chain = ($chain->Verify() && $chain->serial >= time() - 365*24*60*60 && isset($chain->transferchain)) ? self::Decode($chain->transferchain) : NULL; | |
112 if ($chain && $chain->Verify() && $chain->serial >= time() - 365*24*60*60) $upd['transferchain'] = $chain->updatemessage; else unset($upd['transferchain']); | |
113 } | |
114 if (isset($upd['value']) && !is_null($upd['value'])) { | |
115 $chain = array('label' => $upd['label'], 'serial' => $upd['serial'], 'key' => $upd['key'], 'transfer' => $upd['transfer']); | |
116 if (isset($upd['expiration'])) $chain['expiration'] = $upd['expiration']; | |
117 if (isset($upd['transferchain'])) $chain['transferchain'] = $upd['transferchain']; | |
118 $chain = self::Create($chain, $seckey); | |
119 if ($chain && strlen($chain->updatemessage) <= 0xffff) $upd['transferchain'] = $chain->updatemessage; | |
120 } | |
121 } | |
97 $data = marc_encode_int32be($upd['serial']); | 122 $data = marc_encode_int32be($upd['serial']); |
98 $data .= chr(strlen($upd['label'])).$upd['label']; | 123 $data .= chr(strlen($upd['label'])).$upd['label']; |
99 $value = array(); | 124 $value = array(); |
100 if (isset($upd->_extensions)) foreach ($upd->_extensions as $identifier => $item) $value[$identifier] = $item; | 125 if (isset($upd->_extensions)) foreach ($upd->_extensions as $identifier => $item) $value[$identifier] = $item; |
101 if (isset($upd['transfer'])) $value[1] = $upd['transfer']; | 126 if (isset($upd['transfer'])) $value[1] = $upd['transfer']; |
102 if (isset($upd['expiration'])) $value[4] = marc_encode_int32be($upd['expiration']); | 127 if (isset($upd['expiration'])) $value[4] = marc_encode_int32be($upd['expiration']); |
128 if (isset($upd['transferchain'])) $value[5] = $upd['transferchain']; | |
103 if (count($value) > 0xff) throw new Exception('Too many extensions used'); | 129 if (count($value) > 0xff) throw new Exception('Too many extensions used'); |
104 $data .= chr(count($value)); | 130 $data .= chr(count($value)); |
105 foreach ($value as $identifier => $item) { | 131 foreach ($value as $identifier => $item) { |
106 $item = (string)$item; | 132 $item = (string)$item; |
107 if (strlen($item) > 0xffff) throw new Exception('Extension data too big'); | 133 if (strlen($item) > 0xffff) throw new Exception('Extension data too big'); |
116 } | 142 } |
117 public function ToArray() { | 143 public function ToArray() { |
118 $arr = array('version' => $this->version, 'key' => $this->key, 'serial' => $this->serial, 'label' => $this->label, 'value' => $this->value); | 144 $arr = array('version' => $this->version, 'key' => $this->key, 'serial' => $this->serial, 'label' => $this->label, 'value' => $this->value); |
119 if (isset($this->expiration)) $arr['expiration'] = $this->expiration; | 145 if (isset($this->expiration)) $arr['expiration'] = $this->expiration; |
120 if (isset($this->transfer)) $arr['transfer'] = $this->transfer; | 146 if (isset($this->transfer)) $arr['transfer'] = $this->transfer; |
147 if (isset($this->transferchain)) $arr['transferchain'] = $this->transferchain; | |
121 return $arr; | 148 return $arr; |
122 } | 149 } |
123 | 150 |
124 private static function EncodeValue($data) { | 151 private static function EncodeValue($data) { |
125 if (is_null($data)) { | 152 if (is_null($data)) { |
175 default: throw new Exception('Unsupported type code '.$type); | 202 default: throw new Exception('Unsupported type code '.$type); |
176 } | 203 } |
177 } | 204 } |
178 public static function CanImport($nw, $cu = NULL) { | 205 public static function CanImport($nw, $cu = NULL) { |
179 if (!$nw || !isset($nw['label'])) return FALSE; | 206 if (!$nw || !isset($nw['label'])) return FALSE; |
207 if ($nw['serial'] > time() + 7*24*60*60) return FALSE; | |
180 if ($cu) { | 208 if ($cu) { |
181 if ($nw['label'] != $cu['label']) return FALSE; | 209 if ($nw['label'] != $cu['label']) return FALSE; |
182 if ($cu['serial'] >= $nw['serial']) return FALSE; | 210 if ($cu['serial'] >= $nw['serial']) return FALSE; |
183 if ($cu['key'] == $nw['key']) return TRUE; | 211 if ($cu['key'] == $nw['key']) return TRUE; |
184 if (isset($cu['expiration']) && $cu['expiration'] < time()) return TRUE; | 212 if (isset($cu['expiration']) && $cu['expiration'] < time()) return TRUE; |
185 if ($cu['serial'] < time() - 365*24*60*60) return TRUE; | 213 if ($cu['serial'] < time() - 365*24*60*60) return TRUE; |
186 if (isset($cu['transfer']) && (!strlen($cu['transfer']) || $cu['transfer'] == $nw['key'])) return TRUE; | 214 if (isset($cu['transfer']) && (!strlen($cu['transfer']) || $cu['transfer'] == $nw['key'])) return TRUE; |
215 if (isset($nw['transferchain']) && ($chain = MARCUpdate::Decode($nw['transferchain'])) && $chain->Verify() && self::CanImport($nw, $chain) && self::CanImport($chain, $cu)) return TRUE; | |
187 return FALSE; | 216 return FALSE; |
188 } else { | 217 } else { |
189 if ($nw['serial'] < time() - 365*24*60*60) return FALSE; | 218 if ($nw['serial'] < time() - 365*24*60*60) return FALSE; |
190 if (isset($nw['expiration']) && $nw['expiration'] < time()) return FALSE; | 219 if (isset($nw['expiration']) && $nw['expiration'] < time()) return FALSE; |
191 } | 220 return TRUE; |
192 return TRUE; | 221 } |
193 } | 222 } |
194 } | 223 } |
195 abstract class MARCDatabase { | 224 abstract class MARCDatabase { |
196 private $_importResourceFilterCallbacks = array(), $_importResourceCallbacks = array(); | 225 private $_importResourceFilterCallbacks = array(), $_importResourceCallbacks = array(); |
197 public abstract function GetResource($label); | 226 public abstract function GetResource($label); |
200 public abstract function DeleteResource($label); | 229 public abstract function DeleteResource($label); |
201 public function Import($resource, $force = FALSE) { | 230 public function Import($resource, $force = FALSE) { |
202 if (is_string($resource)) $resource = MARCUpdate::Decode($resource); | 231 if (is_string($resource)) $resource = MARCUpdate::Decode($resource); |
203 if (!$resource) return FALSE; | 232 if (!$resource) return FALSE; |
204 $current = $this->GetResource($resource['label']); | 233 $current = $this->GetResource($resource['label']); |
205 if (!$force) foreach ($this->_importResourceFilterCallbacks as $callback) if (!call_user_func($callback, $this, $resource, $current)) return FALSE; | 234 if (!$force && !$this->CanImportResource($resource, $current)) return FALSE; |
206 if (!$force && !MARCUpdate::CanImport($resource, $current)) return FALSE; | |
207 if (!$resource->Verify()) return FALSE; | 235 if (!$resource->Verify()) return FALSE; |
208 if (!$this->ImportInternal($resource)) return FALSE; | 236 if (!$this->ImportInternal($resource)) return FALSE; |
209 $this->ResourceImported($resource); | 237 $this->ResourceImported($resource); |
210 return $resource; | 238 return $resource; |
211 } | 239 } |
240 protected function CanImportResource($resource, $current) { | |
241 foreach ($this->_importResourceFilterCallbacks as $callback) if (!call_user_func($callback, $this, $resource, $current)) return FALSE; | |
242 if (!MARCUpdate::CanImport($resource, $current)) return FALSE; | |
243 return TRUE; | |
244 } | |
245 protected function ResourceImported($resource) { | |
246 foreach ($this->_importResourceCallbacks as $callback) call_user_func($callback, $this, $resource); | |
247 } | |
248 public function RegisterResourceFilterCallback($callback) { | |
249 $this->_importResourceFilterCallbacks[] = $callback; | |
250 } | |
251 public function RegisterResourceImportCallback($callback) { | |
252 $this->_importResourceCallbacks[] = $callback; | |
253 } | |
212 public function UpdateResource($resource, $seckey, $force = FALSE) { | 254 public function UpdateResource($resource, $seckey, $force = FALSE) { |
255 if (is_a($resource, 'MARCUpdate')) $resource = $resource->ToArray(); | |
256 if (!$force) unset($resource['serial'], $resource['key']); | |
213 $res = MARCUpdate::Create($resource, $seckey, $force ? NULL : $this->GetResource($resource['label'])); | 257 $res = MARCUpdate::Create($resource, $seckey, $force ? NULL : $this->GetResource($resource['label'])); |
214 if (!$res) return FALSE; | 258 if (!$res) return FALSE; |
215 return $this->Import($res, $force); | 259 return $this->Import($res, $force); |
216 } | |
217 protected function CanImportResource(&$resource, $force = FALSE) { | |
218 if (is_string($resource)) $resource = MARCUpdate::Decode($resource); | |
219 if (!$resource) return FALSE; | |
220 $current = $this->GetResource($resource['label']); | |
221 if (!$force) foreach ($this->_importResourceFilterCallbacks as $callback) if (!call_user_func($callback, $this, $resource, $current)) return FALSE; | |
222 if (!$force && !MARCUpdate::CanImport($resource, $current)) return FALSE; | |
223 if (!$resource->Verify()) return FALSE; | |
224 return TRUE; | |
225 } | |
226 protected function ResourceImported($resource) { | |
227 foreach ($this->_importResourceCallbacks as $callback) call_user_func($callback, $this, $resource); | |
228 } | |
229 public function RegisterResourceFilterCallback($callback) { | |
230 $this->_importResourceFilterCallbacks[] = $callback; | |
231 } | |
232 public function RegisterResourceImportCallback($callback) { | |
233 $this->_importResourceCallbacks[] = $callback; | |
234 } | 260 } |
235 public function SyncHTTP($server, $options = array()) { | 261 public function SyncHTTP($server, $options = array()) { |
236 $log = isset($options['log']) ? $options['log'] : TRUE; | 262 $log = isset($options['log']) ? $options['log'] : TRUE; |
237 $result = array(); | 263 $result = array(); |
238 $method = isset($options['method']) ? $options['method'] : 'PUT'; | 264 $method = isset($options['method']) ? $options['method'] : 'PUT'; |