Exemple #1
0
 /**
  * Encodes a stdClass instance.
  *
  * @param \stdClass $data The EMV TLV data structure.
  *
  * @return string Returns a string representing the encoded EMV TLV data
  *                structure.
  */
 protected function encodeObject(\stdClass $data)
 {
     if (!isset($data->tag, $data->value) || !in_array(strlen($data->tag), array(2, 4, 6))) {
         return '';
     }
     $value = (string) $data->value;
     if (Tag::isConstructed(hexdec(substr($data->tag, 0, 2)))) {
         $value = $this->encode($data->value);
     }
     $length = strlen($value) / 2;
     /*
      * ISO/IEC 7816-4:2013, sect. 5.2.2.2: "ISO/IEC 7816 supports length
      * fields [...] up to five bytes."
      *
      * This means that the value field can be made of 4294967295 (FFFFFFFF)
      * bytes at most.
      *
      * If the length excesses this value, bail out.
      */
     if ($length > 4294967295.0) {
         return '';
     }
     $bytes = $length < 128 ? 1 : ($length < 256 ? 2 : ($length < 65536 ? 3 : ($length < 16777216 ? 4 : 5)));
     if ($bytes === 1) {
         $length = sprintf('%02X', $length);
     } else {
         $length = sprintf('%032b', $length);
         $length = str_split($length, 8);
         $length = array_map(function ($bits) {
             return sprintf('%02X', bindec($bits));
         }, $length);
         $length = array_filter($length, function ($bits) {
             return '00' !== $bits;
         });
         $length = implode('', $length);
         $length = strval(79 + $bytes) . $length;
     }
     return sprintf('%s%s%s', $data->tag, $length, $value);
 }
Exemple #2
0
 /**
  * Decodes a list of bytes representing some EMV TLV-encoded data.
  *
  * @param int[] $bytes The list of bytes.
  *
  * @return \stdClass[] Returns a list of \stdClass instances containing the
  *         parsed EMV TLV-encoded data. May contain nested lists of objects.
  */
 protected function decodeTlv($bytes)
 {
     $tree = array();
     $extent = count($bytes);
     $cursor = 0;
     while ($cursor < $extent) {
         // Tag
         // -----------------------------------------------------------------
         if (!isset($bytes[$cursor])) {
             break;
         }
         $tag = array($bytes[$cursor]);
         // Is the tag valid? No? Move the cursor forward and skip any
         // remaining computation.
         if (!Tag::isValid($tag[0])) {
             /**
              * Trigger an error? Throw an exception? Log something? No! To
              * be really useful such a message should include the original
              * EMV-TLV data block, which we cannot disclose inside the log
              * files because of PCI DSS requirements.
              */
             $cursor++;
             continue;
         }
         $tagIsConstructed = Tag::isConstructed($tag[0]);
         if (Tag::isMultiByte($tag[0])) {
             // Two-byte tag
             // -------------------------------------------------------------
             $cursor++;
             if (!isset($bytes[$cursor])) {
                 break;
             }
             $tag[] = $bytes[$cursor];
             if (!Tag::isLast($bytes[$cursor])) {
                 // Three-byte tag
                 // -------------------------------------------------------------
                 $cursor++;
                 if (!isset($bytes[$cursor])) {
                     break;
                 }
                 $tag[] = $bytes[$cursor];
             }
         }
         $tag = array_map(function ($byte) {
             return sprintf('%02X', $byte);
         }, $tag);
         $tag = implode('', $tag);
         $cursor++;
         // Length
         // -----------------------------------------------------------------
         if (!isset($bytes[$cursor])) {
             break;
         }
         $length = $bytes[$cursor];
         if (!Length::isValid($length)) {
             /**
              * Trigger an error? Throw an exception? Log something? No! To
              * be really useful such a message should include the original
              * EMV TLV data block, which we cannot disclose inside the log
              * files because of PCI DSS requirements.
              */
             break;
         }
         $length = Length::getLength($length);
         if (Length::isMultiByte($bytes[$cursor])) {
             $length_cursor = 0;
             $length_extent = $length;
             $length = array();
             while ($length_cursor < $length_extent) {
                 $cursor++;
                 if (!isset($bytes[$cursor])) {
                     break;
                 }
                 $length[] = $bytes[$cursor] << ($length_extent - $length_cursor - 1) * 8;
                 $length_cursor++;
             }
             if (!isset($bytes[$cursor])) {
                 break;
             }
             $length_output = 0;
             foreach ($length as $length_part) {
                 $length_output = $length_output | $length_part;
             }
             $length = $length_output;
         }
         $cursor++;
         // Value
         // -----------------------------------------------------------------
         if (!isset($bytes[$cursor])) {
             break;
         }
         $value = array_slice($bytes, $cursor, $length);
         if ($tagIsConstructed) {
             $value = $this->decodeTlv($value);
         } else {
             $value = array_map(function ($byte) {
                 return sprintf('%02x', $byte);
             }, $value);
             $value = implode('', $value);
         }
         $cursor += $length;
         // All together now!
         // -----------------------------------------------------------------
         $leaf = new \stdClass();
         $leaf->name = Tag::getName($tag);
         $leaf->length = $length;
         $leaf->value = $value;
         // Add it to the output, now!
         // -----------------------------------------------------------------
         $tree[$tag] = $leaf;
     }
     return $tree;
 }