Completed
Pull Request — master (#46)
by Antonio Oertel
04:53
created

Asn::parseExtensions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 3
crap 1
1
<?php
2
3
namespace NFePHP\Common\Certificate;
4
5
/**
6
 * Classe auxiliar para obter informações dos certificados digitais A1 (PKCS12)
7
 * @category   NFePHP
8
 * @package    NFePHP\Common\Certificate
9
 * @copyright  Copyright (c) 2008-2014
10
 * @license    http://www.gnu.org/licenses/lesser.html LGPL v3
11
 * @author     Roberto L. Machado <linux.rlm at gmail dot com>
12
 * @link       http://github.com/nfephp-org/nfephp for the canonical source repository
13
 */
14
15
use NFePHP\Common\Certificate\Oids;
16
17
class Asn extends Base
18
{
19
    /**
20
     * Comprimento do campo sendo usado
21
     *
22
     * @var integer
23
     */
24
    protected static $len = 0;
25
    
26
    /**
27
     * getCNPJCert
28
     *
29
     * Obtêm o numero de CNPJ da chave publica do Certificado (A1)
30
     *
31
     * @param string $certpem conteúdo do certificado
0 ignored issues
show
Documentation introduced by
There is no parameter named $certpem. Did you maybe mean $certPem?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
32
     * @return string CNPJ
33
     */
34 4
    public static function getCNPJCert($certPem)
35
    {
36 4
        $certDer = self::pem2Der((string) $certPem);
37 4
        $data = self::getOIDdata((string) $certDer, '2.16.76.1.3.3');
38 4
        return (string) $data[0][1][1][0][1];
39
    }
40
    
41
    /**
42
     * getOIDdata
43
     * Recupera a informação referente ao OID contido no certificado
44
     * Este método assume que a OID está inserida dentro de uma estrutura do
45
     * tipo "sequencia", como primeiro elemento da estrutura
46
     * @param string $certDer
47
     * @param string $oidNumber
48
     * @return array
49
     */
50 4
    protected static function getOIDdata($certDer, $oidNumber)
51
    {
52
        //converte onumero OTD de texto para hexadecimal
53 4
        $oidHexa = self::oidtoHex((string) $oidNumber);
54
        //Divide o certificado usando a OID como marcador,uma antes do OID e outra contendo o OID.
55
        //Normalmente o certificado será dividido em duas partes, pois em geral existe
56
        //apenas um OID de cada tipo no certificado, mas podem haver mais.
57 4
        $partes = explode($oidHexa, $certDer);
58 4
        $ret = array();
59
        //se count($partes) > 1 então o OID foi localizado no certificado
60 4
        $tot = count($partes);
61 4
        if ($tot > 1) {
62
            //O inicio da sequencia que nos interessa pode estar a 3 ou 2 digitos
63
            //antes do inicio da OID, isso depende do numero de bytes usados para
64
            //identificar o tamanho da sequencia
65 4
            for ($i = 1; $i < $tot; $i++) {
66
                //recupera da primeira parte os 4 últimos digitos na parte sem o OID
67 4
                $xcv4 = substr($partes[$i-1], strlen($partes[$i-1])-4, 4);
68
                //recupera da primeira parte os 3 ultimos digitos na parte sem o OID
69 4
                $xcv3 = substr($partes[$i-1], strlen($partes[$i-1])-3, 3);
70
                //recupera da primeira parte os 2 ultimos digitos na parte em o OID
71 4
                $xcv2 = substr($partes[$i-1], strlen($partes[$i-1])-2, 2);
72
                //verifica se o primeiro digito é Hex 030
73 4
                if ($xcv4[0] == chr(0x30)) {
74
                    //se for, então tamanho é definido por esses 4 bytes
75
                    $xcv = $xcv4;
76
                } else {
77
                    //se for, então tamanho é definido por esses 3 bytes
78 4
                    if ($xcv3[0] == chr(0x30)) {
79
                        $xcv = $xcv3;
80
                    } else {
81
                        //então tamanho é definido por esses 2 bytes
82 4
                        $xcv = $xcv2;
83
                    }
84
                }
85
                //reconstroi a sequencia, marca do tamanho do campo, OID e
86
                //a parte do certificado com o OID
87 4
                $data = $xcv . $oidHexa . $partes[$i];
88
                //converte para decimal, o segundo digito da sequencia
89 4
                $len = (integer) ord($data[1]);
90 4
                $bytes = 0;
91
                // obtem tamanho da parte de dados da oid
92 4
                self::getLength($len, $bytes, (string) $data);
93
                // Obtem o conjunto de bytes pertencentes a oid
94 4
                $oidData = substr($data, 2 + $bytes, $len);
95
                //parse dos dados da oid
96 4
                $ret[] =  self::parseASN((string) $oidData);
97
            }
98
        }
99 4
        return $ret;
100
    }
101
102
    /**
103
     * parseASN
104
     * Retorna a informação requerida do certificado
105
     * @param string $data bloco de dados do certificado a ser traduzido
106
     * @param boolean $contextEspecific
107
     * @return array com o dado do certificado já traduzido
108
     */
109 4
    protected static function parseASN($data, $contextEspecific = false)
110
    {
111 4
        $result = array();
112 4
        while (strlen($data) > 1) {
113 4
            $class = ord($data[0]);
114
            switch ($class) {
115 4
                case 0x30:
116
                    // Sequence
117
                    self::parseSequence($data, $result);
118
                    break;
119 4
                case 0x31:
120
                    self::parseSetOf($data, $result);
121
                    break;
122 4
                case 0x01:
123
                    // Boolean type
124
                    self::parseBooleanType($data, $result);
125
                    break;
126 4
                case 0x02:
127
                    // Integer type
128
                    self::parseIntegerType($data, $result);
129
                    break;
130 4
                case 0x03:
131
                    self::parseBitString($data, $result);
132
                    break;
133 4
                case 0x04:
134 4
                    self::parseOctetSting($data, $result, $contextEspecific);
135 4
                    break;
136 4
                case 0x0C:
137
                    self::parseUtf8String($data, $result, $contextEspecific);
138
                    break;
139 4
                case 0x05:
140
                    // Null type
141
                    $data = substr($data, 2);
142
                    $result[] = array('null', null);
143
                    break;
144 4
                case 0x06:
145 4
                    self::parseOIDtype($data, $result);
146 4
                    break;
147 4
                case 0x16:
148
                    self::parseIA5String($data, $result);
149
                    break;
150 4
                case 0x12:
151 4
                case 0x14:
152 4
                case 0x15:
153 4
                case 0x81:
154
                    self::parseString($data, $result);
155
                    break;
156 4
                case 0x80:
157
                    // Character string type
158
                    self::parseCharString($data, $result);
159
                    break;
160 4
                case 0x13:
161 4
                case 0x86:
162
                    // Printable string type
163
                    self::parsePrintableString($data, $result);
164
                    break;
165 4
                case 0x17:
166
                    // Time types
167
                    self::parseTimesType($data, $result);
168
                    break;
169 4
                case 0x82:
170
                    // X509v3 extensions?
171
                    self::parseExtensions($data, $result, 'extension : X509v3 extensions');
172
                    break;
173 4
                case 0xa0:
174
                    // Extensions Context Especific
175 4
                    self::parseExtensions($data, $result, 'Context Especific');
176 4
                    break;
177
                case 0xa3:
178
                    // Extensions
179
                    self::parseExtensions($data, $result, 'extension (0xA3)');
180
                    break;
181
                case 0xe6:
182
                    // Hex Extensions extension (0xE6)
183
                    self::parseHexExtensions($data, $result, 'extension (0xE6)');
184
                    break;
185
                case 0xa1:
186
                    // Hex Extensions extension (0xA1)
187
                    self::parseHexExtensions($data, $result, 'extension (0xA1)');
188
                    break;
189
                default:
190
                    // Unknown
191
                    $result[] = 'UNKNOWN' .  $data;
192
                    $data = '';
193
                    break;
194
            }
195
        }
196 4
        if (count($result) > 1) {
197 4
            return $result;
198
        } else {
199 4
            return array_pop($result);
200
        }
201
    }
202
    
203
    /**
204
     * parseCommon
205
     * @param string $data
206
     * @param string $result
207
     * @return string
208
     */
209 4
    protected static function parseCommon($data, &$result)
210
    {
211 4
        self::$len = (integer) ord($data[1]);
212 4
        $bytes = 0;
213 4
        self::getLength(self::$len, $bytes, (string) $data);
214 4
        $result = substr($data, 2 + $bytes, self::$len);
215 4
        return substr($data, 2 + $bytes + self::$len);
216
    }
217
218
    /**
219
     * parseBooleanType
220
     * @param string $data
221
     * @param array $result
222
     * @return void
223
     */
224
    protected static function parseBooleanType(&$data, &$result)
225
    {
226
        // Boolean type
227
        $booleanValue = (boolean) (ord($data[2]) == 0xff);
228
        $dataI = substr($data, 3);
229
        $result[] = array(
230
            'boolean (1)',
231
            $booleanValue);
232
        $data = $dataI;
233
    }
234
235
    /**
236
     * parseIntegerType
237
     * @param string $data
238
     * @param array $result
239
     * @return void
240
     */
241
    protected static function parseIntegerType(&$data, &$result)
242
    {
243
        $dataI = self::parseCommon($data, $integerData);
244
        if (self::$len == 16) {
245
            $result[] = array(
246
                'integer('.self::$len.')',
247
                $integerData);
248
        } else {
249
            $value = 0;
250
            if (self::$len <= 4) {
251
                // metodo funciona bem para inteiros pequenos
252
                for ($i = 0; $i < strlen($integerData); $i++) {
253
                    $value = ($value << 8) | ord($integerData[$i]);
254
                }
255
            } else {
256
                // metodo trabalha com inteiros arbritrários
257
                if (extension_loaded('bcmath')) {
258
                    for ($i = 0; $i < strlen($integerData); $i++) {
259
                        $value = bcadd(bcmul($value, 256), ord($integerData[$i]));
260
                    }
261
                } else {
262
                    $value = -1;
263
                }
264
            }
265
            $result[] = array('integer(' . self::$len . ')', $value);
266
        }
267
        $data = $dataI;
268
    }
269
     
270
    /**
271
     * parseHexExtensions
272
     * @param string $data
273
     * @param array $result
274
     * @param string $text
275
     * @return void
276
     */
277
    protected static function parseHexExtensions(&$data, &$result, $text)
278
    {
279
        $extensionData = substr($data, 0, 1);
280
        $dataI = substr($data, 1);
281
        $result[] = array(
282
            $text .' (' . self::$len . ')',
283
            dechex($extensionData));
284
        $data = $dataI;
285
    }
286
287
    /**
288
     * parseTimesType
289
     * @param string $data
290
     * @param array $result
291
     * @return void
292
     */
293
    protected static function parseTimesType(&$data, &$result)
294
    {
295
        // Time types
296
        $dataI = self::parseCommon($data, $timeData);
297
        $result[] = array(
298
            'utctime (' . self::$len . ')',
299
             $timeData);
300
        $data = $dataI;
301
    }
302
    
303
    /**
304
     * parsePrintableString
305
     * @param string $data
306
     * @param array $result
307
     * @return void
308
     */
309
    protected static function parsePrintableString(&$data, &$result)
310
    {
311
        // Printable string type
312
        $data = self::parseCommon($data, $stringData);
313
        $result[] = array(
314
            'Printable String (' . self::$len . ')',
315
            $stringData);
316
    }
317
    
318
    /**
319
     * parseCharString
320
     * @param string $data
321
     * @param array $result
322
     * @return void
323
     */
324
    protected static function parseCharString(&$data, &$result)
325
    {
326
        // Character string type
327
        $data = self::parseCommon($data, $stringData);
328
        $result[] = array(
329
            'string (' . self::$len . ')',
330
            self::printHex((string) $stringData));
331
    }
332
    
333
    /**
334
     * parseExtensions
335
     * @param string $data
336
     * @param array $result
337
     * @param string $text
338
     * @return void
339
     */
340 4
    protected static function parseExtensions(&$data, &$result, $text)
341
    {
342
        // Extensions
343 4
        $data = self::parseCommon($data, $extensionData);
344 4
        $result[] = array(
345 4
            "$text (" . self::$len . ")",
346 4
            array(self::parseASN((string) $extensionData, true)));
347 4
    }
348
    
349
    /**
350
     * parseSequence
351
     * @param string $data
352
     * @param array $result
353
     * @return void
354
     */
355
    protected static function parseSequence(&$data, &$result)
356
    {
357
        // Sequence
358
        $data = self::parseCommon($data, $sequenceData);
359
        $values = self::parseASN((string) $sequenceData);
360
        if (!is_array($values) || is_string($values[0])) {
361
            $values = array($values);
362
        }
363
        $result[] = array(
364
            'sequence ('.self::$len.')',
365
            $values);
366
    }
367
    
368
    /**
369
     * parseOIDtype
370
     * @param string $data
371
     * @param array $result
372
     * @return void
373
     */
374 4
    protected static function parseOIDtype(&$data, &$result)
375
    {
376
        // Object identifier type
377 4
        $data = self::parseCommon($data, $oidData);
378
        // Unpack the OID
379 4
        $plain  = floor(ord($oidData[0]) / 40);
380 4
        $plain .= '.' . ord($oidData[0]) % 40;
381 4
        $value = 0;
382 4
        $iCount = 1;
383 4
        while ($iCount < strlen($oidData)) {
384 4
            $value = $value << 7;
385 4
            $value = $value | (ord($oidData[$iCount]) & 0x7f);
386 4
            if (!(ord($oidData[$iCount]) & 0x80)) {
387 4
                $plain .= '.' . $value;
388 4
                $value = 0;
389
            }
390 4
            $iCount++;
391
        }
392 4
        $oidResp = Oids::getOid($plain);
0 ignored issues
show
Documentation introduced by
$plain is of type string, but the function expects a object<NFePHP\Common\Certificate\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
393 4
        if ($oidResp) {
394
            $result[] =  array(
395
                'oid('.self::$len . '): '.$plain,
396
                $oidResp);
397
        } else {
398 4
            $result[] = array(
399 4
                'oid('.self::$len.'): '.$plain,
400 4
                $plain);
401
        }
402 4
    }
403
    
404
    /**
405
     * parseSetOf
406
     * @param string $data
407
     * @param array $result
408
     * @return void
409
     */
410
    protected static function parseSetOf(&$data, &$result)
411
    {
412
        $data = self::parseCommon($data, $sequenceData);
413
        $result[] = array(
414
            'set (' . self::$len . ')',
415
            self::parseASN((string) $sequenceData));
416
    }
417
    
418
    /**
419
     * parseOctetSting
420
     * @param string $data
421
     * @param array $result
422
     * @param boolean $contextEspecific
423
     * @return void
424
     */
425 4
    protected static function parseOctetSting(&$data, &$result, $contextEspecific)
426
    {
427
        // Octetstring type
428 4
        $data = self::parseCommon($data, $octectstringData);
429 4
        if ($contextEspecific) {
430 4
            $result[] = array(
431 4
                'octet string('.self::$len.')',
432 4
                $octectstringData);
433
        } else {
434
            $result[] = array(
435
                'octet string ('.self::$len.')',
436
                self::parseASN((string) $octectstringData));
437
        }
438 4
    }
439
    
440
    /**
441
     * parseUtf8String
442
     * @param string $data
443
     * @param array $result
444
     * @param boolean $contextEspecific
445
     * @return void
446
     */
447
    protected static function parseUtf8String(&$data, &$result, $contextEspecific)
448
    {
449
        // UTF8 STRING
450
        $data = self::parseCommon($data, $octectstringData);
451
        if ($contextEspecific) {
452
            $result[] = array(
453
                'utf8 string('.self::$len.')',
454
                $octectstringData);
455
        } else {
456
            $result[] = array(
457
                'utf8 string ('.self::$len.')',
458
                self::parseASN((string) $octectstringData));
459
        }
460
    }
461
462
    /**
463
     * parseIA5String
464
     * @param string $data
465
     * @param array $result
466
     * @return void
467
     */
468
    protected static function parseIA5String(&$data, &$result)
469
    {
470
        // Character string type
471
        $data = self::parseCommon($data, $stringData);
472
        $result[] = array(
473
            'IA5 String (' . self::$len . ')',
474
            $stringData);
475
    }
476
    
477
    /**
478
     * parseString
479
     * @param string $data
480
     * @param array $result
481
     * @return void
482
     */
483
    protected static function parseString(&$data, &$result)
484
    {
485
        // Character string type
486
        $data = self::parseCommon($data, $stringData);
487
        $result[] = array(
488
            'string (' . self::$len . ')',
489
            $stringData);
490
    }
491
    
492
    /**
493
     * parseBitString
494
     * @param string $data
495
     * @param array $result
496
     * @return void
497
     */
498
    protected static function parseBitString(&$data, &$result)
499
    {
500
        // Bitstring type
501
        $data = self::parseCommon($data, $bitstringData);
502
        $result[] = array(
503
            'bit string ('.self::$len.')',
504
            'UnsedBits:'.ord($bitstringData[0]).':'.ord($bitstringData[1]));
505
    }
506
}
507