Feature: Labelprint für Kistenetiketten hinzugefügt

This commit is contained in:
2025-10-27 12:14:44 +01:00
parent 43bc416554
commit 14bae6c9ef
1068 changed files with 229014 additions and 1807 deletions

View File

@@ -0,0 +1,426 @@
<?php
/**
* Compute.php
*
* @since 2008-01-02
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt;
use Com\Tecnick\Pdf\Encrypt\Exception as EncException;
/**
* Com\Tecnick\Pdf\Encrypt\Compute
*
* PHP class to generate encryption data
*
* @since 2008-01-02
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* @SuppressWarnings("PHPMD.ExcessiveClassComplexity")
*/
abstract class Compute extends \Com\Tecnick\Pdf\Encrypt\Data
{
/**
* Encrypt data using the specified encrypt type.
*
* @param int|string|false $type Encrypt type.
* @param string $data Data string to encrypt.
* @param string $key Encryption key.
* @param int $objnum Object number.
*/
public function encrypt(
int|string|false $type,
string $data = '',
string $key = '',
int $objnum = 0,
): string {
if (empty($this->encryptdata['encrypted']) || ($type === false)) {
return $data;
}
if (! isset(self::ENCMAP[$type])) {
throw new EncException('unknown encryption type: ' . $type);
}
if (($key == '') && ($type == $this->encryptdata['mode'])) {
if ($this->encryptdata['mode'] < 3) {
$key = $this->getObjectKey($objnum);
} elseif ($this->encryptdata['mode'] == 3) {
$key = $this->encryptdata['key'];
}
}
$class = '\\Com\\Tecnick\\Pdf\\Encrypt\\Type\\' . self::ENCMAP[$type];
$obj = new $class();
return $obj->encrypt($data, $key);
}
/**
* Compute encryption key depending on object number where the encrypted data is stored.
* This is used for all strings and streams without crypt filter specifier.
*
* @param int $objnum Object number.
*/
public function getObjectKey(int $objnum): string
{
$objkey = $this->encryptdata['key'] . pack('VXxx', $objnum);
if ($this->encryptdata['mode'] == 2) {
// AES-128 padding
$objkey .= "\x73\x41\x6C\x54"; // sAlT
}
$objkey = substr($this->encrypt('MD5-16', $objkey, 'H*'), 0, (($this->encryptdata['Length'] / 8) + 5));
return substr($objkey, 0, 16);
}
/**
* Convert encryption P value to a string of bytes, low-order byte first.
*
* @param int $protection 32bit encryption permission value (P value).
*/
public function getEncPermissionsString(int $protection): string
{
$binprot = sprintf('%032b', $protection);
return chr((int) bindec(substr($binprot, 24, 8)))
. chr((int) bindec(substr($binprot, 16, 8)))
. chr((int) bindec(substr($binprot, 8, 8)))
. chr((int) bindec(substr($binprot, 0, 8)));
}
/**
* Return the permission code used on encryption (P value).
*
* @param array<string> $permissions The set of permissions (specify the ones you want to block).
* @param int $mode Encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
*/
public function getUserPermissionCode(
array $permissions,
int $mode = 0,
): int {
$protection = 2_147_422_012; // 32 bit: (01111111 11111111 00001111 00111100)
foreach ($permissions as $permission) {
if (! isset(self::PERMBITS[$permission])) {
continue;
}
if ($mode <= 0 && self::PERMBITS[$permission] > 32) {
continue;
}
// set only valid permissions
if (self::PERMBITS[$permission] == 2) {
// the logic for bit 2 is inverted (cleared by default)
$protection += self::PERMBITS[$permission];
} else {
$protection -= self::PERMBITS[$permission];
}
}
return $protection;
}
/**
* Compute UE value
*/
protected function getUEValue(): string
{
$hashkey = hash('sha256', $this->encryptdata['user_password'] . $this->encryptdata['UKS'], true);
return $this->encrypt('AESnopad', $this->encryptdata['key'], $hashkey);
}
/**
* Compute OE value
*/
protected function getOEValue(): string
{
$hashkey = hash(
'sha256',
$this->encryptdata['owner_password'] . $this->encryptdata['OKS'] . $this->encryptdata['U'],
true
);
return $this->encrypt('AESnopad', $this->encryptdata['key'], $hashkey);
}
/**
* Compute U value
*/
protected function getUvalue(): string
{
if ($this->encryptdata['mode'] == 0) { // RC4-40
return $this->encrypt('RC4', self::ENCPAD, $this->encryptdata['key']);
}
if ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
$tmp = $this->encrypt('MD5-16', self::ENCPAD . $this->encryptdata['fileid'], 'H*');
$enc = $this->encrypt('RC4', $tmp, $this->encryptdata['key']);
$len = strlen($tmp);
for ($idx = 1; $idx <= 19; ++$idx) {
$ekey = '';
for ($jdx = 0; $jdx < $len; ++$jdx) {
$ekey .= chr(ord($this->encryptdata['key'][$jdx]) ^ $idx);
}
$enc = $this->encrypt('RC4', $enc, $ekey);
}
$enc .= str_repeat("\x00", 16);
return substr($enc, 0, 32);
}
// AES-256 ($this->encryptdata['mode'] = 3)
$seed = $this->encrypt('MD5-16', $this->encrypt('seed'), 'H*');
// User Validation Salt
$this->encryptdata['UVS'] = substr($seed, 0, 8);
// User Key Salt
$this->encryptdata['UKS'] = substr($seed, 8, 16);
return hash('sha256', $this->encryptdata['user_password'] . $this->encryptdata['UVS'], true)
. $this->encryptdata['UVS'] . $this->encryptdata['UKS'];
}
/**
* Compute O value
*/
protected function getOValue(): string
{
if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
$tmp = $this->encrypt('MD5-16', $this->encryptdata['owner_password'], 'H*');
if ($this->encryptdata['mode'] > 0) {
for ($idx = 0; $idx < 50; ++$idx) {
$tmp = $this->encrypt('MD5-16', $tmp, 'H*');
}
}
$owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
$enc = $this->encrypt('RC4', $this->encryptdata['user_password'], $owner_key);
if ($this->encryptdata['mode'] > 0) {
$len = strlen($owner_key);
for ($idx = 1; $idx <= 19; ++$idx) {
$ekey = '';
for ($jdx = 0; $jdx < $len; ++$jdx) {
$ekey .= chr(ord($owner_key[$jdx]) ^ $idx);
}
$enc = $this->encrypt('RC4', $enc, $ekey);
}
}
return $enc;
}
// AES-256 ($this->encryptdata['mode'] = 3)
$seed = $this->encrypt('MD5-16', $this->encrypt('seed'), 'H*');
// Owner Validation Salt
$this->encryptdata['OVS'] = substr($seed, 0, 8);
// Owner Key Salt
$this->encryptdata['OKS'] = substr($seed, 8, 16);
return hash(
'sha256',
$this->encryptdata['owner_password'] . $this->encryptdata['OVS'] . $this->encryptdata['U'],
true
) . $this->encryptdata['OVS'] . $this->encryptdata['OKS'];
}
/**
* Compute encryption key
*/
protected function generateEncryptionKey(): void
{
if ($this->encryptdata['pubkey']) {
$this->generatePublicEncryptionKey();
} else { // standard mode
$this->generateStandardEncryptionKey();
}
}
/**
* Compute standard encryption key
*/
protected function generateStandardEncryptionKey(): void
{
$keybytelen = ($this->encryptdata['Length'] / 8);
if ($this->encryptdata['mode'] == 3) { // AES-256
// generate 256 bit random key
$this->encryptdata['key'] = substr(hash('sha256', $this->encrypt('seed'), true), 0, $keybytelen);
// truncate passwords
$this->encryptdata['user_password'] = substr($this->encryptdata['user_password'], 0, 127);
$this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'], 0, 127);
$this->encryptdata['U'] = $this->getUValue();
$this->encryptdata['UE'] = $this->getUEValue();
$this->encryptdata['O'] = $this->getOValue();
$this->encryptdata['OE'] = $this->getOEValue();
$this->encryptdata['P'] = $this->encryptdata['protection'];
// Computing the encryption dictionary's Perms (permissions) value
$perms = $this->getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
$perms .= chr(255) . chr(255) . chr(255) . chr(255); // bytes 4-7
$perms .= 'T'; // $this->encryptdata['CF']['EncryptMetadata'] is never set, so we always encrypt
$perms .= 'adb'; // bytes 9-11
$perms .= 'nick'; // bytes 12-15
$this->encryptdata['perms'] = $this->encrypt('AESnopad', $perms, $this->encryptdata['key']);
} else { // RC4-40, RC4-128, AES-128
// Pad passwords
$this->encryptdata['user_password'] = substr($this->encryptdata['user_password'] . self::ENCPAD, 0, 32);
$this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'] . self::ENCPAD, 0, 32);
$this->encryptdata['O'] = $this->getOValue();
// get default permissions (reverse byte order)
$permissions = $this->getEncPermissionsString($this->encryptdata['protection']);
// Compute encryption key
$tmp = $this->encrypt(
'MD5-16',
$this->encryptdata['user_password']
. $this->encryptdata['O']
. $permissions
. $this->encryptdata['fileid'],
'H*'
);
if ($this->encryptdata['mode'] > 0) {
for ($idx = 0; $idx < 50; ++$idx) {
$tmp = $this->encrypt('MD5-16', substr($tmp, 0, $keybytelen), 'H*');
}
}
$this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
$this->encryptdata['U'] = $this->getUValue();
$this->encryptdata['P'] = $this->encryptdata['protection'];
}
}
/**
* Compute public encryption key
*
* @SuppressWarnings("PHPMD.CyclomaticComplexity")
* @SuppressWarnings("PHPMD.NPathComplexity")
* @SuppressWarnings("PHPMD.ExcessiveMethodLength")
*/
protected function generatePublicEncryptionKey(): void
{
$keybytelen = ($this->encryptdata['Length'] / 8);
// random 20-byte seed
$seed = sha1($this->encrypt('seed'), true);
$recipient_bytes = '';
$pubkeys = $this->encryptdata['pubkeys'] ?? throw new EncException('Missing pubkeys');
foreach ($pubkeys as $pubkey) {
// for each public certificate
if (isset($pubkey['p'])) {
$pkprotection = $this->getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
} else {
$pkprotection = $this->encryptdata['protection'];
}
// get default permissions (reverse byte order)
$pkpermissions = $this->getEncPermissionsString($pkprotection);
// envelope data
$envelope = $seed . $pkpermissions;
// write the envelope data to a temporary file
$tempkeyfile = tempnam(
sys_get_temp_dir(),
'__tcpdf_key_' . md5($this->encryptdata['fileid'] . $envelope) . '_'
);
if ($tempkeyfile === false) {
throw new EncException('Unable to generate temporary key file name');
}
if (file_put_contents($tempkeyfile, $envelope) === false) {
throw new EncException('Unable to create temporary key file: ' . $tempkeyfile);
}
$tempencfile = tempnam(
sys_get_temp_dir(),
'__tcpdf_enc_' . md5($this->encryptdata['fileid'] . $envelope) . '_'
);
if ($tempencfile === false) {
throw new EncException('Unable to generate temporary key file name');
}
if (! function_exists('openssl_pkcs7_encrypt')) {
throw new EncException(
'Unable to encrypt the file: ' . $tempkeyfile . "\n"
. 'Public-Key Security requires openssl_pkcs7_encrypt.'
);
}
$pkey = file_get_contents($pubkey['c']);
if ($pkey === false) {
throw new EncException('Unable to read public key file: ' . $pubkey['c']);
}
if (
! openssl_pkcs7_encrypt(
$tempkeyfile,
$tempencfile,
$pkey,
[],
PKCS7_BINARY
)
) {
throw new EncException(
'Unable to encrypt the file: ' . $tempkeyfile . "\n"
. 'OpenSSL error: ' . openssl_error_string()
);
}
// read encryption signature
$signature = file_get_contents($tempencfile);
if ($signature === false) {
throw new EncException('Unable to read signature file: ' . $tempencfile);
}
$sigcontentpos = strpos($signature, 'Content-Disposition');
if ($sigcontentpos === false) {
throw new EncException('Unable to find signature content');
}
// extract signature
$signature = substr($signature, $sigcontentpos);
$tmparr = explode("\n\n", $signature);
$signature = trim($tmparr[1]);
unset($tmparr);
// decode signature
$signature = base64_decode($signature);
if ($signature === false) {
throw new EncException('Unable to decode signature: ' . $tempencfile);
}
$sigarr = unpack('H*', $signature);
if ($sigarr === false) {
throw new EncException('Unable to unpack signature: ' . $tempencfile);
}
// convert signature to hex
$hexsignature = current($sigarr);
if (($hexsignature === false) || (!is_string($hexsignature))) {
throw new EncException('Unable to convert signature: ' . $tempencfile);
}
// store signature on recipients array
$this->encryptdata['Recipients'][] = $hexsignature;
// The bytes of each item in the Recipients array of PKCS#7 objects
// in the order in which they appear in the array
$recipient_bytes .= $signature;
}
// calculate encryption key
if ($this->encryptdata['mode'] == 3) { // AES-256
$this->encryptdata['key'] = substr(hash('sha256', $seed . $recipient_bytes, true), 0, $keybytelen);
} else { // RC4-40, RC4-128, AES-128
$this->encryptdata['key'] = substr(sha1($seed . $recipient_bytes, true), 0, $keybytelen);
}
}
}

