/**
  * @covers Hex::encode()
  * @covers Hex::decode()
  * @covers Hex::encodeUpper()
  */
 public function testRandom()
 {
     for ($i = 1; $i < 32; ++$i) {
         for ($j = 0; $j < 50; ++$j) {
             $random = \random_bytes($i);
             $enc = Hex::encode($random);
             $this->assertSame($random, Hex::decode($enc));
             $this->assertSame(\bin2hex($random), $enc);
             $enc = Hex::encodeUpper($random);
             $this->assertSame($random, Hex::decode($enc));
             $this->assertSame(\strtoupper(\bin2hex($random)), $enc);
         }
     }
 }
 public function testVectorBase16()
 {
     $this->assertSame(Hex::encode(''), '');
     $this->assertSame(Hex::encode('f'), '66');
     $this->assertSame(Hex::encode('fo'), '666f');
     $this->assertSame(Hex::encode('foo'), '666f6f');
     $this->assertSame(Hex::encode('foob'), '666f6f62');
     $this->assertSame(Hex::encode('fooba'), '666f6f6261');
     $this->assertSame(Hex::encode('foobar'), '666f6f626172');
     $this->assertSame(Hex::encodeUpper(''), '');
     $this->assertSame(Hex::encodeUpper('f'), '66');
     $this->assertSame(Hex::encodeUpper('fo'), '666F');
     $this->assertSame(Hex::encodeUpper('foo'), '666F6F');
     $this->assertSame(Hex::encodeUpper('foob'), '666F6F62');
     $this->assertSame(Hex::encodeUpper('fooba'), '666F6F6261');
     $this->assertSame(Hex::encodeUpper('foobar'), '666F6F626172');
 }
Esempio n. 3
0
 /**
  * Convert a private key to the appropriate format.
  *
  * @access public
  * @param \phpseclib\Math\BigInteger $n
  * @param \phpseclib\Math\BigInteger $e
  * @param \phpseclib\Math\BigInteger $d
  * @param array $primes
  * @param array $exponents
  * @param array $coefficients
  * @param string $password optional
  * @return string
  */
 static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '')
 {
     if (count($primes) != 2) {
         return false;
     }
     $raw = array('modulus' => $n->toBytes(true), 'publicExponent' => $e->toBytes(true), 'privateExponent' => $d->toBytes(true), 'prime1' => $primes[1]->toBytes(true), 'prime2' => $primes[2]->toBytes(true), 'exponent1' => $exponents[1]->toBytes(true), 'exponent2' => $exponents[2]->toBytes(true), 'coefficient' => $coefficients[2]->toBytes(true));
     $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
     $encryption = !empty($password) || is_string($password) ? 'aes256-cbc' : 'none';
     $key .= $encryption;
     $key .= "\r\nComment: " . self::$comment . "\r\n";
     $public = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']);
     $source = pack('Na*Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption, strlen(self::$comment), self::$comment, strlen($public), $public);
     $public = Base64::encode($public);
     $key .= "Public-Lines: " . (strlen($public) + 63 >> 6) . "\r\n";
     $key .= chunk_split($public, 64);
     $private = pack('Na*Na*Na*Na*', strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'], strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient']);
     if (empty($password) && !is_string($password)) {
         $source .= pack('Na*', strlen($private), $private);
         $hashkey = 'putty-private-key-file-mac-key';
     } else {
         $private .= Random::string(16 - (strlen($private) & 15));
         $source .= pack('Na*', strlen($private), $private);
         $crypto = new AES();
         $crypto->setKey(static::generateSymmetricKey($password, 32));
         $crypto->setIV(str_repeat("", $crypto->getBlockLength() >> 3));
         $crypto->disablePadding();
         $private = $crypto->encrypt($private);
         $hashkey = 'putty-private-key-file-mac-key' . $password;
     }
     $private = Base64::encode($private);
     $key .= 'Private-Lines: ' . (strlen($private) + 63 >> 6) . "\r\n";
     $key .= chunk_split($private, 64);
     $hash = new Hash('sha1');
     $hash->setKey(sha1($hashkey, true));
     $key .= 'Private-MAC: ' . Hex::encode($hash->hash($source)) . "\r\n";
     return $key;
 }
