Esempio n. 1
0
 public function authenticate(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base $message, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     if (!$message instanceof \fpoirotte\Pssht\Messages\USERAUTH\REQUEST\PublicKey) {
         throw new \InvalidArgumentException();
     }
     if ($message->getSignature() === null) {
         return self::AUTH_REJECT;
     }
     $logging = \Plop\Plop::getInstance();
     $reverse = gethostbyaddr($transport->getAddress());
     $algos = \fpoirotte\Pssht\Algorithms::factory();
     $cls = $algos->getClass('PublicKey', $message->getAlgorithm());
     if ($cls === null || !$this->store->exists($message->getUserName(), $message->getKey())) {
         $logging->info('Rejected public key connection from remote host "%(reverse)s" ' . 'to "%(luser)s" (unsupported key)', array('luser' => escape($message->getUserName()), 'reverse' => $reverse));
         return self::AUTH_REJECT;
     }
     $key = $cls::loadPublic(base64_encode($message->getKey()));
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeString($context['DH']->getExchangeHash());
     $encoder->encodeBytes(chr(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base::getMessageId()));
     $encoder->encodeString($message->getUserName());
     $encoder->encodeString($message->getServiceName());
     $encoder->encodeString(static::getName());
     $encoder->encodeBoolean(true);
     $encoder->encodeString($message->getAlgorithm());
     $encoder->encodeString($message->getKey());
     if ($key->check($encoder->getBuffer()->get(0), $message->getSignature())) {
         $logging->info('Accepted public key connection from remote host "%(reverse)s" ' . 'to "%(luser)s" (using "%(algorithm)s" algorithm)', array('luser' => escape($message->getUserName()), 'reverse' => $reverse, 'algorithm' => escape($message->getAlgorithm())));
         return self::AUTH_ACCEPT;
     }
     $logging->info('Rejected public key connection from remote host "%(reverse)s" ' . 'to "%(luser)s" (invalid signature)', array('luser' => escape($message->getUserName()), 'reverse' => $reverse));
     return self::AUTH_REJECT;
 }
Esempio n. 2
0
 public final function compute($seqno, $data)
 {
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $nonce = $encoder->encodeUint64($seqno)->getBuffer()->get(0);
     $res = $this->umac->umac($this->key, $data, $nonce);
     return $res;
 }