View File

@@ -0,0 +1,179 @@
<?php
/**
* Encrypt.php
*
* @since 2008-01-02
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt;
/**
* Com\Tecnick\Pdf\Encrypt\Data
*
* Ecrypt common data
*
* @since 2008-01-02
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
abstract class Data extends \Com\Tecnick\Pdf\Encrypt\Output
{
/**
* Encryption padding string.
*
* @var string
*/
protected const ENCPAD = "\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";
/**
* Map permission modes and bits.
*
* @var array<string, int>
*/
protected const PERMBITS = [
// bit 2 -- inverted logic: cleared by default
// When set permits change of encryption and enables all other permissions.
'owner' => 2,
// bit 3
// Print the document.
'print' => 4,
// bit 4
// Modify the contents of the document by operations other than those controlled
// by 'fill-forms', 'extract' and 'assemble'.
'modify' => 8,
// bit 5
// Copy or otherwise extract text and graphics from the document.
'copy' => 16,
// bit 6
// Add or modify text annotations, fill in interactive form fields, and,
// if 'modify' is also set, create or modify interactive form fields
// (including signature fields).
'annot-forms' => 32,
// bit 9
// Fill in existing interactive form fields (including signature fields),
// even if 'annot-forms' is not specified.
'fill-forms' => 256,
// bit 10
// Extract text and graphics (in support of accessibility to users with
// disabilities or for other purposes).
'extract' => 512,
// bit 11
// Assemble the document (insert, rotate, or delete pages and create bookmarks
// or thumbnail images), even if 'modify' is not set.
'assemble' => 1024,
// bit 12
// Print the document to a representation from which a faithful digital copy of the
// PDF content could be generated. When this is not set, printing is limited to a
// low-level representation of the appearance, possibly of degraded quality.
'print-high' => 2048,
];
/**
* Encryption settings.
*
* @var array<int, array{
* 'V': int,
* 'Length': int,
* 'CF': array{
* 'CFM': string,
* 'Length'?: int,
* 'AuthEvent': string,
* },
* 'SubFilter': string,
* 'Recipients': array<string, string>,
* }>
*/
protected const ENCRYPT_SETTINGS = [
0 => [
// RC4 40 bit
'V' => 1,
'Length' => 40,
'CF' => [
'CFM' => 'V2',
'AuthEvent' => 'DocOpen',
],
'SubFilter' => '',
'Recipients' => [],
],
1 => [
// RC4 128 bit
'V' => 2,
'Length' => 128,
'CF' => [
'CFM' => 'V2',
'AuthEvent' => 'DocOpen',
],
'SubFilter' => 'adbe.pkcs7.s4',
'Recipients' => [],
],
2 => [
// AES 128 bit
'V' => 4,
'Length' => 128,
'CF' => [
'CFM' => 'AESV2',
'Length' => 16,
'AuthEvent' => 'DocOpen',
],
'SubFilter' => 'adbe.pkcs7.s5',
'Recipients' => [],
],
3 => [
// AES 256 bit
'V' => 5,
'Length' => 256,
'CF' => [
'CFM' => 'AESV3',
'Length' => 32,
'AuthEvent' => 'DocOpen',
],
'SubFilter' => 'adbe.pkcs7.s5',
'Recipients' => [],
],
];
/**
* Define a list of available encrypt encoders.
*
* @var array<int|string, string>
*/
protected const ENCMAP = [
0 => 'RCFourFive', // RC4-40
1 => 'RCFourSixteen', // RC4-128
2 => 'AESSixteen', // AES-128
3 => 'AESThirtytwo', // AES-256
'RC4' => 'RCFour', // RC4-40
'RC4-40' => 'RCFourFive', // RC4-40
'RC4-128' => 'RCFourSixteen', // RC4-128
'AES' => 'AES', // AES-256
'AES-128' => 'AESSixteen', // AES-128
'AES-256' => 'AESThirtytwo', // AES-256
'AESnopad' => 'AESnopad', // AES - no padding
'MD5-16' => 'MDFiveSixteen', // MD5-16
'seed' => 'Seed', // Random seed string
];
}

