encodeLength() static public method

DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
static public encodeLength ( integer $length ) : string
$length integer
return string
Example #1
0
 /**
  * Convert a public key to the appropriate format
  *
  * @access public
  * @param \phpseclib\Math\BigInteger $n
  * @param \phpseclib\Math\BigInteger $e
  * @return string
  */
 static function savePublicKey(BigInteger $n, BigInteger $e)
 {
     $modulus = $n->toBytes(true);
     $publicExponent = $e->toBytes(true);
     // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
     // RSAPublicKey ::= SEQUENCE {
     //     modulus           INTEGER,  -- n
     //     publicExponent    INTEGER   -- e
     // }
     $components = array('modulus' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($modulus)), $modulus), 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($publicExponent)), $publicExponent));
     $RSAPublicKey = pack('Ca*a*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']);
     $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($RSAPublicKey), 64) . '-----END RSA PUBLIC KEY-----';
     return $RSAPublicKey;
 }
Example #2
0
 /**
  * ASN.1 Encode (Helper function)
  *
  * @param string $source
  * @param string $mapping
  * @param int $idx
  * @return string
  * @throws \RuntimeException if the input has an error in it
  * @access private
  */
 function _encode_der($source, $mapping, $idx = null, $special = array())
 {
     if ($source instanceof Element) {
         return $source->element;
     }
     // do not encode (implicitly optional) fields with value set to default
     if (isset($mapping['default']) && $source === $mapping['default']) {
         return '';
     }
     if (isset($idx)) {
         if (isset($special[$idx])) {
             $source = call_user_func($special[$idx], $source);
         }
         $this->location[] = $idx;
     }
     $tag = $mapping['type'];
     switch ($tag) {
         case self::TYPE_SET:
             // Children order is not important, thus process in sequence.
         // Children order is not important, thus process in sequence.
         case self::TYPE_SEQUENCE:
             $tag |= 0x20;
             // set the constructed bit
             // ignore the min and max
             if (isset($mapping['min']) && isset($mapping['max'])) {
                 $value = array();
                 $child = $mapping['children'];
                 foreach ($source as $content) {
                     $temp = $this->_encode_der($content, $child, null, $special);
                     if ($temp === false) {
                         return false;
                     }
                     $value[] = $temp;
                 }
                 /* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared
                                         as octet strings with the shorter components being padded at their trailing end with 0-octets.
                                         NOTE - The padding octets are for comparison purposes only and do not appear in the encodings."
                 
                                        -- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf  */
                 if ($mapping['type'] == self::TYPE_SET) {
                     sort($value);
                 }
                 $value = implode($value, '');
                 break;
             }
             $value = '';
             foreach ($mapping['children'] as $key => $child) {
                 if (!array_key_exists($key, $source)) {
                     if (!isset($child['optional'])) {
                         return false;
                     }
                     continue;
                 }
                 $temp = $this->_encode_der($source[$key], $child, $key, $special);
                 if ($temp === false) {
                     return false;
                 }
                 // An empty child encoding means it has been optimized out.
                 // Else we should have at least one tag byte.
                 if ($temp === '') {
                     continue;
                 }
                 // if isset($child['constant']) is true then isset($child['optional']) should be true as well
                 if (isset($child['constant'])) {
                     /*
                       From X.680-0207.pdf#page=58 (30.6):
                     
                       "The tagging construction specifies explicit tagging if any of the following holds:
                        ...
                        c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or
                        AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or
                        an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
                     */
                     if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
                         $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | 0x20 | $child['constant']);
                         $temp = $subtag . Functions::encodeLength(strlen($temp)) . $temp;
                     } else {
                         $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | ord($temp[0]) & 0x20 | $child['constant']);
                         $temp = $subtag . substr($temp, 1);
                     }
                 }
                 $value .= $temp;
             }
             break;
         case self::TYPE_CHOICE:
             $temp = false;
             foreach ($mapping['children'] as $key => $child) {
                 if (!isset($source[$key])) {
                     continue;
                 }
                 $temp = $this->_encode_der($source[$key], $child, $key, $special);
                 if ($temp === false) {
                     return false;
                 }
                 // An empty child encoding means it has been optimized out.
                 // Else we should have at least one tag byte.
                 if ($temp === '') {
                     continue;
                 }
                 $tag = ord($temp[0]);
                 // if isset($child['constant']) is true then isset($child['optional']) should be true as well
                 if (isset($child['constant'])) {
                     if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
                         $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | 0x20 | $child['constant']);
                         $temp = $subtag . Functions::encodeLength(strlen($temp)) . $temp;
                     } else {
                         $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | ord($temp[0]) & 0x20 | $child['constant']);
                         $temp = $subtag . substr($temp, 1);
                     }
                 }
             }
             if (isset($idx)) {
                 array_pop($this->location);
             }
             if ($temp && isset($mapping['cast'])) {
                 $temp[0] = chr($mapping['class'] << 6 | $tag & 0x20 | $mapping['cast']);
             }
             return $temp;
         case self::TYPE_INTEGER:
         case self::TYPE_ENUMERATED:
             if (!isset($mapping['mapping'])) {
                 if (is_numeric($source)) {
                     $source = new BigInteger($source);
                 }
                 $value = $source->toBytes(true);
             } else {
                 $value = array_search($source, $mapping['mapping']);
                 if ($value === false) {
                     return false;
                 }
                 $value = new BigInteger($value);
                 $value = $value->toBytes(true);
             }
             if (!strlen($value)) {
                 $value = chr(0);
             }
             break;
         case self::TYPE_UTC_TIME:
         case self::TYPE_GENERALIZED_TIME:
             $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
             $format .= 'mdHis';
             $value = @gmdate($format, strtotime($source)) . 'Z';
             break;
         case self::TYPE_BIT_STRING:
             if (isset($mapping['mapping'])) {
                 $bits = array_fill(0, count($mapping['mapping']), 0);
                 $size = 0;
                 for ($i = 0; $i < count($mapping['mapping']); $i++) {
                     if (in_array($mapping['mapping'][$i], $source)) {
                         $bits[$i] = 1;
                         $size = $i;
                     }
                 }
                 if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) {
                     $size = $mapping['min'] - 1;
                 }
                 $offset = 8 - ($size + 1 & 7);
                 $offset = $offset !== 8 ? $offset : 0;
                 $value = chr($offset);
                 for ($i = $size + 1; $i < count($mapping['mapping']); $i++) {
                     unset($bits[$i]);
                 }
                 $bits = implode('', array_pad($bits, $size + $offset + 1, 0));
                 $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' ')));
                 foreach ($bytes as $byte) {
                     $value .= chr(bindec($byte));
                 }
                 break;
             }
         case self::TYPE_OCTET_STRING:
             /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
                                the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven.
             
                                -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */
             $value = Base64::decode($source);
             break;
         case self::TYPE_OBJECT_IDENTIFIER:
             $oid = preg_match('#(?:\\d+\\.)+#', $source) ? $source : array_search($source, $this->oids);
             if ($oid === false) {
                 throw new \RuntimeException('Invalid OID');
                 return false;
             }
             $value = '';
             $parts = explode('.', $oid);
             $value = chr(40 * $parts[0] + $parts[1]);
             for ($i = 2; $i < count($parts); $i++) {
                 $temp = '';
                 if (!$parts[$i]) {
                     $temp = "";
                 } else {
                     while ($parts[$i]) {
                         $temp = chr(0x80 | $parts[$i] & 0x7f) . $temp;
                         $parts[$i] >>= 7;
                     }
                     $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7f);
                 }
                 $value .= $temp;
             }
             break;
         case self::TYPE_ANY:
             $loc = $this->location;
             if (isset($idx)) {
                 array_pop($this->location);
             }
             switch (true) {
                 case !isset($source):
                     return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special);
                 case is_int($source):
                 case $source instanceof BigInteger:
                     return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special);
                 case is_float($source):
                     return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special);
                 case is_bool($source):
                     return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special);
                 case is_array($source) && count($source) == 1:
                     $typename = implode('', array_keys($source));
                     $outtype = array_search($typename, $this->ANYmap, true);
                     if ($outtype !== false) {
                         return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special);
                     }
             }
             $filters = $this->filters;
             foreach ($loc as $part) {
                 if (!isset($filters[$part])) {
                     $filters = false;
                     break;
                 }
                 $filters = $filters[$part];
             }
             if ($filters === false) {
                 throw new \RuntimeException('No filters defined for ' . implode('/', $loc));
                 return false;
             }
             return $this->_encode_der($source, $filters + $mapping, null, $special);
         case self::TYPE_NULL:
             $value = '';
             break;
         case self::TYPE_NUMERIC_STRING:
         case self::TYPE_TELETEX_STRING:
         case self::TYPE_PRINTABLE_STRING:
         case self::TYPE_UNIVERSAL_STRING:
         case self::TYPE_UTF8_STRING:
         case self::TYPE_BMP_STRING:
         case self::TYPE_IA5_STRING:
         case self::TYPE_VISIBLE_STRING:
         case self::TYPE_VIDEOTEX_STRING:
         case self::TYPE_GRAPHIC_STRING:
         case self::TYPE_GENERAL_STRING:
             $value = $source;
             break;
         case self::TYPE_BOOLEAN:
             $value = $source ? "ÿ" : "";
             break;
         default:
             throw new \RuntimeException('Mapping provides no type definition for ' . implode('/', $this->location));
             return false;
     }
     if (isset($idx)) {
         array_pop($this->location);
     }
     if (isset($mapping['cast'])) {
         if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) {
             $value = chr($tag) . Functions::encodeLength(strlen($value)) . $value;
             $tag = $mapping['class'] << 6 | 0x20 | $mapping['cast'];
         } else {
             $tag = $mapping['class'] << 6 | ord($temp[0]) & 0x20 | $mapping['cast'];
         }
     }
     return chr($tag) . Functions::encodeLength(strlen($value)) . $value;
 }
Example #3
0
 /**
  * Convert a public key to the appropriate format
  *
  * @access public
  * @param \phpseclib\Math\BigInteger $n
  * @param \phpseclib\Math\BigInteger $e
  * @return string
  */
 static function savePublicKey(BigInteger $n, BigInteger $e)
 {
     $modulus = $n->toBytes(true);
     $publicExponent = $e->toBytes(true);
     // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
     // RSAPublicKey ::= SEQUENCE {
     //     modulus           INTEGER,  -- n
     //     publicExponent    INTEGER   -- e
     // }
     $components = array('modulus' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($modulus)), $modulus), 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($publicExponent)), $publicExponent));
     $RSAPublicKey = pack('Ca*a*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']);
     // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
     $rsaOID = "0\r\t*†H†÷\r";
     // hex version of MA0GCSqGSIb3DQEBAQUA
     $RSAPublicKey = chr(0) . $RSAPublicKey;
     $RSAPublicKey = chr(3) . ASN1::encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
     $RSAPublicKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey);
     $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($RSAPublicKey), 64) . '-----END PUBLIC KEY-----';
     return $RSAPublicKey;
 }