Esempio n. 3
0
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     unset($context['rekeying']);
     $response = new \fpoirotte\Pssht\Messages\NEWKEYS();
     $transport->writeMessage($response);
     $logging = \Plop\Plop::getInstance();
     // Reset the various keys.
     $kexAlgo = $context['kexAlgo'];
     $kexAlgo = new $kexAlgo();
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeMpint($context['DH']->getSharedSecret());
     $sharedSecret = $encoder->getBuffer()->get(0);
     $exchangeHash = $context['DH']->getExchangeHash();
     $sessionId = $context['sessionIdentifier'];
     $limiters = array('A' => array($context['C2S']['Encryption'], 'getIVSize'), 'B' => array($context['S2C']['Encryption'], 'getIVSize'), 'C' => array($context['C2S']['Encryption'], 'getKeySize'), 'D' => array($context['S2C']['Encryption'], 'getKeySize'), 'E' => array($context['C2S']['MAC'], 'getKeySize'), 'F' => array($context['C2S']['MAC'], 'getKeySize'));
     $shared = gmp_strval($context['DH']->getSharedSecret(), 16);
     $shared = str_pad($shared, strlen($shared) + 1 >> 1 << 1, '0', STR_PAD_LEFT);
     $logging->debug('Key exchange: %s', array($context['kexAlgo']));
     $logging->debug('Shared secret: %s', array(wordwrap($shared, 16, ' ', true)));
     $logging->debug('Hash: %s', array(wordwrap(bin2hex($exchangeHash), 16, ' ', true)));
     foreach (array('A', 'B', 'C', 'D', 'E', 'F') as $keyIndex) {
         $key = $kexAlgo->hash($sharedSecret . $exchangeHash . $keyIndex . $sessionId);
         $limit = call_user_func($limiters[$keyIndex]);
         while (strlen($key) < $limit) {
             $key .= $kexAlgo->hash($sharedSecret . $exchangeHash . $key);
         }
         $key = (string) substr($key, 0, $limit);
         $context['keys'][$keyIndex] = $key;
         $logging->debug('Key %(keyName)s: %(keyValue)s', array('keyName' => $keyIndex, 'keyValue' => wordwrap(bin2hex($key), 16, ' ', true)));
     }
     // Encryption
     $cls = $context['C2S']['Encryption'];
     $transport->setDecryptor(new $cls($context['keys']['A'], $context['keys']['C']));
     $logging->debug('C2S Encryption: %s', array($cls));
     $cls = $context['S2C']['Encryption'];
     $transport->setEncryptor(new $cls($context['keys']['B'], $context['keys']['D']));
     $logging->debug('S2C Encryption: %s', array($cls));
     // MAC
     $cls = $context['C2S']['MAC'];
     $transport->setInputMAC(new $cls($context['keys']['E']));
     $logging->debug('C2S MAC: %s', array($cls));
     $cls = $context['S2C']['MAC'];
     $transport->setOutputMAC(new $cls($context['keys']['F']));
     $logging->debug('S2C MAC: %s', array($cls));
     // Compression
     $cls = $context['C2S']['Compression'];
     $transport->setUncompressor(new $cls(CompressionInterface::MODE_UNCOMPRESS));
     $logging->debug('C2S Compression: %s', array($cls));
     $cls = $context['S2C']['Compression'];
     $transport->setCompressor(new $cls(CompressionInterface::MODE_COMPRESS));
     $logging->debug('S2C Compression: %s', array($cls));
     return true;
 }
Esempio n. 4
0
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $localChannel = $decoder->decodeUint32();
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeUint32($localChannel);
     $decoder->getBuffer()->unget($encoder->getBuffer()->get(0));
     if (isset($this->handlers[$localChannel][$msgType])) {
         $handler = $this->handlers[$localChannel][$msgType];
         $logging = \Plop\Plop::getInstance();
         $logging->debug('Calling %(handler)s for channel #%(channel)d ' . 'with message type #%(msgType)d', array('handler' => get_class($handler) . '::handle', 'channel' => $localChannel, 'msgType' => $msgType));
         return $handler->handle($msgType, $decoder, $transport, $context);
     }
     return true;
 }
Esempio n. 5
0
 public function serialize(\fpoirotte\Pssht\Wire\Encoder $encoder)
 {
     parent::serialize($encoder);
     $encoder->encodeBoolean($this->signature !== null);
     $encoder->encodeString($this->algorithm);
     $encoder->encodeString($this->key);
     if ($this->signature !== null) {
         $encoder2 = new \fpoirotte\Pssht\Wire\Encoder();
         $encoder2->encodeString($this->algorithm);
         $encoder2->encodeString($this->signature);
         $encoder->encodeString($encoder2->getBuffer()->get(0));
     }
     return $this;
 }
Esempio n. 6
0
 public function serialize(\fpoirotte\Pssht\Wire\Encoder $encoder)
 {
     parent::serialize($encoder);
     $encoder->encodeString($this->algorithm);
     $encoder->encodeString($this->key);
     $encoder->encodeString($this->hostname);
     $encoder->encodeString($this->remoteUser);
     // Special handling of the signature.
     $encoder2 = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder2->encodeString($this->algorithm);
     $encoder2->encodeString($this->signature);
     $encoder->encodeString($encoder2->getBuffer()->get(0));
     return $this;
 }