Esempio n. 4
0
 /**
  * Converts a BigInteger to a hex string (eg. base-16)).
  *
  * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
  * saved as two's compliment.
  *
  * Here's an example:
  * <code>
  * <?php
  *    $a = new \phpseclib\Math\BigInteger('65');
  *
  *    echo $a->toHex(); // outputs '41'
  * ?>
  * </code>
  *
  * @param bool $twos_compliment
  * @return string
  * @access public
  * @internal Converts a base-2**26 number to base-2**8
  */
 function toHex($twos_compliment = false)
 {
     return Hex::encode($this->toBytes($twos_compliment));
 }
Esempio n. 5
0
 /**
  * Get the Distinguished Name for a certificates subject
  *
  * @param mixed $format optional
  * @param array $dn optional
  * @access public
  * @return bool
  */
 function getDN($format = self::DN_ARRAY, $dn = null)
 {
     if (!isset($dn)) {
         $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
     }
     switch ((int) $format) {
         case self::DN_ARRAY:
             return $dn;
         case self::DN_ASN1:
             $asn1 = new ASN1();
             $asn1->loadOIDs($this->oids);
             $filters = array();
             $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
             $asn1->loadFilters($filters);
             $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
             return $asn1->encodeDER($dn, $this->Name);
         case self::DN_CANON:
             //  No SEQUENCE around RDNs and all string values normalized as
             // trimmed lowercase UTF-8 with all spacing as one blank.
             // constructed RDNs will not be canonicalized
             $asn1 = new ASN1();
             $asn1->loadOIDs($this->oids);
             $filters = array();
             $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
             $asn1->loadFilters($filters);
             $result = '';
             $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
             foreach ($dn['rdnSequence'] as $rdn) {
                 foreach ($rdn as $i => $attr) {
                     $attr =& $rdn[$i];
                     if (is_array($attr['value'])) {
                         foreach ($attr['value'] as $type => $v) {
                             $type = array_search($type, $asn1->ANYmap, true);
                             if ($type !== false && isset($asn1->stringTypeSize[$type])) {
                                 $v = $asn1->convert($v, $type);
                                 if ($v !== false) {
                                     $v = preg_replace('/\\s+/', ' ', $v);
                                     $attr['value'] = strtolower(trim($v));
                                     break;
                                 }
                             }
                         }
                     }
                 }
                 $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName);
             }
             return $result;
         case self::DN_HASH:
             $dn = $this->getDN(self::DN_CANON, $dn);
             $hash = new Hash('sha1');
             $hash = $hash->hash($dn);
             extract(unpack('Vhash', $hash));
             return strtolower(Hex::encode(pack('N', $hash)));
     }
     // Default is to return a string.
     $start = true;
     $output = '';
     $result = array();
     $asn1 = new ASN1();
     $asn1->loadOIDs($this->oids);
     $filters = array();
     $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
     $asn1->loadFilters($filters);
     $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
     foreach ($dn['rdnSequence'] as $field) {
         $prop = $field[0]['type'];
         $value = $field[0]['value'];
         $delim = ', ';
         switch ($prop) {
             case 'id-at-countryName':
                 $desc = 'C';
                 break;
             case 'id-at-stateOrProvinceName':
                 $desc = 'ST';
                 break;
             case 'id-at-organizationName':
                 $desc = 'O';
                 break;
             case 'id-at-organizationalUnitName':
                 $desc = 'OU';
                 break;
             case 'id-at-commonName':
                 $desc = 'CN';
                 break;
             case 'id-at-localityName':
                 $desc = 'L';
                 break;
             case 'id-at-surname':
                 $desc = 'SN';
                 break;
             case 'id-at-uniqueIdentifier':
                 $delim = '/';
                 $desc = 'x500UniqueIdentifier';
                 break;
             case 'id-at-postalAddress':
                 $delim = '/';
                 $desc = 'postalAddress';
                 break;
             default:
                 $delim = '/';
                 $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop);
         }
         if (!$start) {
             $output .= $delim;
         }
         if (is_array($value)) {
             foreach ($value as $type => $v) {
                 $type = array_search($type, $asn1->ANYmap, true);
                 if ($type !== false && isset($asn1->stringTypeSize[$type])) {
                     $v = $asn1->convert($v, $type);
                     if ($v !== false) {
                         $value = $v;
                         break;
                     }
                 }
             }
             if (is_array($value)) {
                 $value = array_pop($value);
                 // Always strip data type.
             }
         } elseif (is_object($value) && $value instanceof Element) {
             $callback = create_function('$x', 'return "\\x" . bin2hex($x[0]);');
             $value = strtoupper(preg_replace_callback('#[^\\x20-\\x7E]#', $callback, $value->element));
         }
         $output .= $desc . '=' . $value;
         $result[$desc] = isset($result[$desc]) ? array_merge((array) $dn[$prop], array($value)) : $value;
         $start = false;
     }
     return $format == self::DN_OPENSSL ? $result : $output;
 }
