function __construct(Alert $alert, string $message) { $this->output = null; $this->alert = $alert; $message = $this->alert->toString() . " " . $message; parent::__construct($message, $alert->getDescCode()); }
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); }
private static function bitLen($password) { // 128(16), 192(24) or 256(32) $l = strlen($password); if ($l != 16 && $l != 24 && $l != 32) { throw new TLSAlertException(Alert::create(Alert::INTERNAL_ERROR), "Invalid gcm key length: {$l}"); } return $l * 8; }
public function encode($data) { $core = $this->core; $data = $this->encodeHeader($data); if ($core->isServer) { throw new TLSAlertException(Alert::create(Alert::UNEXPECTED_MESSAGE), "Server received Hello Request"); } else { // We don't re-negotiate throw new TLSAlertException(Alert::create(Alert::NO_RENEGOTIATION), "No renegotiation"); } }
public function encodeRecord($data) { while (!is_null($data) && strlen($data) > 0) { $strlen = strlen($data); $record = $this->getRecord(); $record->encode($data); $data = $record->get('dataRest'); if ($strlen == strlen($data)) { throw new TLSAlertException(Alert::create(Alert::INTERNAL_ERROR), "Failed on encodeRecord"); } } }
public function encodeHeader($data) { // https://tools.ietf.org/html/rfc5246#section-7.4 $this->msgType = $msgType = Core::_unpack('C', $data[0]); $this->length = $length = Core::_unpack('N', $data[1] . $data[2] . $data[3] . 0x0) >> 8; $data = substr($data, 4, $length); $this->payload = $data; if ($this->length != strlen($data)) { throw new TLSAlertException(Alert::create(Alert::ILLEGAL_PARAMETER), "Invalid Handshake payload: " . $this->length); } return $data; }
/** * 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 __construct(array $arr) { $cipherID = $arr[0] << 8 | $arr[1]; if (!array_key_exists($cipherID, self::$cipherList)) { throw new TLSAlertException(Alert::create(Alert::INTERNAL_ERROR), "Failed to initiate CipherSuite. cipherID: {$cipherID}"); } $cipherSuite = self::$cipherList[$cipherID]; $this->cipherID = $cipherID; $this->cipherType = $cipherSuite['cipher_type']; $this->ivLen = $cipherSuite['iv_len']; $this->keyLen = $cipherSuite['key_len']; $this->macLen = $cipherSuite['mac_len']; $this->cryptoMethod = $cipherSuite['crypto_method']; $this->macAlgorithm = $cipherSuite['mac']; }
public function encode($data) { $core = $this->core; $data = $this->encodeHeader($data); /* * https://tools.ietf.org/html/rfc5246#section-7.4.9 * * Note that this * representation has the same encoding as with previous versions. * Future cipher suites MAY specify other lengths but such length * MUST be at least 12 bytes. */ $this->verifyData = substr($data, 0, $this->length); // Get all handshakeMessages excluding this message $handshakeMessages = $core->getHandshakeMessages(1); // Get verify data $verifyData = $this->getVerifyData($core->isServer ^ true, $handshakeMessages); if ($this->verifyData != $verifyData) { throw new TLSAlertException(Alert::create(Alert::BAD_RECORD_MAC), "Handshake Finished: verifyData mismatched:" . base64_encode($this->verifyData) . "<=>" . base64_encode($verifyData)); } }
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); }
/** * @Override */ public function decode() { $conn = $this->conn; $core = $conn->getCore(); $cipherSuite = $core->cipherSuite; $sharedKey = $conn->Key; $ivLen = $cipherSuite->getIVLen(); $macLen = $cipherSuite->getMACLen(); $MAC = $this->calculateMAC(); $IV = Core::getRandom($ivLen); $data = $this->payload . $MAC; // Calculate and append padding $fpd = function ($l, $bz) { return $l + $bz - $l % $bz - $l; }; $paddingLength = $fpd(strlen($this->payload . $MAC) + 1, $ivLen); $data .= Core::_pack('C', $paddingLength); $encData = $cipherSuite->blockEncrypt($data, $sharedKey, $IV); if (false === $encData) { throw new TLSAlertException(Alert::create(Alert::BAD_RECORD_MAC), "Cipher block encryption failed"); } $encData = $IV . $encData; $this->incrementSeq(); if ($this->contentType == ContentType::HANDSHAKE) { $core->countHandshakeMessages($this->payload); } $this->set('payload', $encData); return parent::decode(); }
/** * @Override */ public function decode() { $conn = $this->conn; $core = $conn->getCore(); $cipherSuite = $core->cipherSuite; $nonceImplicit = $conn->IV; // 4 bytes $sharedKey = $conn->Key; $aad = $this->getAAD($this->length); $nonceExplicit = $this->getSeq(); // 8 bytes /* * https://tools.ietf.org/html/rfc5288 page 2 * * struct { * opaque salt[4]; * opaque nonce_explicit[8]; * } GCMNonce; */ $nonce = $nonceImplicit . $nonceExplicit; $encData = $cipherSuite->gcmEncrypt($this->payload, $sharedKey, $nonce, $aad); if (false === $encData) { throw new TLSAlertException(Alert::create(Alert::BAD_RECORD_MAC), "Cipher gcm encryption failed"); } $this->incrementSeq(); if ($this->contentType == ContentType::HANDSHAKE) { $core->countHandshakeMessages($this->payload); } $payload = $nonceExplicit . $encData; $this->set('payload', $payload); return parent::decode(); }
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 static function _unpack($f, $d) { $r = unpack($f, $d); if (!is_array($r) || !isset($r[1])) { throw new TLSAlertException(Alert::create(Alert::INTERNAL_ERROR), "unpack failed. format: {$f}, data: {$d}"); } return $r[1]; }
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 encodeAlert($data) { $core = $this->core; $alert = new Alert(); $alert->encode($data); $this->content = $alert; $core->isClosed = true; if ($alert->getDescCode() != Alert::CLOSE_NOTIFY) { throw new TLSAlertException($alert, "Alert received from peer"); } }