public function encode($data) { $core = $this->core; $extensions = $core->extensions; // Client $connIn = $core->getInDuplex(); // Server $connOut = $core->getOutDuplex(); $data = $this->encodeHeader($data); // ECDHE if ($core->cipherSuite->isECDHEEnabled()) { $publicKeyLen = Core::_unpack('C', $data[0]); $publicKey = substr($data, 1, $publicKeyLen); $preMaster = $extensions->call('Curve', 'calculatePreMaster', null, $publicKey); } else { // https://tools.ietf.org/html/rfc5246#section-7.4.7.1 // Get a Premaster Secret $preMasterLen = Core::_unpack('n', $data[0] . $data[1]); $encPreMaster = substr($data, 2, $preMasterLen); $privateKey = $core->getConfig('private_key'); openssl_private_decrypt($encPreMaster, $preMaster, $privateKey); $vMajor = Core::_unpack('C', $preMaster[0]); $vMinor = Core::_unpack('C', $preMaster[1]); list($vMajor2, $vMinor2) = $core->getVersion(); if ($vMajor != $vMajor2 || $vMinor != $vMinor) { throw new TLSAlertException(Alert::create(Alert::BAD_RECORD_MAC), "Invalid protocol version in PreMaster {$vMajor} <=> {$vMajor2}, {$vMinor} <=> {$vMinor2}"); } } $this->setKeys($preMaster, $connOut, $connIn); }
public function encode($data) { $this->level = Core::_unpack('C', $data[0]); $this->descCode = Core::_unpack('C', $data[1]); // We got alert message from peer $this->fromPeer = true; }
public function onEncodeClientHello($type, $data) { $core = $this->core; if ($type != TLSExtensions::TYPE_SIGNATURE_ALGORITHM) { return; } $protoVersion = $core->getProtocolVersion(); /* * Note: this extension is not meaningful for TLS versions prior to 1.2. * Clients MUST NOT offer it if they are offering prior versions. * However, even if clients do offer it, the rules specified in [TLSEXT] * require servers to ignore extensions they do not understand. */ if ($protoVersion < 32) { return; } $length = Core::_unpack('n', $data[0] . $data[1]); $data = substr($data, 2); for ($i = 0; $i < $length; $i += 2) { $hash = Core::_unpack('C', $data[$i]); $sig = Core::_unpack('C', $data[$i + 1]); $algorithm = $hash << 8 | $sig; if (in_array($algorithm, self::$supportedAlgorithmList)) { $this->algorithm = $algorithm; break; } } }
protected function incrementSeq() { if (is_null($this->seq)) { $this->seq = $this->getZeroSeq(); } for ($i = 7; $i >= 0; $i--) { $num = Core::_unpack('C', $this->seq[$i]) + 1; $this->seq[$i] = Core::_pack('C', $num); if ($num % 256 > 0) { break; } } }
/** * Client Hello * https://tools.ietf.org/html/rfc5246#section-7.4.1.2 */ public function encode($data) { $core = $this->core; $connIn = $core->getInDuplex(); $data = $this->encodeHeader($data); // https://tools.ietf.org/html/rfc5246#section-7.4.1.2 $vMajor = $major = Core::_unpack('C', $data[0]); $vMinor = $minor = Core::_unpack('C', $data[1]); // Set TLS Version $core->setVersion($vMajor, $vMinor); // Get and set Client Random $random = substr($data, 2, 32); $connIn->random = $random; $sessionLength = Core::_unpack('C', $data[34]); $data = substr($data, 35); // SessionID if > 0 if ($sessionLength > 0) { $sessionID = substr($data, 0, $sessionLength); $core->setSessionID($sessionID); $data = substr($data, $sessionLength); } $cipherLength = Core::_unpack('n', $data[0] . $data[1]); $data = substr($data, 2); $cipherIDs = []; // https://github.com/pornin/TestSSLServer/blob/master/Src/CipherSuite.cs for ($i = 0; $i < $cipherLength; $i += 2) { // https://tools.ietf.org/html/rfc5246#section-7.4.1.2 $cipher1 = Core::_unpack('C', $data[$i]); $cipher2 = Core::_unpack('C', $data[$i + 1]); $cipherIDs[] = [$cipher1, $cipher2]; } $this->requestCipherIDs = $cipherIDs; $data = substr($data, $cipherLength); $compressionLength = Core::_unpack('C', $data[0]); $compressionMethod = Core::_unpack('C', $data[1]); if ($compressionMethod != 0x0) { throw new TLSAlertException(Alert::create(Alert::HANDSHAKE_FAILURE), "compressionMethod is not null"); } $core->setCompressionMethod($compressionMethod); // Extensions $extLength = Core::_unpack('n', $data[2] . $data[3]); $data = substr($data, 4, $extLength); $this->requestedExtensions = $extensions = $this->encodeExtensions($data); $core->extensions->onEncodeClientHello($extensions); $cipherID = CipherSuites::pickCipherID($core, $cipherIDs); if (is_null($cipherID)) { throw new TLSAlertException(Alert::create(Alert::INTERNAL_ERROR), "Cipher Suite not found"); } $core->cipherSuite = new CipherSuites($cipherID); }
public function encode($data) { $core = $this->core; $data = $this->encodeHeader($data); $crtsLength = Core::_unpack('N', $data[0] . $data[1] . $data[2] . 0x0) >> 8; $crtsData = substr($data, 3, $crtsLength); for ($i = 0; $i < $crtsLength;) { $crtLength = Core::_unpack('n', $crtsData[$i + 1] . $crtsData[$i + 2]); if (0 >= (int) $crtLength) { break; } $crtDers[] = substr($crtsData, $i + 3, $crtLength); $i += $crtLength + 3; } $core->setCrtDers($crtDers); }
/** * for Client Hello and Server Hello */ protected function encodeExtensions($data) { $extensions = []; for ($j = 0; $j < strlen($data);) { $extType = Core::_unpack('n', $data[$j] . $data[$j + 1]); $extDataLen = Core::_unpack('n', $data[$j + 2] . $data[$j + 3]); if (0 == $extDataLen) { $j += 2 + 2; continue; } $extData = substr($data, $j + 4, $extDataLen); $j += 2 + 2 + $extDataLen; $extensions[] = ['type' => $extType, 'data' => $extData]; } return $extensions; }
protected function encodeHeader($data) { $data = $this->encodeBuffer->flush() . $data; $this->contentType = Core::_unpack('C', $data[0]); $vMajor = Core::_unpack('C', $data[1]); $vMinor = Core::_unpack('C', $data[2]); $this->length = Core::_unpack('n', $data[3] . $data[4]); if ($this->length > $this->maxLength) { /* * A TLSCiphertext record was received that had a length more than * 2^14+2048 bytes, or a record decrypted to a TLSCompressed record * with more than 2^14+1024 bytes. */ throw new TLSAlertException(Alert::create(Alert::RECORD_OVERFLOW), "Exceed max length of payload: " . strlen($data)); } if ($this->length > strlen($data)) { $this->encodeBuffer->set($data); return false; } $this->payload = substr($data, 5, $this->length); $this->dataRest = substr($data, 5 + $this->length); return true; }
public function encode($data) { $core = $this->core; $connIn = $core->getInDuplex(); $data = $this->encodeHeader($data); $vMajor = Core::_unpack('C', $data[0]); $vMinor = Core::_unpack('C', $data[1]); // Server Random $random = substr($data, 2, 32); $connIn->random = $random; // Session ID $sessionLength = Core::_unpack('C', $data[34]); $data = substr($data, 35); // SessionID if > 0 if ($sessionLength > 0) { $sessionID = substr($data, 35, $sessionLength); $core->setSessionID($sessionID); $data = substr($data, $sessionLength); } $cipherID = [Core::_unpack('C', $data[0]), Core::_unpack('C', $data[1])]; $cipherSuite = new CipherSuites($cipherID); if (is_null($cipherSuite)) { throw new TLSAlertException(Alert::create(Alert::INTERNAL_ERROR), "cipherSuite is null"); } $core->cipherSuite = $cipherSuite; // Cipher Suite $core->setCompressionMethod(Core::_unpack('C', $data[2])); // Extensions if (strlen($data) < 5) { return; } $extLength = Core::_unpack('n', $data[3] . $data[4]); $data = substr($data, 5, $extLength); $this->requestedExtensions = $extensions = $this->encodeExtensions($data); $core->extensions->onEncodeServerHello($extensions); }
public function encodeServerKeyExchange($data) { // Must turn true when onEncodeServerHello is called //if( !$this->isUncompressed ) // return; $core = $this->core; $hs = HandShakeFactory::getInstance($core, HandshakeType::SERVER_KEY_EXCHANGE); $data = $hs->encodeHeader($data); $length = $hs->get('length'); $curveType = Core::_unpack('C', $data[0]); if ($curveType != 0x3) { throw new TLSAlertException(Alert::create(Alert::INTERNAL_ERROR), "Not named curve type: " + $curveType); } $namedCurveType = Core::_unpack('n', $data[1] . $data[2]); if (!EcDH::isSupported($namedCurveType)) { throw new TLSAlertException(Alert::create(Alert::INTERNAL_ERROR), "Unknow named curve: " + $namedCurveType); } $this->namedCurveType = $namedCurveType; $this->ecdh = new EcDH($this->namedCurveType); $publicKeyLen = Core::_unpack('C', $data[3]); $data = substr($data, 4); $publicKeyBin = substr($data, 0, $publicKeyLen); // Calculate and set premaster $this->calculatePremaster($publicKeyBin); // TODO verify signature }
public function encodeHandshake($payload) { $core = $this->core; // Incomming Record $recordIn = $core->getInDuplex()->getRecord(); // Outgoing Record $recordOut = $core->getOutDuplex()->getRecord(); // Buffer to send $bufferOut = $core->getBufferOut(); // Extension $extensions = $core->extensions; if ($core->isHandshaked) { throw new TLSAlertException(Alert::create(Alert::UNEXPECTED_MESSAGE), "Handshake message received after handshake is complete"); } /* * https://tools.ietf.org/html/rfc5246#section-7.4 * * Get Handshake Msg type */ $handshakeType = Core::_unpack('C', $payload[0]); if ($this->expectedHandshakeType != $handshakeType) { throw new TLSAlertException(Alert::create(Alert::UNEXPECTED_MESSAGE), "Unexpected handshake message: {$handshakeType} <=> " . $this->expectedHandshakeType); } $handshake = HandshakeFactory::getInstance($core, $handshakeType); $handshake->encode($payload); $this->content = $handshake; switch ($this->expectedHandshakeType) { case HandshakeType::SERVER_HELLO: $this->expectedHandshakeType = HandshakeType::CERTIFICATE; break; case HandshakeType::CERTIFICATE: if ($core->cipherSuite->isECDHEEnabled()) { $this->expectedHandshakeType = HandshakeType::SERVER_KEY_EXCHANGE; } else { $this->expectedHandshakeType = HandshakeType::SERVER_HELLO_DONE; } break; case HandshakeType::SERVER_KEY_EXCHANGE: $this->expectedHandshakeType = HandshakeType::SERVER_HELLO_DONE; break; case HandshakeType::SERVER_HELLO_DONE: // =========================================== // Send Client Key Exchange // =========================================== $clientKeyExchange = HandshakeFactory::getInstance($core, HandshakeType::CLIENT_KEY_EXCHANGE); $bufferOut->set($this->decodeContent($clientKeyExchange->decode(), ContentType::HANDSHAKE)); // =========================================== // Send Change Cipher Spec // =========================================== $changeCipherSpec = new ChangeCipherSpec(); $bufferOut->append($this->decodeContent($changeCipherSpec->decode(), ContentType::CHANGE_CIPHER_SPEC)); // Enable encryption $recordOut->cipherChanged(); // =========================================== // Send Client finished // =========================================== $clientFinished = HandShakeFactory::getInstance($core, HandshakeType::FINISHED); $bufferOut->append($this->decodeContent($clientFinished->decode(), ContentType::HANDSHAKE)); $this->expectedHandshakeType = HandshakeType::FINISHED; break; case HandshakeType::FINISHED: $core->isHandshaked = true; break; } if (strlen($payload) > $handshake->get('length')) { $payload = substr($payload, $handshake->get('length')); $this->encodeHandshake($payload); } }
public function encode($data) { $msg = Core::_unpack('C', $data[0]); }