Esempio n. 6
0
 /**
  * Convert a private key to the appropriate format.
  *
  * @access public
  * @param \phpseclib\Math\BigInteger $n
  * @param \phpseclib\Math\BigInteger $e
  * @param \phpseclib\Math\BigInteger $d
  * @param array $primes
  * @param array $exponents
  * @param array $coefficients
  * @param string $password optional
  * @return string
  */
 static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '')
 {
     $num_primes = count($primes);
     $raw = array('version' => $num_primes == 2 ? chr(0) : chr(1), 'modulus' => $n->toBytes(true), 'publicExponent' => $e->toBytes(true), 'privateExponent' => $d->toBytes(true), 'prime1' => $primes[1]->toBytes(true), 'prime2' => $primes[2]->toBytes(true), 'exponent1' => $exponents[1]->toBytes(true), 'exponent2' => $exponents[2]->toBytes(true), 'coefficient' => $coefficients[2]->toBytes(true));
     $components = array();
     foreach ($raw as $name => $value) {
         $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($value)), $value);
     }
     $RSAPrivateKey = implode('', $components);
     if ($num_primes > 2) {
         $OtherPrimeInfos = '';
         for ($i = 3; $i <= $num_primes; $i++) {
             // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
             //
             // OtherPrimeInfo ::= SEQUENCE {
             //     prime             INTEGER,  -- ri
             //     exponent          INTEGER,  -- di
             //     coefficient       INTEGER   -- ti
             // }
             $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
             $OtherPrimeInfo .= pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
             $OtherPrimeInfo .= pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
             $OtherPrimeInfos .= pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
         }
         $RSAPrivateKey .= pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
     }
     $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
     if (!empty($password) || is_string($password)) {
         $cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm);
         $iv = Random::string($cipher->getBlockLength() >> 3);
         $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3));
         $cipher->setIV($iv);
         $iv = strtoupper(Hex::encode($iv));
         $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . "Proc-Type: 4,ENCRYPTED\r\n" . "DEK-Info: " . self::$defaultEncryptionAlgorithm . ",{$iv}\r\n" . "\r\n" . chunk_split(Base64::encode($cipher->encrypt($RSAPrivateKey)), 64) . '-----END RSA PRIVATE KEY-----';
     } else {
         $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . chunk_split(Base64::encode($RSAPrivateKey), 64) . '-----END RSA PRIVATE KEY-----';
     }
     return $RSAPrivateKey;
 }
