cryptal  latest
Cryptography Abstraction Layer
Crypto.php
1 <?php
2 
4 
11 
12 class Crypto extends \php_user_filter
13 {
14  protected $blockSize;
15  protected $padding;
16  protected $mode;
17  protected $method;
18  protected $buffer;
19 
20  public function onCreate()
21  {
22  if (!isset($this->params['algorithm']) ||
23  !is_object($this->params['algorithm']) ||
24  !($this->params['algorithm'] instanceof CipherEnum)) {
25  throw new \InvalidArgumentException('Invalid algorithm');
26  }
27 
28  if (!isset($this->params['mode']) ||
29  !is_object($this->params['mode']) ||
30  !($this->params['mode'] instanceof ModeEnum)) {
31  throw new \InvalidArgumentException('Invalid mode');
32  }
33 
34  if (!isset($this->params['key']) || !is_string($this->params['key'])) {
35  throw new \InvalidArgumentException('Missing or invalid key');
36  }
37 
38  $padding = new \fpoirotte\Cryptal\Padding\None();
39  if (isset($this->params['padding'])) {
40  $padding = $this->params['padding'];
41  }
42  if (!is_object($padding) || !($padding instanceof PaddingInterface)) {
43  throw new \InvalidArgumentException('Invalid padding scheme');
44  }
45 
46  $tagLength = CryptoInterface::DEFAULT_TAG_LENGTH;
47  if (isset($this->params['tagLength'])) {
48  $tagLength = $this->params['tagLength'];
49  }
50  if (!is_integer($tagLength) || $tagLength < 0) {
51  throw new \InvalidArgumentException('Invalid tag length');
52  }
53 
54  $iv = isset($this->params['iv']) ? $this->params['iv'] : '';
55  if (!is_string($iv)) {
56  throw new \InvalidArgumentException('Invalid initialization vector');
57  }
58 
59  // Make sure the selected mode is supported.
60  if (!isset($this->params['mode'])) {
61  throw new \InvalidArgumentException('No mode specified');
62  }
63  $mode = "\\fpoirotte\\Cryptal\\Modes\\" . substr($this->params['mode'], strlen('MODE_'));
64  $interfaces = class_implements($mode, true);
65  if (!$interfaces || !in_array("fpoirotte\Cryptal\SymmetricModeInterface", $interfaces)) {
66  throw new \InvalidArgumentException('Unsupported mode');
67  }
68 
69  $allowUnsafe = isset($this->params['allowUnsafe']) ? (bool) $this->params['allowUnsafe'] : false;
70  $cipher = Registry::buildCipher(
71  $this->params['algorithm'],
72  ModeEnum::MODE_ECB(),
73  new \fpoirotte\Cryptal\Padding\None(),
74  $this->params['key'],
75  $tagLength,
76  $allowUnsafe
77  );
78 
79  $this->buffer = '';
80  $this->blockSize = $cipher->getBlockSize();
81  $this->padding = $padding;
82  $this->mode = new $mode(
83  $cipher,
84  $iv,
85  $tagLength
86  );
87  if ('cryptal.decrypt' === $this->filtername && $this->mode instanceof AsymmetricModeInterface) {
88  $this->method = 'decrypt';
89  } else {
90  $this->method = 'encrypt';
91  }
92  return true;
93  }
94 
95  public function filter($in, $out, &$consumed, $closing)
96  {
97  $res = PSFS_FEED_ME;
98  $method = $this->method;
99 # $options = stream_context_get_options($this->stream);
100 
101  while (true) {
102  $bucket = stream_bucket_make_writeable($in);
103  if ($bucket) {
104  $this->buffer .= $bucket->data;
105  } elseif ('cryptal.encrypt' === $this->filtername && $closing) {
106  // Add the padding scheme
107  $missing = $this->blockSize - (strlen($this->buffer) % $this->blockSize);
108  $this->buffer .= $this->padding->getPaddingData($this->blockSize, $missing);
109  }
110 
111  $available = strlen($this->buffer);
112  $nbBlocks = ($available - ($available % $this->blockSize)) / $this->blockSize;
113 
114  if ($nbBlocks > 0) {
115  $consume = $nbBlocks * $this->blockSize;
116  $outBuffer = '';
117  for ($i = 0; $i < $nbBlocks; $i++) {
118  $outBuffer .= $this->mode->$method(
119  substr($this->buffer, $this->blockSize * $i, $this->blockSize),
120  $this->stream
121  );
122  }
123 
124  if (!$bucket && 'cryptal.decrypt' === $this->filtername && $closing) {
125  // Remove the padding scheme
126  $padLen = $this->padding->getPaddingSize($outBuffer, $this->blockSize);
127  if ($padLen) {
128  $outBuffer = (string) substr($outBuffer, 0, -$padLen);
129  }
130  }
131 
132  stream_bucket_append($out, stream_bucket_new($this->stream, $outBuffer));
133  $this->buffer = (string) substr($this->buffer, $consume);
134  $consumed += $consume;
135  $res = PSFS_PASS_ON;
136  }
137 
138  if (!$bucket) {
139  return $res;
140  }
141  }
142  }
143 }