Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.24% covered (success)
95.24%
20 / 21
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
AesCipher
95.24% covered (success)
95.24%
20 / 21
66.67% covered (warning)
66.67%
2 / 3
8
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 encrypt
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
2.01
 decrypt
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Crypt;
6
7/**
8 * AES-CBC cipher for PDF encryption (ISO 32000-2 ยง7.6.3).
9 *
10 * Supports 128-bit and 256-bit keys. The 16-byte IV is prepended to
11 * the ciphertext on encrypt and stripped on decrypt, per the PDF spec
12 * requirement that each encrypted string/stream carries its own IV.
13 */
14final class AesCipher implements CryptInterface
15{
16    public function __construct(private int $keyBits = 128)
17    {
18        if ($keyBits !== 128 && $keyBits !== 256) {
19            throw new \InvalidArgumentException("keyBits must be 128 or 256, got $keyBits");
20        }
21    }
22
23    public function encrypt(string $data, string $key): string
24    {
25        $iv = random_bytes(16);
26        $cipherMethod = "AES-{$this->keyBits}-CBC";
27        // Pad or truncate key to required length
28        $keyBytes = $this->keyBits / 8;
29        $key = str_pad(substr($key, 0, $keyBytes), $keyBytes, "\x00");
30
31        $encrypted = openssl_encrypt($data, $cipherMethod, $key, OPENSSL_RAW_DATA, $iv);
32        if ($encrypted === false) {
33            throw new \RuntimeException('AES encryption failed: ' . openssl_error_string());
34        }
35        return $iv . $encrypted;
36    }
37
38    public function decrypt(string $data, string $key): string
39    {
40        if (strlen($data) < 16) {
41            throw new \RuntimeException('AES decrypt: data too short (missing IV)');
42        }
43        $iv = substr($data, 0, 16);
44        $ciphertext = substr($data, 16);
45        $cipherMethod = "AES-{$this->keyBits}-CBC";
46        $keyBytes = $this->keyBits / 8;
47        $key = str_pad(substr($key, 0, $keyBytes), $keyBytes, "\x00");
48
49        $decrypted = openssl_decrypt($ciphertext, $cipherMethod, $key, OPENSSL_RAW_DATA, $iv);
50        if ($decrypted === false) {
51            throw new \RuntimeException('AES decryption failed: ' . openssl_error_string());
52        }
53        return $decrypted;
54    }
55}