phpseclib\Common\Functions\ASN1::decodeLength PHP Method

decodeLength() 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 decodeLength ( string &$string ) : integer
$string string
return integer
    static function decodeLength(&$string)
    {
        $length = ord(Strings::shift($string));
        if ($length & 0x80) {
            // definite length, long form
            $length &= 0x7f;
            $temp = Strings::shift($string, $length);
            list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
        }
        return $length;
    }

Usage Example

示例#1
0
 /**
  * Break a public or private key down into its constituent components
  *
  * @access public
  * @param string $key
  * @param string $password optional
  * @return array
  */
 static function load($key, $password = '')
 {
     if (!is_string($key)) {
         return false;
     }
     $components = array('isPublicKey' => strpos($key, 'PUBLIC') !== false);
     /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
                "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
                protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
                two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
     
                http://tools.ietf.org/html/rfc1421#section-4.6.1.1
                http://tools.ietf.org/html/rfc1421#section-4.6.1.3
     
                DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
                DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
                function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
                own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
                implementation are part of the standard, as well.
     
                * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
     if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
         $iv = Hex::decode(trim($matches[2]));
         // remove the Proc-Type / DEK-Info sections as they're no longer needed
         $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
         $ciphertext = self::_extractBER($key);
         if ($ciphertext === false) {
             $ciphertext = $key;
         }
         $crypto = self::getEncryptionObject($matches[1]);
         $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
         $crypto->setIV($iv);
         $key = $crypto->decrypt($ciphertext);
         if ($key === false) {
             return false;
         }
     } else {
         if (self::$format != self::MODE_DER) {
             $decoded = self::_extractBER($key);
             if ($decoded !== false) {
                 $key = $decoded;
             } elseif (self::$format == self::MODE_PEM) {
                 return false;
             }
         }
     }
     if (ord(Strings::shift($key)) != self::ASN1_SEQUENCE) {
         return false;
     }
     if (ASN1::decodeLength($key) != strlen($key)) {
         return false;
     }
     $tag = ord(Strings::shift($key));
     /* intended for keys for which OpenSSL's asn1parse returns the following:
     
                 0:d=0  hl=4 l= 631 cons: SEQUENCE
                 4:d=1  hl=2 l=   1 prim:  INTEGER           :00
                 7:d=1  hl=2 l=  13 cons:  SEQUENCE
                 9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
                20:d=2  hl=2 l=   0 prim:   NULL
                22:d=1  hl=4 l= 609 prim:  OCTET STRING
     
                ie. PKCS8 keys */
     if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "0") {
         Strings::shift($key, 3);
         $tag = self::ASN1_SEQUENCE;
     }
     if ($tag == self::ASN1_SEQUENCE) {
         $temp = Strings::shift($key, ASN1::decodeLength($key));
         if (ord(Strings::shift($temp)) != self::ASN1_OBJECT) {
             return false;
         }
         $length = ASN1::decodeLength($temp);
         switch (Strings::shift($temp, $length)) {
             case "*†H†÷\r":
                 // rsaEncryption
                 break;
             case "*†H†÷\r":
                 // pbeWithMD5AndDES-CBC
                 /*
                    PBEParameter ::= SEQUENCE {
                        salt OCTET STRING (SIZE(8)),
                        iterationCount INTEGER }
                 */
                 if (ord(Strings::shift($temp)) != self::ASN1_SEQUENCE) {
                     return false;
                 }
                 if (ASN1::decodeLength($temp) != strlen($temp)) {
                     return false;
                 }
                 Strings::shift($temp);
                 // assume it's an octet string
                 $salt = Strings::shift($temp, ASN1::decodeLength($temp));
                 if (ord(Strings::shift($temp)) != self::ASN1_INTEGER) {
                     return false;
                 }
                 ASN1::decodeLength($temp);
                 list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
                 Strings::shift($key);
                 // assume it's an octet string
                 $length = ASN1::decodeLength($key);
                 if (strlen($key) != $length) {
                     return false;
                 }
                 $crypto = new DES(DES::MODE_CBC);
                 $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount);
                 $key = $crypto->decrypt($key);
                 if ($key === false) {
                     return false;
                 }
                 return self::load($key);
             default:
                 return false;
         }
         /* intended for keys for which OpenSSL's asn1parse returns the following:
         
                         0:d=0  hl=4 l= 290 cons: SEQUENCE
                         4:d=1  hl=2 l=  13 cons:  SEQUENCE
                         6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
                        17:d=2  hl=2 l=   0 prim:   NULL
                        19:d=1  hl=4 l= 271 prim:  BIT STRING */
         $tag = ord(Strings::shift($key));
         // skip over the BIT STRING / OCTET STRING tag
         ASN1::decodeLength($key);
         // skip over the BIT STRING / OCTET STRING length
         // "The initial octet shall encode, as an unsigned binary integer wtih 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 (section 8.6.2.2)
         if ($tag == self::ASN1_BITSTRING) {
             Strings::shift($key);
         }
         if (ord(Strings::shift($key)) != self::ASN1_SEQUENCE) {
             return false;
         }
         if (ASN1::decodeLength($key) != strlen($key)) {
             return false;
         }
         $tag = ord(Strings::shift($key));
     }
     if ($tag != self::ASN1_INTEGER) {
         return false;
     }
     $length = ASN1::decodeLength($key);
     $temp = Strings::shift($key, $length);
     if (strlen($temp) != 1 || ord($temp) > 2) {
         $components['modulus'] = new BigInteger($temp, 256);
         Strings::shift($key);
         // skip over self::ASN1_INTEGER
         $length = ASN1::decodeLength($key);
         $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(Strings::shift($key, $length), 256);
         return $components;
     }
     if (ord(Strings::shift($key)) != self::ASN1_INTEGER) {
         return false;
     }
     $length = ASN1::decodeLength($key);
     $components['modulus'] = new BigInteger(Strings::shift($key, $length), 256);
     Strings::shift($key);
     $length = ASN1::decodeLength($key);
     $components['publicExponent'] = new BigInteger(Strings::shift($key, $length), 256);
     Strings::shift($key);
     $length = ASN1::decodeLength($key);
     $components['privateExponent'] = new BigInteger(Strings::shift($key, $length), 256);
     Strings::shift($key);
     $length = ASN1::decodeLength($key);
     $components['primes'] = array(1 => new BigInteger(Strings::shift($key, $length), 256));
     Strings::shift($key);
     $length = ASN1::decodeLength($key);
     $components['primes'][] = new BigInteger(Strings::shift($key, $length), 256);
     Strings::shift($key);
     $length = ASN1::decodeLength($key);
     $components['exponents'] = array(1 => new BigInteger(Strings::shift($key, $length), 256));
     Strings::shift($key);
     $length = ASN1::decodeLength($key);
     $components['exponents'][] = new BigInteger(Strings::shift($key, $length), 256);
     Strings::shift($key);
     $length = ASN1::decodeLength($key);
     $components['coefficients'] = array(2 => new BigInteger(Strings::shift($key, $length), 256));
     if (!empty($key)) {
         if (ord(Strings::shift($key)) != self::ASN1_SEQUENCE) {
             return false;
         }
         ASN1::decodeLength($key);
         while (!empty($key)) {
             if (ord(Strings::shift($key)) != self::ASN1_SEQUENCE) {
                 return false;
             }
             ASN1::decodeLength($key);
             $key = substr($key, 1);
             $length = ASN1::decodeLength($key);
             $components['primes'][] = new BigInteger(Strings::shift($key, $length), 256);
             Strings::shift($key);
             $length = ASN1::decodeLength($key);
             $components['exponents'][] = new BigInteger(Strings::shift($key, $length), 256);
             Strings::shift($key);
             $length = ASN1::decodeLength($key);
             $components['coefficients'][] = new BigInteger(Strings::shift($key, $length), 256);
         }
     }
     return $components;
 }