Esempio n. 7
0
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $channel = $decoder->decodeUint32();
     $type = $decoder->decodeString();
     $wantsReply = $decoder->decodeBoolean();
     $encoder->encodeUint32($channel);
     $encoder->encodeString($type);
     $encoder->encodeBoolean($wantsReply);
     $decoder->getBuffer()->unget($encoder->getBuffer()->get(0));
     $remoteChannel = $this->connection->getChannel($channel);
     switch ($type) {
         case 'exec':
         case 'shell':
         case 'pty-req':
             // Normalize the name.
             // Eg. "pty-req" becomes "PtyReq".
             $cls = str_replace(' ', '', ucwords(str_replace('-', ' ', $type)));
             $cls = '\\fpoirotte\\Pssht\\Messages\\CHANNEL\\REQUEST\\' . $cls;
             $message = $cls::unserialize($decoder);
             break;
         default:
             if ($wantsReply) {
                 $response = new \fpoirotte\Pssht\Messages\CHANNEL\FAILURE($remoteChannel);
                 $transport->writeMessage($response);
             }
             return true;
     }
     if (!$wantsReply) {
         return true;
     }
     if (in_array($type, array('shell', 'exec'), true)) {
         $response = new \fpoirotte\Pssht\Messages\CHANNEL\SUCCESS($remoteChannel);
     } else {
         $response = new \fpoirotte\Pssht\Messages\CHANNEL\FAILURE($remoteChannel);
     }
     $transport->writeMessage($response);
     if (in_array($type, array('shell', 'exec'), true)) {
         $callable = $transport->getApplicationFactory();
         if ($callable !== null) {
             call_user_func($callable, $transport, $this->connection, $message);
         }
     }
     return true;
 }
Esempio n. 8
0
 /**
  * Construct a new SSH_MSG_KEXDH_REPLY message.
  *
  *  \param fpoirotte::Pssht::Messages::KEXDH::INIT $kexDHInit
  *      Client's contribution to the Diffie-Hellman Key Exchange.
  *
  *  \param fpoirotte::Pssht::PublicKeyInterface $key
  *      Server's public key.
  *
  *  \param fpoirotte::Pssht::EncryptionInterface $encryptionAlgo
  *      Encryption algorithm in use.
  *
  *  \param fpoirotte::Pssht::KEXInterface $kexAlgo
  *      Key exchange algorithm to use.
  *
  *  \param fpoirotte::Pssht::Messages::KEXINIT $serverKEX
  *      Algorithms supported by the server.
  *
  *  \param fpoirotte::Pssht::Messages::KEXINIT $clientKEX
  *      Algorithms supported by the client.
  *
  *  \param string $serverIdent
  *      Server's identification string
  *
  *  \param string $clientIdent
  *      Client's identification string
  */
 public function __construct(\fpoirotte\Pssht\Messages\KEXDH\INIT $kexDHInit, \fpoirotte\Pssht\PublicKeyInterface $key, \fpoirotte\Pssht\EncryptionInterface $encryptionAlgo, \fpoirotte\Pssht\KEXInterface $kexAlgo, \fpoirotte\Pssht\Messages\KEXINIT $serverKEX, \fpoirotte\Pssht\Messages\KEXINIT $clientKEX, $serverIdent, $clientIdent)
 {
     if (!is_string($serverIdent)) {
         throw new \InvalidArgumentException();
     }
     if (!is_string($clientIdent)) {
         throw new \InvalidArgumentException();
     }
     $keyLength = min(20, max($encryptionAlgo->getKeySize(), 16));
     $randBytes = openssl_random_pseudo_bytes(2 * $keyLength);
     $y = gmp_init(bin2hex($randBytes), 16);
     $prime = gmp_init($kexAlgo::getPrime(), 16);
     $this->f = gmp_powm($kexAlgo::getGenerator(), $y, $prime);
     $this->K = gmp_powm($kexDHInit->getE(), $y, $prime);
     $this->K_S = $key;
     $this->kexDHInit = $kexDHInit;
     $this->kexAlgo = $kexAlgo;
     $this->serverKEX = $serverKEX;
     $this->clientKEX = $clientKEX;
     $this->serverIdent = $serverIdent;
     $this->clientIdent = $clientIdent;
     $msgId = chr(\fpoirotte\Pssht\Messages\KEXINIT::getMessageId());
     // $sub is used to create the structure for the hashing function.
     $sub = new \fpoirotte\Pssht\Wire\Encoder(new \fpoirotte\Pssht\Buffer());
     $this->K_S->serialize($sub);
     $K_S = $sub->getBuffer()->get(0);
     $sub->encodeString($this->clientIdent);
     $sub->encodeString($this->serverIdent);
     // $sub2 is used to compute the value
     // of various fields inside the structure.
     $sub2 = new \fpoirotte\Pssht\Wire\Encoder(new \fpoirotte\Pssht\Buffer());
     $sub2->encodeBytes($msgId);
     // Add message identifier.
     $this->clientKEX->serialize($sub2);
     $sub->encodeString($sub2->getBuffer()->get(0));
     $sub2->encodeBytes($msgId);
     // Add message identifier.
     $this->serverKEX->serialize($sub2);
     $sub->encodeString($sub2->getBuffer()->get(0));
     $sub->encodeString($K_S);
     $sub->encodeMpint($this->kexDHInit->getE());
     $sub->encodeMpint($this->f);
     $sub->encodeMpint($this->K);
     $logging = \Plop\Plop::getInstance();
     $origData = $sub->getBuffer()->get(0);
     $data = wordwrap(bin2hex($origData), 4, ' ', true);
     $data = wordwrap($data, 32 + 7, PHP_EOL, true);
     $logging->debug("Signature payload:\r\n%s", array($data));
     $this->H = $this->kexAlgo->hash($origData);
 }
