Passed
Push — master ( 42b683...7c1a7f )
by
unknown
16:41 queued 02:17
created

Der::peek()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 6
nop 1
dl 0
loc 16
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace WAYF;
4
5
require_once 'Oids.php';
6
7
class Der extends Oids {
8
	protected $tag;
9
	protected $len;
10
	protected $value;
11
	protected $class;
12
	protected $constructed;
13
	protected $buffer;
14
	protected $stack = [];
15
	protected $i;
16
	private $ignoredextensions = [
0 ignored issues
show
introduced by
The private property $ignoredextensions is not used, and could be removed.
Loading history...
17
		'netscape-cert-type' => 1,
18
	];
19
	private $id;
20
21
	protected function init($der) {
22
		$this->buffer = $der;
23
		$this->i = 0;
24
		$this->id = uniqid();
25
	}
26
27
	protected function dump($note = '') {
28
		$z = strlen((string) $this->buffer) - $this->i;
29
		print_r("{$note}\n");
30
		print_r("len: {$z}\n");
31
		print_r(chunk_split(bin2hex(substr((string) $this->buffer, $this->i)), 2, ':'));
32
		echo "\n";
33
	}
34
35
	protected function pr($note = '') {
36
		$savei = $this->i;
37
		$byte = ord($this->buffer[$this->i++]);
38
		$tag = $byte & 0x1F;
39
		$class = $byte & 0xC0;
40
		$constructed = $byte & 0x20;
0 ignored issues
show
Unused Code introduced by
The assignment to $constructed is dead and can be removed.
Loading history...
41
		$len = $this->vallen();
42
		$this->i = $savei;
43
		print_r("{$note}\n");
44
		print_r("i  : {$this->i}\n");
45
		print_r("len: {$len}\n");
46
		print_r("class:   {$class}\n");
47
		print_r("tag  :   {$tag}\n");
48
		print_r(chunk_split(bin2hex(substr((string) $this->buffer, $this->i, min(32, strlen((string) $this->buffer) - $this->i))) . "\n", 2, ':'));
49
		print_r("---\n");
50
	}
51
52
	private function tlv($expectedtag = null) {
53
		$byte = ord($this->buffer[$this->i++]);
54
		$this->tag = $byte & 0x1F;
55
		if ($expectedtag < 0) {
56
			$this->tag = $expectedtag = -$expectedtag;
57
		}
58
		if ($expectedtag && $expectedtag != $this->tag) {
59
			trigger_error("expected tag == {$expectedtag}, got {$this->tag} {$this->id}\n", E_USER_ERROR);
60
		}
61
		$this->class = $byte & 0xC0;
62
		$this->constructed = $byte & 0x20;
63
		$this->len = $this->vallen();
64
	}
65
66
	protected function next($expectedtag = null) {
67
		$this->tlv($expectedtag);
68
		if ($this->constructed) {
69
			return;
70
		}
71
		$value = substr((string) $this->buffer, $this->i, $this->len);
72
		if ($this->class == 0 || $this->class == 0x80) {
73
			if ($this->tag == 2 || $this->tag == 10) { # ints and enums
74
				$int = 0;
75
				foreach (str_split($value) as $byte) {
76
					$int = bcmul($int, '256', 0);
77
					$int = bcadd($int, ord($byte), 0);
78
				}
79
				$this->value = $int;
80
			}
81
			elseif ($this->tag == 1) { # boolean
82
				$this->value = ord($value) != 0;
83
			}
84
			elseif ($this->tag == 3) { # bit string
85
				$this->value = $value;
86
			}
87
			elseif ($this->tag == 5) { # null
88
				$this->value = null;
89
			}
90
			else {
91
				$this->value = $value;
92
			}
93
		}
94
		$this->i += $this->len;
95
96
		return $this->value;
97
	}
98
99
	protected function der($expectedtag = null, $pass = false) {
100
		$oldi = $this->i;
101
		$this->tlv($expectedtag);
102
		$i = $this->i;
103
		if (!$pass) {
104
			$this->i = $oldi;
105
		}
106
		else {
107
			$this->i += $this->len;
108
		}
109
110
		return substr((string) $this->buffer, $oldi, $this->len + $i - $oldi);
111
	}
112
113
	/*
114
	 * if provided with a tag and the tag is equal to the current tag
115
	 * peek considers it EXPLICIT, consumes it and return true
116
	 */
117
	protected function peek($tag = null) {
118
		$t = null;
119
		if ($this->i < end($this->stack)) {
120
			$t = ord($this->buffer[$this->i]) & 0x1F;
121
		}
122
		if ($tag !== null) {
123
			if ($t === $tag) {
124
				$this->next($tag);
125
126
				return true;
127
			}
128
129
			return false;
130
		}
131
132
		return $t;
133
	}
134
135
	protected function vallen() {
136
		$byte = ord($this->buffer[$this->i++]);
137
		$res = $len = $byte & 0x7F;
138
		if ($byte >= 0x80) {
139
			$res = 0;
140
			for ($c = 0; $c < $len; ++$c) {
141
				$res = $res * 256 + ord($this->buffer[$this->i++]);
142
			}
143
		}
144
145
		return $res;
146
	}
147
148
	protected function beginsequence($tag = 16) {
149
		$this->begin($tag);
150
	}
151
152
	protected function beginset($tag = 17) {
153
		$this->begin($tag);
154
	}
155
156
	protected function begin($tag) {
157
		$this->next($tag);
158
		array_push($this->stack, $this->i + $this->len);
159
	}
160
161
	protected function in() {
162
		return $this->i < end($this->stack);
163
	}
164
165
	protected function end() {
166
		$end = array_pop($this->stack);
167
		if ($end != $this->i) {
168
			trigger_error("sequence or set length does not match: {$end} != {$this->i}", E_USER_ERROR);
169
		}
170
	}
171
172
	protected function extensions() {
173
		$this->beginsequence();
174
		$extns = [];
175
		while ($this->in()) {
176
			$this->beginsequence();
177
			$extnID = $this->oid();
178
			$theext['critical'] = $this->peek(1);
179
			$theext['extnValue'] = $this->next(4);
180
181
			try {
182
				if (method_exists($this, $extnID)) {
183
					$theext['extnValue'] = call_user_func([$this, $extnID], $theext['extnValue']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $theext does not seem to be defined for all execution paths leading up to this point.
Loading history...
184
				}
185
				elseif (!empty($ignoredextensions['$extnID'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ignoredextensions seems to never exist and therefore empty should always be true.
Loading history...
186
					trigger_error("Unknown extension {$extnID}", E_USER_ERROR);
187
				}
188
				else {
189
					$theext['extnValue'] = chunk_split(bin2hex((string) $theext['extnValue']), 2, ':');
190
				}
191
			}
192
			catch (\Exception) {
193
				$theext['extnValue'] = chunk_split(bin2hex((string) $theext['extnValue']), 2, ':');
194
			}
195
			$this->end();
196
			$extns[$extnID] = $theext;
197
		}
198
		$this->end();
199
200
		return $extns;
201
	}
202
203
	protected function signatureAlgorithm() {
204
		$this->beginsequence();
205
		$salg = $this->oid();
206
		if ($this->in()) {
207
			$this->next(); # alg param - ignore for now
208
		}
209
		$this->end();
210
211
		return $salg;
212
	}
213
214
	protected function name($tag = null) {
215
		$this->beginsequence($tag);
216
		$res = [];
217
		while ($this->in()) {
218
			$parts = [];
219
			$this->beginset(); # set of AttributeTypeAndValue
220
			while ($this->in()) {
221
				$this->beginsequence();
222
				$parts[$this->oid()] = $this->next(); # AttributeValue
223
				$this->end();
224
			}
225
			$this->end();
226
			$res[] = $parts;
227
		}
228
		$this->end();
229
230
		return $res;
231
	}
232
233
	protected function oid($tag = 6) {
234
		$v = $this->oid_($this->next($tag));
235
236
		return $this->oids[$v] ?? $v;
237
	}
238
239
	protected function oid_($oid) {
240
		$len = strlen((string) $oid);
241
		$v = "";
242
		$n = 0;
243
		for ($c = 0; $c < $len; ++$c) {
244
			$x = ord($oid[$c]);
245
			$n = $n * 128 + ($x & 0x7F);
246
			if ($x <= 127) {
247
				$v .= $v ? '.' . $n : ((int) ($n / 40) . '.' . ($n % 40));
248
				$n = 0;
249
			}
250
		}
251
252
		return $v . '*';
253
	}
254
255
	protected function time($tag = null) {
256
		$time = $this->next($tag);
257
		if ($this->tag == 23) {
258
			$time = (substr((string) $time, 0, 2) < 50 ? '20' : '19') . $time;
259
		}
260
		elseif ($this->tag != 24) {
261
			trigger_error('expected der utc or generalized time', E_USER_ERROR);
262
		}
263
264
		return $time;
265
	}
266
267
	protected function keyident($tag = 4) {
268
		return chunk_split(bin2hex((string) $this->next($tag)), 2, ':');
269
	}
270
}
271