/** * Verifies that the Merkle root exists, matches this package and version, * and has the same checksum as the one we calculated. * * @param InstallFile $file * @return bool */ public function verifyMerkleRoot(InstallFile $file) : bool { $debugArgs = ['supplier' => $this->supplier->getName(), 'name' => $this->package]; $db = \Airship\get_database(); $merkle = $db->row('SELECT * FROM airship_tree_updates WHERE merkleroot = ?', $file->getMerkleRoot()); if (empty($merkle)) { $this->log('Merkle root not found in tree', LogLevel::DEBUG, $debugArgs); // Not found in Keyggdrasil return false; } $data = \Airship\parseJSON($merkle['data'], true); $instType = \strtolower($this->type); $keyggdrasilType = \strtolower($data['pkg_type']); if (!\hash_equals($instType, $keyggdrasilType)) { $this->log('Wrong package type', LogLevel::DEBUG, $debugArgs); // Wrong package type return false; } if (!\hash_equals($this->supplier->getName(), $data['supplier'])) { $this->log('Wrong supplier', LogLevel::DEBUG, $debugArgs); // Wrong supplier return false; } if (!\hash_equals($this->package, $data['name'])) { $this->log('Wrong package', LogLevel::DEBUG, $debugArgs); // Wrong package return false; } // Finally, we verify that the checksum matches the entry in our Merkle tree: return \hash_equals($file->getHash(), $data['checksum']); }
/** * Are any updates available? * * @param string $supplier * @param string $packageName * @param string $minVersion * @param string $apiEndpoint * * @return UpdateInfo[] * * @throws \Airship\Alerts\Hail\NoAPIResponse */ public function updateCheck(string $supplier = '', string $packageName = '', string $minVersion = '', string $apiEndpoint = 'version') : array { if (empty($supplier)) { $supplier = $this->supplier->getName(); } $channelsConfigured = $this->supplier->getChannels(); if (empty($channelsConfigured)) { throw new NoAPIResponse(\trk('errors.hail.no_channel_configured')); } foreach ($channelsConfigured as $channelName) { $channel = $this->getChannel($channelName); $publicKey = $channel->getPublicKey(); foreach ($channel->getAllURLs() as $ch) { try { $response = $this->hail->postSignedJSON($ch . API::get($apiEndpoint), $publicKey, ['type' => $this->type, 'supplier' => $supplier, 'package' => $packageName, 'minimum' => $minVersion]); if ($response['status'] === 'error') { $this->log($response['error'], LogLevel::ERROR, ['response' => $response, 'channel' => $ch, 'supplier' => $supplier, 'type' => $this->type, 'package' => $packageName]); continue; } $updates = []; foreach ($response['versions'] as $update) { $updates[] = new UpdateInfo($update, $ch, $publicKey, $supplier, $packageName); } if (empty($updates)) { $this->log('No updates found.', LogLevel::DEBUG, ['type' => \get_class($this), 'supplier' => $supplier, 'package' => $packageName, 'channelName' => $channelName, 'channel' => $ch]); return []; } return $this->sortUpdatesByVersion(...$updates); } catch (SignatureFailed $ex) { // Log? Definitely suppress, however. $this->log('Automatic update - signature failure. (' . \get_class($ex) . ')', LogLevel::ALERT, ['exception' => \Airship\throwableToArray($ex), 'channelName' => $channelName, 'channel' => $ch]); } catch (TransferException $ex) { // Log? Definitely suppress, however. $this->log('Automatic update failure. (' . \get_class($ex) . ')', LogLevel::WARNING, ['exception' => \Airship\throwableToArray($ex), 'channelName' => $channelName, 'channel' => $ch]); } } } throw new NoAPIResponse(\trk('errors.hail.no_channel_responded')); }