View File

@@ -0,0 +1,272 @@
<?php
/**
* Encrypt.php
*
* @since 2008-01-02
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt;
use Com\Tecnick\Pdf\Encrypt\Exception as EncException;
/**
* Com\Tecnick\Pdf\Encrypt\Encrypt
*
* PHP class for encrypting data for PDF documents
*
* @since 2008-01-02
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* @phpstan-import-type TEncryptData from Output
*/
class Encrypt extends \Com\Tecnick\Pdf\Encrypt\Compute
{
/**
* Set PDF document protection (permission settings)
*
* NOTES: The protection against modification is for people who have the full Acrobat product.
* If you don't set any password, the document will open as usual.
* If you set a user password, the PDF viewer will ask for it before displaying the document.
* The master password, if different from the user one, can be used to get full access.
* Protecting a document requires to encrypt it, which requires long processign time and may cause timeouts.
*
* @param bool $enabled False if the encryption is disabled (i.e. the document is in PDF/A mode)
* @param string $file_id File ID
* @param int $mode Encryption strength: 0 = RC4 40; 1 = RC4 128; 2 = AES 128; 3 = AES 256
* @param array<string> $permissions The set of permissions (specify the ones you want to block):
* 'owner' // When set permits change of encryption and enables all other permissions.
* // (inverted logic: cleared by default).
* 'print' // Print the document.
* 'modify' // Modify the contents of the document by operations other than those controlled
* // by 'fill-forms', 'extract' and 'assemble'.
* 'copy' // Copy or otherwise extract text and graphics from the document.
* 'annot-forms' // Add or modify text annotations, fill in interactive form fields, and,
* // if 'modify' is also set, create or modify interactive form fields
* // (including signature fields).
* 'fill-forms' // Fill in existing interactive form fields (including signature fields),
* // even if 'annot-forms' is not specified.
* 'extract' // Extract text and graphics (in support of accessibility to users with
* // disabilities or for other purposes).
* 'assemble' // Assemble the document (insert, rotate, or delete pages and create bookmarks
* // or thumbnail images), even if 'modify' is not set.
* 'print-high' // Print the document to a representation from which a faithful digital copy of the
* // PDF content could be generated. When this is not set, printing is limited to a
* // low-level representation of the appearance, possibly of degraded quality.
*
* @param string $user_pass User password. Empty by default.
* @param string $owner_pass Owner password. If not specified, a random value is used.
* @param ?array{array{'c':string, 'p':array<string>}} $pubkeys
* Array of recipients containing public-key certificates ('c') and permissions ('p').
* For example:
* array(array('c' => 'file://../examples/data/cert/test.crt', 'p' => array('print')))
* To create self-signed certificate:
* openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout cert.pem -out cert.pem
* To export crt to p12: openssl pkcs12 -export -in cert.pem -out cert.p12
* To convert pfx certificate to pem: openssl pkcs12 -in cert.pfx -out cert.pem -nodes
*/
public function __construct(
bool $enabled = false,
string $file_id = '',
int $mode = 0,
array $permissions = [
'print',
'modify',
'copy',
'annot-forms',
'fill-forms',
'extract',
'assemble',
'print-high',
],
string $user_pass = '',
string $owner_pass = '',
array|null $pubkeys = null
) {
if (! $enabled) {
return;
}
$this->encryptdata['protection'] = $this->getUserPermissionCode($permissions, $mode);
if ($pubkeys !== null && $pubkeys !== []) {
// public-key mode
$this->encryptdata['pubkeys'] = $pubkeys;
if ($mode == 0) {
// public-Key Security requires at least 128 bit
$mode = 1;
}
// Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
$this->encryptdata['pubkey'] = true;
$this->encryptdata['Filter'] = 'Adobe.PubSec';
$this->encryptdata['StmF'] = 'DefaultCryptFilter';
$this->encryptdata['StrF'] = 'DefaultCryptFilter';
} else {
// standard mode (password mode)
$this->encryptdata['pubkey'] = false;
$this->encryptdata['Filter'] = 'Standard';
$this->encryptdata['StmF'] = 'StdCF';
$this->encryptdata['StrF'] = 'StdCF';
}
if ($owner_pass == '') {
$owner_pass = md5($this->encrypt('seed'));
}
$this->encryptdata['user_password'] = $user_pass;
$this->encryptdata['owner_password'] = $owner_pass;
if (($mode < 0) || ($mode > 3)) {
throw new EncException('unknown encryption mode: ' . $this->encryptdata['mode']);
}
$this->encryptdata['mode'] = $mode;
/** @phpstan-ignore-next-line */
$this->encryptdata = array_merge($this->encryptdata, self::ENCRYPT_SETTINGS[$mode]);
if (! $this->encryptdata['pubkey']) {
/** @phpstan-ignore-next-line */
unset($this->encryptdata['SubFilter'], $this->encryptdata['Recipients']);
}
$this->encryptdata['encrypted'] = true;
$this->encryptdata['fileid'] = $this->convertHexStringToString($file_id);
$this->generateEncryptionKey();
}
/**
* Get the encryption data array.
*
* @return TEncryptData
*/
public function getEncryptionData(): array
{
return $this->encryptdata;
}
/**
* Convert hexadecimal string to string.
*
* @param string $bstr Byte-string to convert.
*/
public function convertHexStringToString(string $bstr): string
{
$str = ''; // string to be returned
$bslength = strlen($bstr);
if ($bslength % 2 != 0) {
// padding
$bstr .= '0';
++$bslength;
}
for ($idx = 0; $idx < $bslength; $idx += 2) {
$str .= chr((int) hexdec($bstr[$idx] . $bstr[($idx + 1)]));
}
return $str;
}
/**
* Convert string to hexadecimal string (byte string).
*
* @param string $str String to convert.
*/
public function convertStringToHexString(string $str): string
{
$chars = preg_split('//', $str, -1, PREG_SPLIT_NO_EMPTY);
if ($chars === false) {
return '';
}
$bstr = '';
foreach ($chars as $char) {
$bstr .= sprintf('%02s', dechex(ord($char)));
}
return $bstr;
}
/**
* Encode a name object.
*
* @param string $name Name object to encode.
*/
public function encodeNameObject(string $name): string
{
$escname = '';
$length = strlen($name);
for ($idx = 0; $idx < $length; ++$idx) {
$chr = $name[$idx];
if (preg_match('/[0-9a-zA-Z#_=-]/', $chr) == 1) {
$escname .= $chr;
} else {
$escname .= sprintf('#%02X', ord($chr));
}
}
return $escname;
}
/**
* Encrypt a string.
*
* @param string $str String to encrypt.
* @param int $objnum Object ID.
*/
public function encryptString(
string $str,
int $objnum = 0,
): string {
return $this->encrypt($this->encryptdata['mode'], $str, '', $objnum);
}
/**
* Format a data string for meta information.
*
* @param string $str Data string to escape.
* @param int $objnum Object ID.
*/
public function escapeDataString(
string $str,
int $objnum = 0,
): string {
return '(' . $this->escapeString($this->encryptString($str, $objnum)) . ')';
}
/**
* Returns a formatted date-time.
*
* @param int $time UTC time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
* @param int $objnum Object ID.
*
* @return string escaped date string.
*/
public function getFormattedDate(
int|null $time = null,
int $objnum = 0,
): string {
if ($time === null) {
$time = time(); // get current UTC time
}
return $this->escapeDataString(
'D:' . substr_replace(date('YmdHisO', $time), "'", -2, 0) . "'",
$objnum
);
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* Exception.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt;
/**
* Com\Tecnick\Pdf\Encrypt\Exception
*
* Custom Exception class
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class Exception extends \Exception
{
}

View File

@@ -0,0 +1,271 @@
<?php
/**
* Output.php
*
* @since 2008-01-02
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt;
/**
* Com\Tecnick\Pdf\Encrypt\Output
*
* PHP class for output encrypt PDF object
*
* @since 2008-01-02
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* @phpstan-type TEncryptData array{
* 'CF': array{
* 'AuthEvent': string,
* 'CFM': string,
* 'EncryptMetadata': bool,
* 'Length': int,
* },
* 'EFF': string,
* 'EncryptMetadata': bool,
* 'Filter': string,
* 'Length': int,
* 'O': string,
* 'OE': string,
* 'OKS': string,
* 'OVS': string,
* 'P': int,
* 'Recipients': array<string>,
* 'StmF': string,
* 'StrF': string,
* 'SubFilter': string,
* 'U': string,
* 'UE': string,
* 'UKS': string,
* 'UVS': string,
* 'V': int,
* 'encrypted': bool,
* 'fileid': string,
* 'key': string,
* 'mode': int,
* 'objid': int,
* 'owner_password': string,
* 'perms': string,
* 'protection': int,
* 'pubkey': bool,
* 'pubkeys'?: array{array{'c':string, 'p':array<string>}},
* 'user_password': string,
* }
*/
abstract class Output
{
/**
* Encryption data
*
* @var TEncryptData
*/
protected $encryptdata = [
'CF' => [
'AuthEvent' => '',
'CFM' => '',
'EncryptMetadata' => true,
'Length' => 0,
],
'EFF' => '',
'EncryptMetadata' => true,
'Filter' => '',
'Length' => 0,
'O' => '',
'OE' => '',
'OKS' => '',
'OVS' => '',
'P' => 0,
'Recipients' => [],
'StmF' => '',
'StrF' => '',
'SubFilter' => '',
'U' => '',
'UE' => '',
'UKS' => '',
'UVS' => '',
'V' => 0,
'encrypted' => false,
'fileid' => '',
'key' => '',
'mode' => 0,
'objid' => 0,
'owner_password' => '',
'perms' => '',
'protection' => 0,
'pubkey' => false,
// 'pubkeys' => [],
'user_password' => '',
];
/**
* Escape a string: add "\" before "\", "(" and ")".
*
* @param string $str String to escape.
*/
public function escapeString(string $str): string
{
return strtr($str, [
')' => '\\)',
'(' => '\\(',
'\\' => '\\\\',
chr(13) => '\r',
]);
}
/**
* Get the PDF encryption block
*
* @param int $pon Current PDF object number
*/
public function getPdfEncryptionObj(int &$pon): string
{
$this->setMissingValues();
$this->encryptdata['objid'] = ++$pon;
$out = $this->encryptdata['objid'] . ' 0 obj' . "\n"
. '<<' . "\n"
. '/Filter /' . $this->encryptdata['Filter'] . "\n";
if (! empty($this->encryptdata['SubFilter'])) {
$out .= '/SubFilter /' . $this->encryptdata['SubFilter'] . "\n";
}
// V is a code specifying the algorithm to be used in encrypting and decrypting the document
$out .= '/V ' . $this->encryptdata['V'] . "\n";
// The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
$out .= '/Length ' . $this->encryptdata['Length'] . "\n";
if ($this->encryptdata['V'] >= 4) {
$out .= $this->getCryptFilter();
// The name of the crypt filter that shall be used by default when decrypting streams.
$out .= '/StmF /' . $this->encryptdata['StmF'] . "\n";
// The name of the crypt filter that shall be used when decrypting all strings in the document.
$out .= '/StrF /' . $this->encryptdata['StrF'] . "\n";
/*
if (!empty($this->encryptdata['EFF'])) {
// The name of the crypt filter that shall be used when encrypting embedded file streams
// that do not have their own crypt filter specifier.
$out .= ' /EFF /'.$this->encryptdata['EFF'];
}
*/
}
return $out . ($this->getAdditionalEncDic()
. '>>' . "\n"
. 'endobj' . "\n");
}
/**
* Get Crypt Filter section
*
* A dictionary whose keys shall be crypt filter names
* and whose values shall be the corresponding crypt filter dictionaries.
*/
protected function getCryptFilter(): string
{
$out = '/CF <<' . "\n"
. '/' . $this->encryptdata['StmF'] . ' <<' . "\n"
. '/Type /CryptFilter' . "\n"
. '/CFM /' . $this->encryptdata['CF']['CFM'] . "\n"; // The method used
if ($this->encryptdata['pubkey']) {
$out .= '/Recipients [';
foreach ($this->encryptdata['Recipients'] as $rec) {
$out .= ' <' . $rec . '>';
}
$out .= ' ]' . "\n"
. '/EncryptMetadata '
. $this->getBooleanString($this->encryptdata['CF']['EncryptMetadata']) . "\n";
}
// The event to be used to trigger the authorization
// that is required to access encryption keys used by this filter.
$out .= '/AuthEvent /' . $this->encryptdata['CF']['AuthEvent'] . "\n";
if (! empty($this->encryptdata['CF']['Length'])) {
// The bit length of the encryption key.
$out .= '/Length ' . $this->encryptdata['CF']['Length'] . "\n";
}
return $out . ('>>' . "\n"
. '>>' . "\n");
}
/**
* get additional encryption dictionary entries for the standard security handler
*/
protected function getAdditionalEncDic(): string
{
$out = '';
if ($this->encryptdata['pubkey']) {
if (($this->encryptdata['V'] < 4) && ! empty($this->encryptdata['Recipients'])) {
$out .= ' /Recipients [';
foreach ($this->encryptdata['Recipients'] as $rec) {
$out .= ' <' . $rec . '>';
}
$out .= ' ]' . "\n";
}
} else {
$out .= '/R ';
if ($this->encryptdata['V'] == 5) { // AES-256
$out .= '5' . "\n"
. '/OE (' . $this->escapeString($this->encryptdata['OE']) . ')' . "\n"
. '/UE (' . $this->escapeString($this->encryptdata['UE']) . ')' . "\n"
. '/Perms (' . $this->escapeString($this->encryptdata['perms']) . ')' . "\n";
} elseif ($this->encryptdata['V'] == 4) { // AES-128
$out .= '4' . "\n";
} elseif ($this->encryptdata['V'] < 2) { // RC-40
$out .= '2' . "\n";
} else { // RC-128
$out .= '3' . "\n";
}
$out .= '/O (' . $this->escapeString($this->encryptdata['O']) . ')' . "\n"
. '/U (' . $this->escapeString($this->encryptdata['U']) . ')' . "\n"
. '/P ' . $this->encryptdata['P'] . "\n"
. '/EncryptMetadata '
. $this->getBooleanString($this->encryptdata['EncryptMetadata']) . "\n";
}
return $out;
}
/**
* Return a string representation of a boolean value
*
* @param bool $value Value to convert
*/
protected function getBooleanString(bool $value): string
{
return ($value ? 'true' : 'false');
}
protected function setMissingValues(): void
{
if (! isset($this->encryptdata['EncryptMetadata'])) {
$this->encryptdata['EncryptMetadata'] = true;
}
if (empty($this->encryptdata['CF'])) {
return;
}
if (isset($this->encryptdata['CF']['EncryptMetadata'])) {
return;
}
$this->encryptdata['CF']['EncryptMetadata'] = true;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* AES.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
use Com\Tecnick\Pdf\Encrypt\Exception as EncException;
/**
* Com\Tecnick\Pdf\Encrypt\Type\AES
*
* AES
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class AES
{
/**
* Encrypt the data using OpenSSL
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
* @param string $mode Cipher
*
* @return string encrypted text
*/
public function encrypt(
string $data,
string $key,
string $mode = '',
): string {
if ($mode === '') {
$mode = strlen($key) > 16 ? 'aes-256-cbc' : 'aes-128-cbc';
}
$aesnopad = new AESnopad();
$aesnopad->checkCipher($mode);
$len = openssl_cipher_iv_length($mode);
if ($len === false) {
throw new EncException('openssl_cipher_iv_length failed');
}
$ivect = openssl_random_pseudo_bytes($len);
return $ivect . $aesnopad->encrypt($data, $key, $ivect, $mode);
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* AESSixteen.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
/**
* Com\Tecnick\Pdf\Encrypt\Type\AESSixteen
*
* AESSixteen
* 16 bytes = 128 bit
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class AESSixteen
{
/**
* Encrypt the data using OpenSSL
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
*
* @return string encrypted text
*/
public function encrypt(
string $data,
string $key,
): string {
$aes = new AES();
return $aes->encrypt($data, $key, 'aes-128-cbc');
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* AESThirtytwo.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
/**
* Com\Tecnick\Pdf\Encrypt\Type\AESThirtytwo
*
* AESThirtytwo
* 32 bytes = 256 bit
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class AESThirtytwo
{
/**
* Encrypt the data using OpenSSL
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
*
* @return string encrypted text
*/
public function encrypt(
string $data,
string $key,
): string {
$aes = new AES();
return $aes->encrypt($data, $key, 'aes-256-cbc');
}
}

View File

@@ -0,0 +1,126 @@
<?php
/**
* AESnopad.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
use Com\Tecnick\Pdf\Encrypt\Exception as EncException;
/**
* Com\Tecnick\Pdf\Encrypt\Type\AESnopad
*
* AES no-padding
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class AESnopad
{
/**
* Block size (IV length):
* openssl_cipher_iv_length('aes-256-cbc')
*
* @var int
*/
public const BLOCKSIZE = 16;
/**
* Initialization Vector (16 bytes)
*
* @var string
*/
public const IVECT = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
/**
* List of valid openssl cyphers for AES encryption.
*
* @var array<string>
*/
public const VALID_CIPHERS = [
'aes-128-cbc',
'aes-256-cbc',
];
/**
* Encrypt the data
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
* @param string $ivect Initialization vector
* @param string $mode Cipher
*
* @return string Encrypted data string.
*/
public function encrypt(
string $data,
string $key,
string $ivect = self::IVECT,
string $mode = 'aes-256-cbc'
): string {
$this->checkCipher($mode);
$enc = openssl_encrypt(
$this->pad($data, self::BLOCKSIZE),
$mode,
$this->pad($key, (2 * self::BLOCKSIZE)),
OPENSSL_RAW_DATA,
$ivect
);
if ($enc === false) {
throw new EncException('encryption error: ' . openssl_error_string());
}
return substr($enc, 0, -16);
}
/**
* Pad the input string to the specified length
* (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0)
*
* @param string $data Data to pad
* @param int $length Padding length
*
* @return string Padded string
*/
protected function pad(string $data, int $length): string
{
$padding = ($length - (strlen($data) % $length));
return substr($data . str_repeat("\x00", $padding), 0, $length);
}
/**
* Check if the cipher is valid and available.
*
* @param string $cipher openSSL cipher name.
*
* @throws EncException in case of error.
*/
public function checkCipher(string $cipher): void
{
if (! in_array($cipher, self::VALID_CIPHERS)) {
throw new EncException('invalid chipher: ' . $cipher);
}
if (! in_array($cipher, openssl_get_cipher_methods())) {
throw new EncException('unavailable chipher: ' . $cipher);
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* MDFiveSixteen.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
/**
* Com\Tecnick\Pdf\Encrypt\Type\MDFiveSixteen
*
* MD5-16
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class MDFiveSixteen
{
/**
* Encrypt the data
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
*
* @return string encrypted text
*/
public function encrypt(
string $data,
string $key = 'H*',
): string {
$key = 'H*';
return pack($key, md5($data));
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* RCFour.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
use Com\Tecnick\Pdf\Encrypt\Exception as EncException;
/**
* Com\Tecnick\Pdf\Encrypt\Type\RCFour
*
* RC4 is the standard encryption algorithm used in PDF format
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class RCFour
{
/**
* List of valid openssl cyphers for RC4 encryption.
*
* @var array<string>
*/
public const VALID_CIPHERS = [
'RC4',
'RC4-40',
];
/**
* Encrypt the data using the RC4 (Rivest Cipher 4, also known as ARC4 or ARCFOUR) algorithm.
* RC4 is one of the standard encryption algorithm used in PDF format.
* If possible, please use AES encryption instead as this is insecure.
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
* @param string $mode Cipher
*
* @return string encrypted text
*/
public function encrypt(
string $data,
string $key,
string $mode = '',
): string {
if ($mode === '') {
$mode = strlen($key) > 5 ? 'RC4' : 'RC4-40';
} elseif (! in_array($mode, self::VALID_CIPHERS)) {
throw new EncException('invalid chipher: ' . $mode);
}
if (! in_array($mode, openssl_get_cipher_methods())) {
return $this->rc4($data, $key);
}
$enc = openssl_encrypt($data, $mode, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
if ($enc === false) {
throw new EncException('openssl_encrypt failed');
}
return $enc;
}
/**
* Returns the input text encrypted using RC4 algorithm and the specified key.
* This function is used when the openssl extension is not available.
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
*
* @return string encrypted text
*/
protected function rc4(
string $data,
string $key,
): string {
$pkey = str_repeat($key, (int) ((256 / strlen($key)) + 1));
$rc4 = range(0, 255);
$pos = 0;
for ($idx = 0; $idx < 256; ++$idx) {
$val = $rc4[$idx];
$pos = ($pos + $val + ord($pkey[$idx])) % 256;
$rc4[$idx] = $rc4[$pos];
$rc4[$pos] = $val;
}
$len = strlen($data);
$posa = 0;
$posb = 0;
$out = '';
for ($idx = 0; $idx < $len; ++$idx) {
$posa = ($posa + 1) % 256;
$val = $rc4[$posa];
$posb = ($posb + $val) % 256;
$rc4[$posa] = $rc4[$posb];
$rc4[$posb] = $val;
$pkey = $rc4[($rc4[$posa] + $rc4[$posb]) % 256];
$out .= chr(ord($data[$idx]) ^ $pkey);
}
return $out;
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* RCFourFive.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
/**
* Com\Tecnick\Pdf\Encrypt\Type\RCFourFive
*
* RC4-40 is the standard encryption algorithm used in PDF format
* The key length is 5 bytes (40 bits)
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class RCFourFive
{
/**
* Encrypt the data
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
*
* @return string encrypted text
*/
public function encrypt(
string $data,
string $key,
): string {
$rcFour = new RCFour();
return $rcFour->encrypt($data, $key, 'RC4-40');
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* RCFourSixteen.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
/**
* Com\Tecnick\Pdf\Encrypt\Type\RCFourSixteen
*
* RC4-40 is the standard encryption algorithm used in PDF format
* The key length is 16 bytes (128 bits)
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class RCFourSixteen
{
/**
* Encrypt the data
*
* @param string $data Data string to encrypt
* @param string $key Encryption key
*
* @return string encrypted text
*/
public function encrypt(
string $data,
string $key,
): string {
$rcFour = new RCFour();
return $rcFour->encrypt($data, $key, 'RC4');
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Seed.php
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*
* This file is part of tc-lib-pdf-encrypt software library.
*/
namespace Com\Tecnick\Pdf\Encrypt\Type;
/**
* Com\Tecnick\Pdf\Encrypt\Type\Seed
*
* generate random seed
*
* @since 2011-05-23
* @category Library
* @package PdfEncrypt
* @author Nicola Asuni <info@tecnick.com>
* @copyright 2011-2024 Nicola Asuni - Tecnick.com LTD
* @license http://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE.TXT)
* @link https://github.com/tecnickcom/tc-lib-pdf-encrypt
*/
class Seed
{
/**
* Encrypt the data
*
* @param string $data Random seed data
* @param string $key Random seed data
* @param string $mode Default mode (openssl or raw)
*
* @return string seed
*/
public function encrypt(
string $data = '',
string $key = '',
string $mode = 'openssl',
): string {
$rnd = uniqid(random_int(0, mt_getrandmax()) . microtime(true), true);
if (function_exists('posix_getpid')) {
$rnd .= posix_getpid();
}
if (
($mode == 'openssl')
&& function_exists('openssl_random_pseudo_bytes')
&& (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
) {
// this is not used on windows systems because it is very slow for a know bug
$rnd .= openssl_random_pseudo_bytes(512);
} else {
for ($idx = 0; $idx < 23; ++$idx) {
$rnd .= uniqid('', true);
}
}
return $rnd . $data . __DIR__ . __FILE__ . $key . serialize($_SERVER) . microtime(true);
}
}