Esempio n. 9
0
 /**
  * Write an SSH message into the output buffer.
  *
  *  \param fpoirotte::Pssht::MessageInterface $message
  *      Message to write into the output buffer.
  *
  *  \retval Transport
  *      Returns this transport layer.
  */
 public function writeMessage(\fpoirotte\Pssht\MessageInterface $message)
 {
     $logging = \Plop\Plop::getInstance();
     // Serialize the message.
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeBytes(chr($message::getMessageId()));
     $message->serialize($encoder);
     $payload = $encoder->getBuffer()->get(0);
     $logging->debug('Sending payload: %s', array(\escape($payload)));
     // Compress the payload if necessary.
     $payload = $this->compressor->update($payload);
     $size = strlen($payload);
     $blockSize = max(8, $this->encryptor->getBlockSize());
     // Compute padding requirements.
     // See http://api.libssh.org/rfc/PROTOCOL
     // for more information on EtM (Encrypt-then-MAC)
     // and RFCs 5116 & 5647 for AEAD & AES-GCM.
     if ($this->outMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) {
         $padSize = $blockSize - (1 + $size) % $blockSize;
     } elseif ($this->encryptor instanceof \fpoirotte\Pssht\AEADInterface) {
         $padSize = $blockSize - (1 + $size) % $blockSize;
     } else {
         $padSize = $blockSize - (1 + 4 + $size) % $blockSize;
     }
     if ($padSize < 4) {
         $padSize = ($padSize + $blockSize) % 256;
     }
     $padding = openssl_random_pseudo_bytes($padSize);
     // Create the packet. Every content passed to $encoder
     // will be encrypted, except possibly for the packet
     // length (see below).
     $encoder->encodeUint32(1 + $size + $padSize);
     if ($this->outMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) {
         // Send the packet length in plaintext.
         $encSize = $encoder->getBuffer()->get(0);
         $this->encoder->encodeBytes($encSize);
     }
     $encoder->encodeBytes(chr($padSize));
     $encoder->encodeBytes($payload);
     $encoder->encodeBytes($padding);
     $packet = $encoder->getBuffer()->get(0);
     $encrypted = $this->encryptor->encrypt($this->outSeqNo, $packet);
     // Compute the MAC.
     if ($this->outMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) {
         $mac = $this->outMAC->compute($this->outSeqNo, $encSize . $encrypted);
     } else {
         $mac = $this->outMAC->compute($this->outSeqNo, $packet);
     }
     // Send the packet on the wire.
     $this->encoder->encodeBytes($encrypted);
     $this->encoder->encodeBytes($mac);
     $this->outSeqNo = ++$this->outSeqNo & 0xffffffff;
     $logging->debug('Sending %(type)s packet ' . '(size: %(size)d, payload: %(payload)d, ' . 'block: %(block)d, padding: %(padding)d)', array('type' => get_class($message), 'size' => strlen($encrypted), 'payload' => $size, 'block' => $blockSize, 'padding' => $padSize));
     return $this;
 }
