| 1 |
efrain |
1 |
<?php
|
|
|
2 |
namespace Aws\Glacier;
|
|
|
3 |
|
|
|
4 |
use Aws\HashInterface;
|
|
|
5 |
|
|
|
6 |
/**
|
|
|
7 |
* Encapsulates the creation of a tree hash from streamed data
|
|
|
8 |
*/
|
|
|
9 |
class TreeHash implements HashInterface
|
|
|
10 |
{
|
|
|
11 |
const MB = 1048576;
|
|
|
12 |
const EMPTY_HASH = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
|
|
|
13 |
|
|
|
14 |
/** @var string Algorithm used for hashing. */
|
|
|
15 |
private $algorithm;
|
|
|
16 |
|
|
|
17 |
/** @var string Buffered data that has not yet been hashed. */
|
|
|
18 |
private $buffer;
|
|
|
19 |
|
|
|
20 |
/** @var array Binary checksums from which the tree hash is derived. */
|
|
|
21 |
private $checksums = [];
|
|
|
22 |
|
|
|
23 |
/** @var string Resulting hash in binary form. */
|
|
|
24 |
private $hash;
|
|
|
25 |
|
|
|
26 |
public function __construct($algorithm = 'sha256')
|
|
|
27 |
{
|
|
|
28 |
$this->algorithm = $algorithm;
|
|
|
29 |
$this->reset();
|
|
|
30 |
}
|
|
|
31 |
|
|
|
32 |
/**
|
|
|
33 |
* {@inheritdoc}
|
|
|
34 |
* @throws \LogicException if the root tree hash is already calculated
|
|
|
35 |
*/
|
|
|
36 |
public function update($data)
|
|
|
37 |
{
|
|
|
38 |
// Error if hash is already calculated.
|
|
|
39 |
if ($this->hash) {
|
|
|
40 |
throw new \LogicException('You may not add more data to a '
|
|
|
41 |
. 'complete tree hash.');
|
|
|
42 |
}
|
|
|
43 |
|
|
|
44 |
// Buffer incoming data.
|
|
|
45 |
$this->buffer .= $data;
|
|
|
46 |
|
|
|
47 |
// When there is more than a MB of data, create a checksum.
|
|
|
48 |
while (strlen($this->buffer) >= self::MB) {
|
|
|
49 |
$data = substr($this->buffer, 0, self::MB);
|
|
|
50 |
$this->buffer = substr($this->buffer, self::MB) ?: '';
|
|
|
51 |
$this->checksums[] = hash($this->algorithm, $data, true);
|
|
|
52 |
}
|
|
|
53 |
|
|
|
54 |
return $this;
|
|
|
55 |
}
|
|
|
56 |
|
|
|
57 |
/**
|
|
|
58 |
* Add a checksum to the tree hash directly
|
|
|
59 |
*
|
|
|
60 |
* @param string $checksum The checksum to add
|
|
|
61 |
* @param bool $inBinaryForm TRUE if checksum is in binary form
|
|
|
62 |
*
|
|
|
63 |
* @return self
|
|
|
64 |
* @throws \LogicException if the root tree hash is already calculated
|
|
|
65 |
*/
|
|
|
66 |
public function addChecksum($checksum, $inBinaryForm = false)
|
|
|
67 |
{
|
|
|
68 |
// Error if hash is already calculated
|
|
|
69 |
if ($this->hash) {
|
|
|
70 |
throw new \LogicException('You may not add more checksums to a '
|
|
|
71 |
. 'complete tree hash.');
|
|
|
72 |
}
|
|
|
73 |
|
|
|
74 |
// Convert the checksum to binary form if necessary
|
|
|
75 |
$this->checksums[] = $inBinaryForm ? $checksum : hex2bin($checksum);
|
|
|
76 |
|
|
|
77 |
return $this;
|
|
|
78 |
}
|
|
|
79 |
|
|
|
80 |
public function complete()
|
|
|
81 |
{
|
|
|
82 |
if (!$this->hash) {
|
|
|
83 |
// Clear out the remaining buffer.
|
|
|
84 |
if (strlen($this->buffer) > 0) {
|
|
|
85 |
$this->checksums[] = hash($this->algorithm, $this->buffer, true);
|
|
|
86 |
$this->buffer = '';
|
|
|
87 |
}
|
|
|
88 |
|
|
|
89 |
// If no hashes, add the EMPTY_HASH.
|
|
|
90 |
if (!$this->checksums) {
|
|
|
91 |
$this->checksums[] = hex2bin(self::EMPTY_HASH);
|
|
|
92 |
}
|
|
|
93 |
|
|
|
94 |
// Perform hashes up the tree to arrive at the root checksum.
|
|
|
95 |
$hashes = $this->checksums;
|
|
|
96 |
while (count($hashes) > 1) {
|
|
|
97 |
$sets = array_chunk($hashes, 2);
|
|
|
98 |
$hashes = array();
|
|
|
99 |
foreach ($sets as $set) {
|
|
|
100 |
$hashes[] = (count($set) === 1)
|
|
|
101 |
? $set[0]
|
|
|
102 |
: hash($this->algorithm, $set[0] . $set[1], true);
|
|
|
103 |
}
|
|
|
104 |
}
|
|
|
105 |
|
|
|
106 |
$this->hash = $hashes[0];
|
|
|
107 |
}
|
|
|
108 |
|
|
|
109 |
return $this->hash;
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
public function reset()
|
|
|
113 |
{
|
|
|
114 |
$this->hash = null;
|
|
|
115 |
$this->checksums = [];
|
|
|
116 |
$this->buffer = '';
|
|
|
117 |
}
|
|
|
118 |
}
|