Rev 38 | Rev 41 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 38 | Rev 40 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * ISO/IEC 7816-5 Application Identifier decoder for PHP |
4 | * ISO/IEC 7816-5 Application Identifier decoder for PHP |
5 | * Copyright 2022 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2022 Daniel Marschall, ViaThinkSoft |
6 | * Version 2022-08-19 |
6 | * Version 2022-09-07 |
7 | * |
7 | * |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | * you may not use this file except in compliance with the License. |
9 | * you may not use this file except in compliance with the License. |
10 | * You may obtain a copy of the License at |
10 | * You may obtain a copy of the License at |
11 | * |
11 | * |
Line 16... | Line 16... | ||
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | * See the License for the specific language governing permissions and |
17 | * See the License for the specific language governing permissions and |
18 | * limitations under the License. |
18 | * limitations under the License. |
19 | */ |
19 | */ |
20 | 20 | ||
- | 21 | include_once __DIR__ . '/misc_functions.inc.php'; |
|
- | 22 | ||
- | 23 | # --- |
|
- | 24 | ||
21 | /* |
25 | /* |
22 | #test2('A000000051AABBCC'); |
26 | #test2('A000000051AABBCC'); |
23 | #test2('B01234567890'); |
27 | #test2('B01234567890'); |
24 | #test2('D276000098AABBCCDDEEFFAABBCCDDEE'); |
28 | #test2('D276000098AABBCCDDEEFFAABBCCDDEE'); |
25 | #test2('F01234567890'); |
29 | #test2('F01234567890'); |
26 | test('91234FFF999'); |
30 | #test('91234FFF999'); |
27 | test('51234FFF999'); |
31 | #test('51234FFF999'); |
- | 32 | #test2('E828BD080F014E585031'); |
|
28 | 33 | ||
29 | function test2($aid) { |
34 | function test2($aid) { |
30 | while ($aid != '') { |
35 | while ($aid != '') { |
31 | echo test($aid); |
36 | echo test($aid); |
32 | $aid = substr($aid,0,strlen($aid)-1); |
37 | $aid = substr($aid,0,strlen($aid)-1); |
Line 52... | Line 57... | ||
52 | } |
57 | } |
53 | */ |
58 | */ |
54 | 59 | ||
55 | # --- |
60 | # --- |
56 | 61 | ||
- | 62 | function _aid_e8_interpretations($aid) { |
|
- | 63 | $ret = array(); |
|
- | 64 | ||
- | 65 | $output_oid = array(); |
|
- | 66 | ||
- | 67 | $aid = strtoupper(str_replace(' ','',$aid)); |
|
- | 68 | $aid = strtoupper(str_replace(':','',$aid)); |
|
- | 69 | $by = str_split($aid,2); |
|
- | 70 | if ((array_shift($by) == 'E8') ) { |
|
- | 71 | // The following part is partially taken from the DER decoder/encoder by Daniel Marschall: |
|
- | 72 | // https://github.com/danielmarschall/oidconverter/blob/master/php/OidDerConverter.class.phps |
|
- | 73 | // (Only the DER "value" part, without "type" and "length") |
|
- | 74 | ||
- | 75 | $part = 2; // DER part 0 (type) and part 1 (length) not present |
|
- | 76 | $fSub = 0; // Subtract value from next number output. Used when encoding {2 48} and up |
|
- | 77 | $ll = gmp_init(0); |
|
- | 78 | $arcBeginning = true; |
|
- | 79 | ||
- | 80 | foreach ($by as $ii => $pb) { |
|
- | 81 | ||
- | 82 | $pb = hexdec($pb); |
|
- | 83 | ||
- | 84 | if ($part == 2) { // First two arcs |
|
- | 85 | $first = $pb / 40; |
|
- | 86 | $second = $pb % 40; |
|
- | 87 | if ($first > 2) { |
|
- | 88 | $first = 2; |
|
- | 89 | $output_oid[] = $first; |
|
- | 90 | $arcBeginning = true; |
|
- | 91 | ||
- | 92 | if (($pb & 0x80) != 0) { |
|
- | 93 | // 2.48 and up => 2+ octets |
|
- | 94 | // Output in "part 3" |
|
- | 95 | ||
- | 96 | if ($pb == 0x80) { |
|
- | 97 | throw new Exception("Encoding error. Illegal 0x80 paddings. (See Rec. ITU-T X.690, clause 8.19.2)\n"); |
|
- | 98 | } else { |
|
- | 99 | $arcBeginning = false; |
|
- | 100 | } |
|
- | 101 | ||
- | 102 | $ll = gmp_add($ll, ($pb & 0x7F)); |
|
- | 103 | $fSub = 80; |
|
- | 104 | $fOK = false; |
|
- | 105 | } else { |
|
- | 106 | // 2.0 till 2.47 => 1 octet |
|
- | 107 | $second = $pb - 80; |
|
- | 108 | $output_oid[] = $second; |
|
- | 109 | $arcBeginning = true; |
|
- | 110 | $fOK = true; |
|
- | 111 | $ll = gmp_init(0); |
|
- | 112 | } |
|
- | 113 | } else { |
|
- | 114 | // 0.0 till 0.37 => 1 octet |
|
- | 115 | // 1.0 till 1.37 => 1 octet |
|
- | 116 | $output_oid[] = $first; |
|
- | 117 | $output_oid[] = $second; |
|
- | 118 | $arcBeginning = true; |
|
- | 119 | $fOK = true; |
|
- | 120 | $ll = gmp_init(0); |
|
- | 121 | } |
|
- | 122 | $part++; |
|
- | 123 | } else { //else if ($part == 3) { // Arc three and higher |
|
- | 124 | if (($pb & 0x80) != 0) { |
|
- | 125 | if ($arcBeginning && ($pb == 0x80)) { |
|
- | 126 | throw new Exception("Encoding error. Illegal 0x80 paddings. (See Rec. ITU-T X.690, clause 8.19.2)"); |
|
- | 127 | } else { |
|
- | 128 | $arcBeginning = false; |
|
- | 129 | } |
|
- | 130 | ||
- | 131 | $ll = gmp_mul($ll, 0x80); |
|
- | 132 | $ll = gmp_add($ll, ($pb & 0x7F)); |
|
- | 133 | $fOK = false; |
|
- | 134 | } else { |
|
- | 135 | $fOK = true; |
|
- | 136 | $ll = gmp_mul($ll, 0x80); |
|
- | 137 | $ll = gmp_add($ll, $pb); |
|
- | 138 | $ll = gmp_sub($ll, $fSub); |
|
- | 139 | $output_oid[] = gmp_strval($ll, 10); |
|
- | 140 | ||
- | 141 | $is_iso_standard = ($output_oid[0] == '1') && ($output_oid[1] == '0'); |
|
- | 142 | ||
- | 143 | $byy = $by; |
|
- | 144 | for ($i=0;$i<=$ii;$i++) array_shift($byy); |
|
- | 145 | ||
- | 146 | $s_oid = implode('.',$output_oid); |
|
- | 147 | ||
- | 148 | if ($is_iso_standard) { |
|
- | 149 | $std_hf = 'Standard ISO/IEC '.$output_oid[2]; |
|
- | 150 | if (isset($output_oid[3])) $std_hf .= "-".$output_oid[3]; |
|
- | 151 | } else { |
|
- | 152 | $std_hf = "Unknown Standard"; // should not happen |
|
- | 153 | } |
|
- | 154 | ||
- | 155 | $pix = implode(':',$byy); |
|
- | 156 | ||
- | 157 | if ($pix !== '') { |
|
- | 158 | $ret[] = array($ii+1,"$std_hf (OID $s_oid)",$pix); |
|
- | 159 | } else { |
|
- | 160 | $ret[] = array($ii+1,"$std_hf (OID $s_oid)",""); |
|
- | 161 | } |
|
- | 162 | ||
- | 163 | // ISO Standards (OID 1.0) will only have 1 or 2 numbers. (Number 1 is the standard, and number 2 |
|
- | 164 | // is the part in case of a multi-part standard). |
|
- | 165 | if ($is_iso_standard && (count($output_oid) == 4)) break; |
|
- | 166 | ||
- | 167 | // Happens only if 0x80 paddings are allowed |
|
- | 168 | // $fOK = gmp_cmp($ll, 0) >= 0; |
|
- | 169 | $ll = gmp_init(0); |
|
- | 170 | $fSub = 0; |
|
- | 171 | $arcBeginning = true; |
|
- | 172 | } |
|
- | 173 | } |
|
- | 174 | } |
|
- | 175 | } |
|
- | 176 | return $ret; |
|
- | 177 | } |
|
- | 178 | ||
57 | function decode_aid($aid,$compact=true) { |
179 | function decode_aid($aid,$compact=true) { |
58 | $sout = ''; |
180 | $sout = ''; |
59 | $out = _decode_aid($aid); |
181 | $out = _decode_aid($aid); |
60 | if ($compact) { |
182 | if ($compact) { |
61 | $max_key_len = 0; |
183 | $max_key_len = 0; |
Line 351... | Line 473... | ||
351 | // https://blog.actorsfit.com/a?ID=00250-166ef507-edff-4400-8d0e-9e85d6ae2310 |
473 | // https://blog.actorsfit.com/a?ID=00250-166ef507-edff-4400-8d0e-9e85d6ae2310 |
352 | 474 | ||
353 | $aid = strtoupper($aid); |
475 | $aid = strtoupper($aid); |
354 | $aid = trim($aid); |
476 | $aid = trim($aid); |
355 | $aid = str_replace(' ','',$aid); |
477 | $aid = str_replace(' ','',$aid); |
- | 478 | $aid = str_replace(':','',$aid); |
|
356 | 479 | ||
357 | if ($aid == '') { |
480 | if ($aid == '') { |
358 | $out[] = "INVALID: The AID is empty"; |
481 | $out[] = "INVALID: The AID is empty"; |
359 | return $out; |
482 | return $out; |
360 | } |
483 | } |
Line 362... | Line 485... | ||
362 | if (!preg_match('@^[0-9A-F]+$@', $aid, $m)) { |
485 | if (!preg_match('@^[0-9A-F]+$@', $aid, $m)) { |
363 | $out[] = "INVALID: AID has invalid characters. Only A..F and 0..9 are allowed"; |
486 | $out[] = "INVALID: AID has invalid characters. Only A..F and 0..9 are allowed"; |
364 | return $out; |
487 | return $out; |
365 | } |
488 | } |
366 | 489 | ||
367 | $aid_hf = ''; |
- | |
368 | for ($i=0; $i<strlen($aid); $i++) { |
- | |
369 | $aid_hf .= $aid[$i]; |
490 | $aid_hf = implode(':',str_split($aid,2)); |
370 | if ($i%2 == 1) $aid_hf .= ' '; |
- | |
371 | } |
- | |
372 | if (strlen($aid)%2 == 1) $aid_hf .= '_'; |
491 | if (strlen($aid)%2 == 1) $aid_hf .= '_'; |
373 | $aid_hf = rtrim($aid_hf); |
- | |
- | 492 | ||
374 | $out[] = array("$aid", "ISO/IEC 7816-5 Application Identifier (AID)"); |
493 | $out[] = array("$aid", "ISO/IEC 7816-5 Application Identifier (AID)"); |
375 | $out[] = array('', "> $aid_hf <"); |
494 | $out[] = array('', "> $aid_hf <"); |
376 | $out[] = array('', c_literal_hexstr($aid)); |
495 | $out[] = array('', c_literal_hexstr($aid)); |
377 | 496 | ||
378 | if ((strlen($aid) == 32) && (substr($aid,-2) == 'FF')) { |
497 | if ((strlen($aid) == 32) && (substr($aid,-2) == 'FF')) { |
Line 561... | Line 680... | ||
561 | } |
680 | } |
562 | 681 | ||
563 | return $out; |
682 | return $out; |
564 | } |
683 | } |
565 | 684 | ||
- | 685 | // Category 'E' |
|
- | 686 | // AID = 'E8' + OID + PIX (OID is DER encoding without type and length) |
|
- | 687 | if ("$category" === "E") { |
|
- | 688 | $out[] = array("$category", "Category $category: Standard"); |
|
- | 689 | ||
- | 690 | $std_scheme = substr($aid,1,1); |
|
- | 691 | if ($std_scheme == '8') { |
|
- | 692 | $out[] = array(" $std_scheme", "Standard identified by OID"); |
|
- | 693 | ||
- | 694 | $data = substr($aid,2); |
|
- | 695 | try { |
|
- | 696 | $interpretations = _aid_e8_interpretations($aid); |
|
- | 697 | foreach ($interpretations as $ii => $interpretation) { |
|
- | 698 | $pos = $interpretation[0]; |
|
- | 699 | $txt1 = $interpretation[1]; // Standard |
|
- | 700 | $txt2 = $interpretation[2]; // PIX (optional) |
|
- | 701 | ||
- | 702 | $aid1 = ' '.substr($aid,2,$pos*2); |
|
- | 703 | $aid2 = substr($aid,2+$pos*2); |
|
- | 704 | ||
- | 705 | $out[] = array("$aid1", "$txt1"); |
|
- | 706 | if ($txt2 !== '') { |
|
- | 707 | $pix = "'$txt2' (".c_literal_hexstr(str_replace(':','',$txt2)).")"; |
|
- | 708 | $out[] = array(str_repeat(' ',strlen($aid1))."$aid2", "with PIX $pix"); |
|
- | 709 | } |
|
- | 710 | if ($ii < count($interpretations)-1) { |
|
- | 711 | $out[] = array('', 'or:'); |
|
- | 712 | } |
|
- | 713 | } |
|
- | 714 | } catch (Exception $e) { |
|
- | 715 | $out[] = array(" $data", "ERROR: ".$e->getMessage()); |
|
- | 716 | ||
- | 717 | } |
|
- | 718 | } else { |
|
- | 719 | // E0..E7, E9..EF are RFU |
|
- | 720 | $unknown = substr($aid,1); |
|
- | 721 | $out[] = array(" $unknown", "ILLEGAL USAGE / RESERVED"); |
|
- | 722 | } |
|
- | 723 | ||
- | 724 | return $out; |
|
- | 725 | } |
|
- | 726 | ||
566 | // Category 'F' |
727 | // Category 'F' |
567 | // AID = 'F' + PIX |
728 | // AID = 'F' + PIX |
568 | if ("$category" === "F") { |
729 | if ("$category" === "F") { |
569 | $out[] = array("$category", "Category $category: Non-registered / Proprietary"); |
730 | $out[] = array("$category", "Category $category: Non-registered / Proprietary"); |
570 | $rid = substr($aid,0,1); |
731 | $rid = substr($aid,0,1); |
Line 576... | Line 737... | ||
576 | $out[] = array('', c_literal_hexstr($pix)); |
737 | $out[] = array('', c_literal_hexstr($pix)); |
577 | } |
738 | } |
578 | return $out; |
739 | return $out; |
579 | } |
740 | } |
580 | 741 | ||
581 | // Category 'B', 'C', and 'E' are reserved |
742 | // Category 'B' and 'C' are reserved |
582 | $out[] = array("$category", "Category $category: ILLEGAL USAGE / RESERVED"); |
743 | $out[] = array("$category", "Category $category: ILLEGAL USAGE / RESERVED"); |
583 | if (strlen($aid) > 1) { |
744 | if (strlen($aid) > 1) { |
584 | $aid_ = substr($aid,1); |
745 | $aid_ = substr($aid,1); |
585 | $out[] = array(" ".$aid_, "Unknown composition"); |
746 | $out[] = array(" ".$aid_, "Unknown composition"); |
586 | $out[] = array('', c_literal_hexstr($aid_)); |
747 | $out[] = array('', c_literal_hexstr($aid_)); |