Esempio n. 10
0
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     if ($this->connection !== null) {
         // Silently ignore subsequent authentication requests
         // after a successful authentication took place.
         return true;
     }
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $user = $decoder->decodeString();
     $service = $decoder->decodeString();
     $method = $decoder->decodeString();
     $encoder->encodeString($user);
     $encoder->encodeString($service);
     $encoder->encodeString($method);
     $decoder->getBuffer()->unget($encoder->getBuffer()->get(0));
     if (!isset($context['authMethods'])) {
         $context['authMethods'] = $this->methods;
     }
     if (!isset($context['banner'])) {
         $context['banner'] = (string) $transport->getBanner();
         if ($context['banner'] !== '') {
             $response = new \fpoirotte\Pssht\Messages\USERAUTH\BANNER($context['banner']);
             $transport->writeMessage($response);
         }
     }
     if (!isset($context['authMethods'][$method])) {
         return $this->failure($transport, $context);
     }
     $messagesCls = array('none' => '\\fpoirotte\\Pssht\\Messages\\USERAUTH\\REQUEST\\None', 'hostbased' => '\\fpoirotte\\Pssht\\Messages\\USERAUTH\\REQUEST\\HostBased', 'password' => '\\fpoirotte\\Pssht\\Messages\\USERAUTH\\REQUEST\\Password', 'publickey' => '\\fpoirotte\\Pssht\\Messages\\USERAUTH\\REQUEST\\PublicKey');
     $methodObj = $context['authMethods'][$method];
     $message = $messagesCls[$method]::unserialize($decoder);
     switch ($methodObj->check($message, $transport, $context)) {
         case AuthenticationInterface::CHECK_IGNORE:
             return true;
         case AuthenticationInterface::CHECK_REJECT:
             return $this->failure($transport, $context);
         case AuthenticationInterface::CHECK_OK:
             break;
         default:
             throw new \RuntimeException();
     }
     switch ($methodObj->authenticate($message, $transport, $context)) {
         case AuthenticationInterface::AUTH_REMOVE:
             unset($context['authMethods'][$method]);
             // Do not break.
         // Do not break.
         case AuthenticationInterface::AUTH_REJECT:
             return $this->failure($transport, $context);
         case AuthenticationInterface::AUTH_ACCEPT:
             break;
         default:
             throw new \RuntimeException();
     }
     unset($context['authMethods'][$method]);
     $response = new \fpoirotte\Pssht\Messages\USERAUTH\SUCCESS();
     $this->connection = new \fpoirotte\Pssht\Connection($transport);
     $transport->writeMessage($response);
     $compressor = $transport->getCompressor();
     if ($compressor instanceof \fpoirotte\Pssht\DelayedCompressionInterface) {
         $compressor->setAuthenticated();
     }
     $uncompressor = $transport->getUncompressor();
     if ($uncompressor instanceof \fpoirotte\Pssht\DelayedCompressionInterface) {
         $uncompressor->setAuthenticated();
     }
     return true;
 }