Esempio n. 7
0
 /**
  * Wrap a private key appropriately
  *
  * @access public
  * @param string $key
  * @param string $type
  * @param string $password
  * @return string
  */
 static function wrapPrivateKey($key, $type, $password)
 {
     if (empty($password) || !is_string($password)) {
         return "-----BEGIN {$type} PRIVATE KEY-----\r\n" . chunk_split(Base64::encode($key), 64) . "-----END {$type} PRIVATE KEY-----";
     }
     $cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm);
     $iv = Random::string($cipher->getBlockLength() >> 3);
     $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3));
     $cipher->setIV($iv);
     $iv = strtoupper(Hex::encode($iv));
     return "-----BEGIN {$type} PRIVATE KEY-----\r\n" . "Proc-Type: 4,ENCRYPTED\r\n" . "DEK-Info: " . self::$defaultEncryptionAlgorithm . ",{$iv}\r\n" . "\r\n" . chunk_split(Base64::encode($cipher->encrypt($key)), 64) . "-----END {$type} PRIVATE KEY-----";
 }
Esempio n. 8
0
 /**
  * Parse Attributes
  *
  * See '7.  File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
  *
  * @param string $response
  * @return array
  * @access private
  */
 function _parseAttributes(&$response)
 {
     $attr = array();
     if (strlen($response) < 4) {
         //user_error('Malformed file attributes');
         return array();
     }
     extract(unpack('Nflags', Strings::shift($response, 4)));
     // SFTPv4+ have a type field (a byte) that follows the above flag field
     foreach ($this->attributes as $key => $value) {
         switch ($flags & $key) {
             case NET_SFTP_ATTR_SIZE:
                 // 0x00000001
                 // The size attribute is defined as an unsigned 64-bit integer.
                 // The following will use floats on 32-bit platforms, if necessary.
                 // As can be seen in the BigInteger class, floats are generally
                 // IEEE 754 binary64 "double precision" on such platforms and
                 // as such can represent integers of at least 2^50 without loss
                 // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
                 $attr['size'] = hexdec(Hex::encode(Strings::shift($response, 8)));
                 break;
             case NET_SFTP_ATTR_UIDGID:
                 // 0x00000002 (SFTPv3 only)
                 if (strlen($response) < 8) {
                     //user_error('Malformed file attributes');
                     return $attr;
                 }
                 $attr += unpack('Nuid/Ngid', Strings::shift($response, 8));
                 break;
             case NET_SFTP_ATTR_PERMISSIONS:
                 // 0x00000004
                 if (strlen($response) < 4) {
                     //user_error('Malformed file attributes');
                     return $attr;
                 }
                 $attr += unpack('Npermissions', Strings::shift($response, 4));
                 // mode == permissions; permissions was the original array key and is retained for bc purposes.
                 // mode was added because that's the more industry standard terminology
                 $attr += array('mode' => $attr['permissions']);
                 $fileType = $this->_parseMode($attr['permissions']);
                 if ($fileType !== false) {
                     $attr += array('type' => $fileType);
                 }
                 break;
             case NET_SFTP_ATTR_ACCESSTIME:
                 // 0x00000008
                 if (strlen($response) < 8) {
                     //user_error('Malformed file attributes');
                     return $attr;
                 }
                 $attr += unpack('Natime/Nmtime', Strings::shift($response, 8));
                 break;
             case NET_SFTP_ATTR_EXTENDED:
                 // 0x80000000
                 if (strlen($response) < 4) {
                     //user_error('Malformed file attributes');
                     return $attr;
                 }
                 extract(unpack('Ncount', Strings::shift($response, 4)));
                 for ($i = 0; $i < $count; $i++) {
                     if (strlen($response) < 4) {
                         //user_error('Malformed file attributes');
                         return $attr;
                     }
                     extract(unpack('Nlength', Strings::shift($response, 4)));
                     $key = Strings::shift($response, $length);
                     if (strlen($response) < 4) {
                         //user_error('Malformed file attributes');
                         return $attr;
                     }
                     extract(unpack('Nlength', Strings::shift($response, 4)));
                     $attr[$key] = Strings::shift($response, $length);
                 }
         }
     }
     return $attr;
 }
