/wp-content/plugins/dk-pdf/vendor/mpdf/mpdf/src/Pdf/Protection.php
https://github.com/livinglab/openlab · PHP · 360 lines · 226 code · 57 blank · 77 comment · 34 complexity · 27571bbd9ba798dee96f1a6bdf4764f5 MD5 · raw file
- <?php
- namespace Mpdf\Pdf;
- use Mpdf\Pdf\Protection\UniqidGenerator;
- class Protection
- {
- /**
- * @var string
- */
- private $lastRc4Key;
- /**
- * @var string
- */
- private $lastRc4KeyC;
- /**
- * @var bool
- */
- private $useRC128Encryption;
- /**
- * @var string
- */
- private $encryptionKey;
- /**
- * @var string
- */
- private $padding;
- /**
- * @var string
- */
- private $uniqid;
- /**
- * @var string
- */
- private $oValue;
- /**
- * @var string
- */
- private $uValue;
- /**
- * @var string
- */
- private $pValue;
- /**
- * @var int[] Array of permission => byte representation
- */
- private $options;
- /**
- * @var \Mpdf\Pdf\Protection\UniqidGenerator
- */
- private $uniqidGenerator;
- public function __construct(UniqidGenerator $uniqidGenerator)
- {
- if (!function_exists('random_int') || !function_exists('random_bytes')) {
- throw new \Mpdf\MpdfException(
- 'Unable to set PDF file protection, CSPRNG Functions are not available. '
- . 'Use paragonie/random_compat polyfill or upgrade to PHP 7.'
- );
- }
- $this->uniqidGenerator = $uniqidGenerator;
- $this->lastRc4Key = '';
- $this->padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08" .
- "\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
- $this->useRC128Encryption = false;
- $this->options = [
- 'print' => 4, // bit 3
- 'modify' => 8, // bit 4
- 'copy' => 16, // bit 5
- 'annot-forms' => 32, // bit 6
- 'fill-forms' => 256, // bit 9
- 'extract' => 512, // bit 10
- 'assemble' => 1024, // bit 11
- 'print-highres' => 2048 // bit 12
- ];
- }
- /**
- * @param array $permissions
- * @param string $user_pass
- * @param string $owner_pass
- * @param int $length
- *
- * @return bool
- */
- public function setProtection($permissions = [], $user_pass = '', $owner_pass = null, $length = 40)
- {
- if (is_string($permissions) && strlen($permissions) > 0) {
- $permissions = [$permissions];
- } elseif (!is_array($permissions)) {
- return false;
- }
- $protection = $this->getProtectionBitsFromOptions($permissions);
- if ($length === 128) {
- $this->useRC128Encryption = true;
- } elseif ($length !== 40) {
- throw new \Mpdf\MpdfException('PDF protection only allows lenghts of 40 or 128');
- }
- if ($owner_pass === null) {
- $owner_pass = bin2hex(random_bytes(23));
- }
- $this->generateEncryptionKey($user_pass, $owner_pass, $protection);
- return true;
- }
- /**
- * Compute key depending on object number where the encrypted data is stored
- *
- * @param int $n
- *
- * @return string
- */
- public function objectKey($n)
- {
- if ($this->useRC128Encryption) {
- $len = 16;
- } else {
- $len = 10;
- }
- return substr($this->md5toBinary($this->encryptionKey . pack('VXxx', $n)), 0, $len);
- }
- /**
- * RC4 is the standard encryption algorithm used in PDF format
- *
- * @param string $key
- * @param string $text
- *
- * @return string
- */
- public function rc4($key, $text)
- {
- if ($this->lastRc4Key != $key) {
- $k = str_repeat($key, 256 / strlen($key) + 1);
- $rc4 = range(0, 255);
- $j = 0;
- for ($i = 0; $i < 256; $i++) {
- $t = $rc4[$i];
- $j = ($j + $t + ord($k[$i])) % 256;
- $rc4[$i] = $rc4[$j];
- $rc4[$j] = $t;
- }
- $this->lastRc4Key = $key;
- $this->lastRc4KeyC = $rc4;
- } else {
- $rc4 = $this->lastRc4KeyC;
- }
- $len = strlen($text);
- $a = 0;
- $b = 0;
- $out = '';
- for ($i = 0; $i < $len; $i++) {
- $a = ($a + 1) % 256;
- $t = $rc4[$a];
- $b = ($b + $t) % 256;
- $rc4[$a] = $rc4[$b];
- $rc4[$b] = $t;
- $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
- $out .= chr(ord($text[$i]) ^ $k);
- }
- return $out;
- }
- /**
- * @return mixed
- */
- public function getUseRC128Encryption()
- {
- return $this->useRC128Encryption;
- }
- /**
- * @return mixed
- */
- public function getUniqid()
- {
- return $this->uniqid;
- }
- /**
- * @return mixed
- */
- public function getOValue()
- {
- return $this->oValue;
- }
- /**
- * @return mixed
- */
- public function getUValue()
- {
- return $this->uValue;
- }
- /**
- * @return mixed
- */
- public function getPValue()
- {
- return $this->pValue;
- }
- private function getProtectionBitsFromOptions($permissions)
- {
- // bit 31 = 1073741824
- // bit 32 = 2147483648
- // bits 13-31 = 2147479552
- // bits 13-32 = 4294963200 + 192 = 4294963392
- $protection = 4294963392; // bits 7, 8, 13-32
- foreach ($permissions as $permission) {
- if (!isset($this->options[$permission])) {
- throw new \Mpdf\MpdfException(sprintf('Invalid permission type "%s"', $permission));
- }
- if ($this->options[$permission] > 32) {
- $this->useRC128Encryption = true;
- }
- if (isset($this->options[$permission])) {
- $protection += $this->options[$permission];
- }
- }
- return $protection;
- }
- private function oValue($user_pass, $owner_pass)
- {
- $tmp = $this->md5toBinary($owner_pass);
- if ($this->useRC128Encryption) {
- for ($i = 0; $i < 50; ++$i) {
- $tmp = $this->md5toBinary($tmp);
- }
- }
- if ($this->useRC128Encryption) {
- $keybytelen = (128 / 8);
- } else {
- $keybytelen = (40 / 8);
- }
- $owner_rc4_key = substr($tmp, 0, $keybytelen);
- $enc = $this->rc4($owner_rc4_key, $user_pass);
- if ($this->useRC128Encryption) {
- $len = strlen($owner_rc4_key);
- for ($i = 1; $i <= 19; ++$i) {
- $key = '';
- for ($j = 0; $j < $len; ++$j) {
- $key .= chr(ord($owner_rc4_key{$j}) ^ $i);
- }
- $enc = $this->rc4($key, $enc);
- }
- }
- return $enc;
- }
- private function uValue()
- {
- if ($this->useRC128Encryption) {
- $tmp = $this->md5toBinary($this->padding . $this->hexToString($this->uniqid));
- $enc = $this->rc4($this->encryptionKey, $tmp);
- $len = strlen($tmp);
- for ($i = 1; $i <= 19; ++$i) {
- $key = '';
- for ($j = 0; $j < $len; ++$j) {
- $key .= chr(ord($this->encryptionKey{$j}) ^ $i);
- }
- $enc = $this->rc4($key, $enc);
- }
- $enc .= str_repeat("\x00", 16);
- return substr($enc, 0, 32);
- } else {
- return $this->rc4($this->encryptionKey, $this->padding);
- }
- }
- private function generateEncryptionKey($user_pass, $owner_pass, $protection)
- {
- // Pad passwords
- $user_pass = substr($user_pass . $this->padding, 0, 32);
- $owner_pass = substr($owner_pass . $this->padding, 0, 32);
- $this->oValue = $this->oValue($user_pass, $owner_pass);
- $this->uniqid = $this->uniqidGenerator->generate();
- // Compute encyption key
- if ($this->useRC128Encryption) {
- $keybytelen = (128 / 8);
- } else {
- $keybytelen = (40 / 8);
- }
- $prot = sprintf('%032b', $protection);
- $perms = chr(bindec(substr($prot, 24, 8)));
- $perms .= chr(bindec(substr($prot, 16, 8)));
- $perms .= chr(bindec(substr($prot, 8, 8)));
- $perms .= chr(bindec(substr($prot, 0, 8)));
- $tmp = $this->md5toBinary($user_pass . $this->oValue . $perms . $this->hexToString($this->uniqid));
- if ($this->useRC128Encryption) {
- for ($i = 0; $i < 50; ++$i) {
- $tmp = $this->md5toBinary(substr($tmp, 0, $keybytelen));
- }
- }
- $this->encryptionKey = substr($tmp, 0, $keybytelen);
- $this->uValue = $this->uValue();
- $this->pValue = $protection;
- }
- private function md5toBinary($string)
- {
- return pack('H*', md5($string));
- }
- private function hexToString($hs)
- {
- $s = '';
- $len = strlen($hs);
- if (($len % 2) != 0) {
- $hs .= '0';
- ++$len;
- }
- for ($i = 0; $i < $len; $i += 2) {
- $s .= chr(hexdec($hs{$i} . $hs{($i + 1)}));
- }
- return $s;
- }
- }