Esempio n. 11
0
 public function authenticate(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base $message, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     if (!$message instanceof \fpoirotte\Pssht\Messages\USERAUTH\REQUEST\HostBased) {
         throw new \InvalidArgumentException();
     }
     $logging = \Plop\Plop::getInstance();
     $reverse = gethostbyaddr($transport->getAddress());
     $untrustedHost = rtrim($message->getHostname(), '.');
     $algos = \fpoirotte\Pssht\Algorithms::factory();
     $cls = $algos->getClass('PublicKey', $message->getAlgorithm());
     if ($cls === null || !$this->store->exists($message->getUserName(), $message->getKey())) {
         $logging->info('Rejected host based connection from %(ruser)s@%(rhost)s ' . '(%(ruser)s@%(reverse)s) to "%(luser)s" ' . '(unsupported key)', array('ruser' => escape($message->getRemoteUser()), 'luser' => escape($message->getUserName()), 'rhost' => escape($untrustedHost), 'reverse' => $reverse));
         return self::AUTH_REMOVE;
     }
     $key = $cls::loadPublic(base64_encode($message->getKey()));
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeString($context['DH']->getExchangeHash());
     $encoder->encodeBytes(chr(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base::getMessageId()));
     $encoder->encodeString($message->getUserName());
     $encoder->encodeString($message->getServiceName());
     $encoder->encodeString(static::getName());
     $encoder->encodeString($message->getAlgorithm());
     $encoder->encodeString($message->getKey());
     $encoder->encodeString($message->getHostname());
     $encoder->encodeString($message->getRemoteUser());
     if (!$key->check($encoder->getBuffer()->get(0), $message->getSignature())) {
         $logging->warn('Rejected host based connection from %(ruser)s@%(rhost)s ' . '(%(ruser)s@%(reverse)s) to "%(luser)s" (invalid signature)', array('ruser' => escape($message->getRemoteUser()), 'luser' => escape($message->getUserName()), 'rhost' => escape($untrustedHost), 'reverse' => $reverse));
         return self::AUTH_REJECT;
     }
     if ($reverse !== $untrustedHost) {
         $logging->warning('Ignored reverse lookup mismatch for %(address)s (' . '"%(reverse)s" vs. "%(untrusted)s")', array('address' => $transport->getAddress(), 'reverse' => $reverse, 'untrusted' => escape($untrustedHost)));
     }
     if ($message->getUserName() !== $message->getRemoteUser()) {
         $logging->warning('Rejected host based connection from %(ruser)s@%(rhost)s ' . '(%(ruser)s@%(reverse)s): remote user does not match ' . 'local user (%(luser)s)', array('ruser' => escape($message->getRemoteUser()), 'luser' => escape($message->getUserName()), 'rhost' => escape($untrustedHost), 'reverse' => $reverse));
         return self::AUTH_REMOVE;
     }
     $logging->info('Accepted host based connection ' . 'from "%(ruser)s@%(rhost)s" (%(ruser)s@%(reverse)s) ' . 'to "%(luser)s" (using "%(algorithm)s" algorithm)', array('ruser' => escape($message->getRemoteUser()), 'luser' => escape($message->getUserName()), 'rhost' => escape($untrustedHost), 'reverse' => $reverse, 'algorithm' => escape($message->getAlgorithm())));
     return self::AUTH_ACCEPT;
 }