Esempio n. 9
0
 /**
  * Get the Distinguished Name for a certificates subject
  *
  * @param mixed $format optional
  * @param array $dn optional
  * @access public
  * @return bool
  */
 function getDN($format = self::DN_ARRAY, $dn = null)
 {
     if (!isset($dn)) {
         $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
     }
     switch ((int) $format) {
         case self::DN_ARRAY:
             return $dn;
         case self::DN_ASN1:
             $asn1 = new ASN1();
             $asn1->loadOIDs($this->oids);
             $filters = array();
             $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
             $asn1->loadFilters($filters);
             return $asn1->encodeDER($dn, $this->Name);
         case self::DN_OPENSSL:
             $dn = $this->getDN(self::DN_STRING, $dn);
             if ($dn === false) {
                 return false;
             }
             $attrs = preg_split('#((?:^|, *|/)[a-z][a-z0-9]*=)#i', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
             $dn = array();
             for ($i = 1; $i < count($attrs); $i += 2) {
                 $prop = trim($attrs[$i], ', =/');
                 $value = $attrs[$i + 1];
                 if (!isset($dn[$prop])) {
                     $dn[$prop] = $value;
                 } else {
                     $dn[$prop] = array_merge((array) $dn[$prop], array($value));
                 }
             }
             return $dn;
         case self::DN_CANON:
             //  No SEQUENCE around RDNs and all string values normalized as
             // trimmed lowercase UTF-8 with all spacing  as one blank.
             $asn1 = new ASN1();
             $asn1->loadOIDs($this->oids);
             $filters = array();
             $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
             $asn1->loadFilters($filters);
             $result = '';
             foreach ($dn['rdnSequence'] as $rdn) {
                 foreach ($rdn as $i => $attr) {
                     $attr =& $rdn[$i];
                     if (is_array($attr['value'])) {
                         foreach ($attr['value'] as $type => $v) {
                             $type = array_search($type, $asn1->ANYmap, true);
                             if ($type !== false && isset($asn1->stringTypeSize[$type])) {
                                 $v = $asn1->convert($v, $type);
                                 if ($v !== false) {
                                     $v = preg_replace('/\\s+/', ' ', $v);
                                     $attr['value'] = strtolower(trim($v));
                                     break;
                                 }
                             }
                         }
                     }
                 }
                 $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName);
             }
             return $result;
         case self::DN_HASH:
             $dn = $this->getDN(self::DN_CANON, $dn);
             $hash = new Hash('sha1');
             $hash = $hash->hash($dn);
             extract(unpack('Vhash', $hash));
             return Hex::encode(pack('N', $hash));
     }
     // Default is to return a string.
     $start = true;
     $output = '';
     $asn1 = new ASN1();
     foreach ($dn['rdnSequence'] as $field) {
         $prop = $field[0]['type'];
         $value = $field[0]['value'];
         $delim = ', ';
         switch ($prop) {
             case 'id-at-countryName':
                 $desc = 'C=';
                 break;
             case 'id-at-stateOrProvinceName':
                 $desc = 'ST=';
                 break;
             case 'id-at-organizationName':
                 $desc = 'O=';
                 break;
             case 'id-at-organizationalUnitName':
                 $desc = 'OU=';
                 break;
             case 'id-at-commonName':
                 $desc = 'CN=';
                 break;
             case 'id-at-localityName':
                 $desc = 'L=';
                 break;
             case 'id-at-surname':
                 $desc = 'SN=';
                 break;
             case 'id-at-uniqueIdentifier':
                 $delim = '/';
                 $desc = 'x500UniqueIdentifier=';
                 break;
             default:
                 $delim = '/';
                 $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop) . '=';
         }
         if (!$start) {
             $output .= $delim;
         }
         if (is_array($value)) {
             foreach ($value as $type => $v) {
                 $type = array_search($type, $asn1->ANYmap, true);
                 if ($type !== false && isset($asn1->stringTypeSize[$type])) {
                     $v = $asn1->convert($v, $type);
                     if ($v !== false) {
                         $value = $v;
                         break;
                     }
                 }
             }
             if (is_array($value)) {
                 $value = array_pop($value);
                 // Always strip data type.
             }
         }
         $output .= $desc . $value;
         $start = false;
     }
     return $output;
 }