1<?php 2/* 3 * Copyright 2008-2010 GuardTime AS 4 * 5 * This file is part of the GuardTime PHP SDK. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20/** 21 * @package tsp 22 */ 23 24/** 25 * Data hash object used as seed when creating and verifying timestamps. 26 * 27 * To calculate the hash sum of some data, first create a data hash object and then 28 * add data to it by using the update methods. Do this until all the data has been 29 * fed to the hash calculator. You can then retrieve the resulting hash value using 30 * getHashedMessage() method but this closes the hash object and you can't add more 31 * data after that. 32 * 33 * Example 1 - new hash: 34 * 35 * <code> 36 * $stream = fopen('http://www.guardtime.com/data.txt', 'r'); 37 * 38 * $hash = new GTDataHash(GTHashAlgorithm::getByName('DEFAULT')); 39 * $hash->update(array(0, 1, 2)); 40 * $hash->updateFile('data.txt'); 41 * $hahs->updateStream($stream); 42 * $hash->close(); 43 * 44 * print_r($hash->getHashedMessage()); 45 * </code> 46 * 47 * Example 2 - hash with existing hash value: 48 * 49 * <code> 50 * $bytes = array( 51 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 52 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 53 * ); 54 * 55 * $hash = new GTDataHash(GTHashAlgorithm::getByName('SHA256'), $bytes); 56 * </code> 57 * 58 * Example 3 - hash from dataImprint: 59 * 60 * <code> 61 * $dataImprint = array( 62 * GTHashAlgorithm::getByName('SHA256')->getGtid(), 63 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 64 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 65 * ); 66 * 67 * $hash = GTDataHash::getInstance($dataImprint); 68 * </code> 69 * 70 * @package tsp 71 */ 72class GTDataHash { 73 74 private $hashContext; 75 private $hashAlgorithm; 76 private $hashedMessage; 77 78 /** 79 * Builds a new hash object with the given hashAlgorithm and hashedMessage. 80 * 81 * When hashedMessage is null the GTDataHash will be open and it will be 82 * possible to add more data using update methods. 83 * 84 * When hashedMessage is specified the GTDataHash will be closed and it's 85 * not possible to add more data using update methods. 86 * 87 * 88 * @throws GTException 89 * @param GTHashAlgorithm $hashAlgorithm hash algorithm to use 90 * @param array $hashedMessage hash bytes 91 */ 92 public function __construct($hashAlgorithm, $hashedMessage = null) { 93 94 if (empty($hashAlgorithm)) { 95 throw new GTException("parameter hashAlgorithm must not be empty"); 96 } 97 98 if (!($hashAlgorithm instanceof GTHashAlgorithm)) { 99 throw new GTException("parameter hashAlgorithm must be an instance of GTHashAlgorithm"); 100 } 101 102 $this->hashAlgorithm = $hashAlgorithm; 103 104 if ($hashedMessage == null) { 105 106 $this->hashContext = hash_init($this->hashAlgorithm->getName()); 107 108 if (!is_resource($this->hashContext)) { 109 throw new GTException("Unable to init GTDataHash with algorithm: {$this->hashAlgorithm->getName()}"); 110 } 111 112 } else { 113 114 if (!is_array($hashedMessage)) { 115 throw new GTException("hash must be an array of bytes"); 116 } 117 118 if (count($hashedMessage) != $this->hashAlgorithm->getLength()) { 119 throw new GTException("hash length does not match with that defined in hash algorithm"); 120 } 121 122 $this->hashedMessage = $hashedMessage; 123 124 } 125 126 } 127 128 /** 129 * Updates this hash calculator with the given bytes. 130 * 131 * @throws GTException 132 * @param $bytes byte array to feed to this hash calculator 133 * @return void 134 */ 135 public function update($bytes) { 136 137 if ($this->isClosed()) { 138 throw new GTException("hash calculator already closed"); 139 } 140 141 if (empty($bytes)) { 142 throw new GTException("parameter bytes must not be empty"); 143 } 144 145 $result = hash_update($this->hashContext, GTUtil::fromByteArray($bytes)); 146 147 if (!$result) { 148 throw new GTException("hash update failed with unknown error"); 149 } 150 } 151 152 /** 153 * Updates this hash calculator with the bytes from the given file. 154 * 155 * @throws GTException 156 * @param string $file file to feed to this hash calculator 157 * @return void 158 */ 159 public function updateFile($file) { 160 161 if ($this->isClosed()) { 162 throw new GTException("hash calculator already closed"); 163 } 164 165 if (empty($file)) { 166 throw new GTException("parameter file must not be empty"); 167 } 168 169 if (!is_file($file)) { 170 throw new GTException("file must be a file"); 171 } 172 173 if (!is_readable($file)) { 174 throw new GTException("file must be readable"); 175 } 176 177 if (filesize($file) == 0) { 178 throw new GTException("file must not be 0 bytes"); 179 } 180 181 $result = hash_update_file($this->hashContext, $file); 182 183 if (!$result) { 184 throw new GTException("hash updateFile failed with unknown error"); 185 } 186 187 } 188 189 /** 190 * Updates this hash calculator with the bytes from the given stream. 191 * 192 * @throws GTException 193 * @param $handle handle of the stream to feed to this hash calculator 194 * @return void 195 */ 196 public function updateStream($handle) { 197 198 if ($this->isClosed()) { 199 throw new GTException("hash calculator already closed"); 200 } 201 202 if (empty($handle)) { 203 throw new GTException("parameter handle must not be empty"); 204 } 205 206 if (!is_resource($handle)) { 207 throw new GTException("parameter handle must be a resource"); 208 } 209 210 $result = hash_update_stream($this->hashContext, $handle); 211 212 if (!$result) { 213 throw new GTException("hash updateStream failed with unknown error"); 214 } 215 216 } 217 218 /** 219 * Explicitly closes this hash calculator. 220 * 221 * After the hash calculator is closed all calls to update methods will throw an exception. 222 * 223 * @return GTDataHash 224 */ 225 public function close() { 226 227 if (!$this->isClosed()) { 228 229 $this->hashedMessage = GTUtil::toByteArray(hash_final($this->hashContext, true)); 230 $this->hashContext = null; 231 232 } 233 234 return $this; 235 } 236 237 /** 238 * Checks if this hash calculator is closed. 239 * 240 * @return bool true if this hash calculator is closed. 241 */ 242 public function isClosed() { 243 return $this->hashContext == null; 244 } 245 246 /** 247 * Gets the GTHashAlgorithm used by this hash calculator. 248 * 249 * @return GTHashAlgorithm the currently used algorithm 250 */ 251 public function getHashAlgorithm() { 252 return $this->hashAlgorithm; 253 } 254 255 /** 256 * Gets the current hash bytes. 257 * 258 * This method will also close the hash calculator when called. After closing 259 * all update() methods will throw an exception. 260 * 261 * @return array 262 */ 263 public function getHashedMessage() { 264 265 if ($this->isClosed()) { 266 $this->close(); 267 } 268 269 return $this->hashedMessage; 270 } 271 272 /** 273 * Gets the current hash data imprint. 274 * 275 * This method will also close the hash calculator when called. After closing 276 * all update() methods will throw an exception. 277 * 278 * Data imprint is an array of bytes consisting of GTHashAlgorithm->getGtid() + hash bytes 279 * 280 * @return array the data imprint 281 */ 282 public function getDataImprint() { 283 284 if (!$this->isClosed()) { 285 $this->close(); 286 } 287 288 $result = array($this->hashAlgorithm->getGtid()); 289 290 foreach ($this->hashedMessage as $byte) { 291 array_push($result, $byte); 292 } 293 294 return $result; 295 } 296 297 /** 298 * Gets a GTDataHash instance from a $dataImprint. 299 * 300 * DataImprint is an array of bytes consisting of GTHashAlgorithm->getGtid() and hash bytes. 301 * 302 * <code> 303 * $dataImprint = array( 304 * GTHashAlgorithm::getByName('SHA256')->getGtid(), 305 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 306 * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 307 * ); 308 * </code> 309 * 310 * @static 311 * @throws GTException 312 * @param array $dataImprint dataImprint bytes 313 * @return GTDataHash 314 */ 315 public static function getInstance($dataImprint) { 316 317 if (empty($dataImprint)) { 318 throw new GTException("parameter dataImprint must not be empty"); 319 } 320 321 if (!is_array($dataImprint)) { 322 throw new GTException("parameter dataImprint must be an array of bytes"); 323 } 324 325 if (count($dataImprint) < 2) { 326 throw new GTException("parameter dataImprint must be at least 2 bytes long"); 327 } 328 329 $hashAlgorithm = GTHashAlgorithm::getByGtid($dataImprint[0]); 330 $hashedMessage = array_slice($dataImprint, 1); 331 332 return new GTDataHash($hashAlgorithm, $hashedMessage); 333 } 334 335} 336 337?> 338