Esempio n. 12
0
 /**
  * Construct a new SSH_MSG_KEXDH_REPLY message.
  *
  *  \param fpoirotte::Pssht::ECC::Curve $curve
  *      Elliptic curve in use.
  *
  *  \param fpoirotte::Pssht::Messages::KEX::ECDH::INIT::RFC5656 $kexDHInit
  *      Client's contribution to the Diffie-Hellman Key Exchange.
  *
  *  \param fpoirotte::Pssht::PublicKeyInterface $key
  *      Server's public key.
  *
  *  \param fpoirotte::Pssht::EncryptionInterface $encryptionAlgo
  *      Encryption algorithm in use.
  *
  *  \param fpoirotte::Pssht::KEXInterface $kexAlgo
  *      Key exchange algorithm to use.
  *
  *  \param fpoirotte::Pssht::Messages::KEXINIT $serverKEX
  *      Algorithms supported by the server.
  *
  *  \param fpoirotte::Pssht::Messages::KEXINIT $clientKEX
  *      Algorithms supported by the client.
  *
  *  \param string $serverIdent
  *      Server's identification string
  *
  *  \param string $clientIdent
  *      Client's identification string
  */
 public function __construct(\fpoirotte\Pssht\ECC\Curve $curve, \fpoirotte\Pssht\Messages\KEX\ECDH\INIT\RFC5656 $kexDHInit, \fpoirotte\Pssht\PublicKeyInterface $key, \fpoirotte\Pssht\EncryptionInterface $encryptionAlgo, \fpoirotte\Pssht\KEXInterface $kexAlgo, \fpoirotte\Pssht\Messages\KEXINIT $serverKEX, \fpoirotte\Pssht\Messages\KEXINIT $clientKEX, $serverIdent, $clientIdent)
 {
     if (!is_string($serverIdent)) {
         throw new \InvalidArgumentException();
     }
     if (!is_string($clientIdent)) {
         throw new \InvalidArgumentException();
     }
     $len = strlen(gmp_strval($curve->getOrder(), 2));
     $len = ceil($len / 8);
     $randBytes = openssl_random_pseudo_bytes($len);
     $d_S = gmp_mod(gmp_init(bin2hex($randBytes), 16), $curve->getModulus());
     $this->Q_S = $curve->getGenerator()->multiply($curve, $d_S);
     $Q_C = $kexDHInit->getQ();
     /// @FIXME this is not optimal...
     $algorithms = \fpoirotte\Pssht\Algorithms::factory();
     $cls = $algorithms->getClass('PublicKey', 'ecdsa-sha2-' . $curve->getName());
     $clientPK = new $cls($Q_C);
     if (!$clientPK->isValid()) {
         throw new \InvalidArgumentException();
     }
     // EC Co-factor DH (sec1-v2, section 3.3.2).
     $P = $Q_C->multiply($curve, gmp_mul($curve->getCofactor(), $d_S));
     if ($P->isIdentity($curve)) {
         throw new \InvalidArgumentException();
     }
     $this->K = $P->x;
     $this->curve = $curve;
     $this->K_S = $key;
     $this->kexDHInit = $kexDHInit;
     $this->kexAlgo = $kexAlgo;
     $this->serverKEX = $serverKEX;
     $this->clientKEX = $clientKEX;
     $this->serverIdent = $serverIdent;
     $this->clientIdent = $clientIdent;
     $msgId = chr(\fpoirotte\Pssht\Messages\KEXINIT::getMessageId());
     // $sub is used to create the structure for the hashing function.
     $sub = new \fpoirotte\Pssht\Wire\Encoder(new \fpoirotte\Pssht\Buffer());
     $this->K_S->serialize($sub);
     $K_S = $sub->getBuffer()->get(0);
     $sub->encodeString($this->clientIdent);
     $sub->encodeString($this->serverIdent);
     // $sub2 is used to compute the value
     // of various fields inside the structure.
     $sub2 = new \fpoirotte\Pssht\Wire\Encoder(new \fpoirotte\Pssht\Buffer());
     $sub2->encodeBytes($msgId);
     // Add message identifier.
     $this->clientKEX->serialize($sub2);
     $sub->encodeString($sub2->getBuffer()->get(0));
     $sub2->encodeBytes($msgId);
     // Add message identifier.
     $this->serverKEX->serialize($sub2);
     $sub->encodeString($sub2->getBuffer()->get(0));
     $sub->encodeString($K_S);
     $sub->encodeString($Q_C->serialize($curve));
     $sub->encodeString($this->Q_S->serialize($curve));
     $sub->encodeMpint($this->K);
     $logging = \Plop\Plop::getInstance();
     $origData = $sub->getBuffer()->get(0);
     $data = wordwrap(bin2hex($origData), 4, ' ', true);
     $data = wordwrap($data, 32 + 7, PHP_EOL, true);
     $logging->debug("Signature payload:\r\n%s", array($data));
     $this->H = $this->kexAlgo->hash($origData);
 }