protected static function unserializeSub(\fpoirotte\Pssht\Wire\Decoder $decoder) { $signature = $decoder->decodeBoolean(); $algorithm = $decoder->decodeString(); $res = array($algorithm, $decoder->decodeString()); if ($signature === true) { $decoder2 = new \fpoirotte\Pssht\Wire\Decoder(new \fpoirotte\Pssht\Buffer($decoder->decodeString())); if ($decoder2->decodeString() !== $algorithm) { throw new \InvalidArgumentException(); } $res[] = $decoder2->decodeString(); } return $res; }
public static function loadPublic($b64) { $decoder = new \fpoirotte\Pssht\Wire\Decoder(); $decoder->getBuffer()->push(base64_decode($b64)); $type = $decoder->decodeString(); if ($type !== static::getName()) { throw new \InvalidArgumentException(); } $pk = $decoder->decodeString(); if ($pk === null) { throw new \InvalidArgumentException(); } return new static(gmp_init(bin2hex($pk), 16)); }
/** * Try to read and handle a single SSH message. * * \retval bool * \b true if a message was successfully read and handled, * \b false otherwise. * * \note * Depending on the circumstances, messages may be successfully * read but left unhandled (eg. because the message was incomplete). * In such cases, the message will be reinjected and \b false * returned, making it possible for a future call to this method * to handle the (full) message again. */ public function readMessage() { $logging = \Plop\Plop::getInstance(); // Initial state: expect the client's identification string. if (!isset($this->context['identity']['client'])) { return $this->handlers[256]->handle(null, $this->decoder, $this, $this->context); } $blockSize = max($this->decryptor->getBlockSize(), 8); // See http://api.libssh.org/rfc/PROTOCOL // for more information on EtM (Encrypt-then-MAC). if ($this->inMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) { $encPayload = $this->decoder->getBuffer()->get(4); if ($encPayload === null) { return false; } $unencrypted = $encPayload; } elseif ($this->decryptor instanceof \fpoirotte\Pssht\AEADInterface) { $encPayload = $this->decoder->getBuffer()->get(4); if ($encPayload === null) { return false; } $unencrypted = $this->decryptor->decrypt($this->inSeqNo, $encPayload); $this->decoder->getBuffer()->unget($encPayload); $encPayload = ''; } else { $encPayload = $this->decoder->getBuffer()->get($blockSize); if ($encPayload === null) { return false; } $unencrypted = $this->decryptor->decrypt($this->inSeqNo, $encPayload); } $buffer = new \fpoirotte\Pssht\Buffer($unencrypted); $decoder = new \fpoirotte\Pssht\Wire\Decoder($buffer); $packetLength = $decoder->decodeUint32(); // Read the rest of the message. if ($this->inMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) { // Only the main payload remains. $toRead = $packetLength; } elseif ($this->decryptor instanceof \fpoirotte\Pssht\AEADInterface) { // packet length (authenticated data) // + encrypted payload // + authentication tag (AT) $toRead = 4 + $packetLength + $this->decryptor->getSize(); } else { $toRead = 4 - $blockSize + $packetLength; } if ($toRead < 0) { throw new \RuntimeException(); } $unencrypted2 = ''; if ($toRead !== 0) { $encPayload2 = $this->decoder->getBuffer()->get($toRead); if ($encPayload2 === null) { $this->decoder->getBuffer()->unget($encPayload); return false; } $unencrypted2 = $this->decryptor->decrypt($this->inSeqNo, $encPayload2); if ($unencrypted2 === null) { return false; } $buffer->push($unencrypted2); } $paddingLength = ord($decoder->decodeBytes()); $payload = $decoder->decodeBytes($packetLength - $paddingLength - 1); $padding = $decoder->decodeBytes($paddingLength); // If a MAC is in use. $macSize = $this->inMAC->getSize(); $actualMAC = ''; if ($macSize > 0) { $actualMAC = $this->decoder->getBuffer()->get($macSize); if ($actualMAC === null) { $this->decoder->getBuffer()->unget($encPayload2)->unget($encPayload); return false; } if ($this->inMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) { // $encPayload actually contains packet length (in plaintext). $macData = $encPayload . $encPayload2; } else { $macData = $unencrypted . $unencrypted2; } $expectedMAC = $this->inMAC->compute($this->inSeqNo, (string) substr($macData, 0, $packetLength + 4)); if ($expectedMAC !== $actualMAC) { throw new \RuntimeException(); } } if (!isset($packetLength, $paddingLength, $payload, $padding, $actualMAC)) { $this->decoder->getBuffer()->unget($actualMAC)->unget($encPayload2)->unget($encPayload); $logging->error('Something went wrong during decoding'); return false; } $payload = $this->uncompressor->update($payload); $decoder = new \fpoirotte\Pssht\Wire\Decoder(new \fpoirotte\Pssht\Buffer($payload)); $msgType = ord($decoder->decodeBytes(1)); $logging->debug('Received payload: %s', array(\escape($payload))); $res = true; if (isset($this->handlers[$msgType])) { $handler = $this->handlers[$msgType]; $logging->debug('Calling %(handler)s with message type #%(msgType)d', array('handler' => get_class($handler) . '::handle', 'msgType' => $msgType)); try { $res = $handler->handle($msgType, $decoder, $this, $this->context); } catch (\fpoirotte\Pssht\Messages\DISCONNECT $e) { if ($e->getCode() !== 0) { $this->writeMessage($e); } throw $e; } } else { $logging->warn('Unimplemented message type (%d)', array($msgType)); $response = new \fpoirotte\Pssht\Messages\UNIMPLEMENTED($this->inSeqNo); $this->writeMessage($response); } $this->inSeqNo = ++$this->inSeqNo & 0xffffffff; return $res; }
protected static function unserializeSub(\fpoirotte\Pssht\Wire\Decoder $decoder) { $algorithm = $decoder->decodeString(); $res = array($algorithm, $decoder->decodeString(), $decoder->decodeString(), $decoder->decodeString()); // Special handling for signature. $decoder2 = new \fpoirotte\Pssht\Wire\Decoder(new \fpoirotte\Pssht\Buffer($decoder->decodeString())); if ($decoder2->decodeString() !== $algorithm) { throw new \InvalidArgumentException(); } $res[] = $decoder2->decodeString(); return $res; }