Exemplo n.º 1
0
 function __construct(Alert $alert, string $message)
 {
     $this->output = null;
     $this->alert = $alert;
     $message = $this->alert->toString() . " " . $message;
     parent::__construct($message, $alert->getDescCode());
 }
Exemplo n.º 2
0
 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);
 }
Exemplo n.º 3
0
 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;
 }
Exemplo n.º 4
0
 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");
     }
 }
Exemplo n.º 5
0
 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");
         }
     }
 }
Exemplo n.º 6
0
 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;
 }
Exemplo n.º 7
0
 /**
  * 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);
 }
Exemplo n.º 8
0
 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'];
 }
Exemplo n.º 9
0
 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));
     }
 }
Exemplo n.º 10
0
 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;
 }
Exemplo n.º 11
0
 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);
 }
Exemplo n.º 12
0
 /**
  * @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();
 }
Exemplo n.º 13
0
 /**
  * @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();
 }
Exemplo n.º 14
0
 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);
     }
 }
Exemplo n.º 15
0
 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];
 }
Exemplo n.º 16
0
 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
 }
Exemplo n.º 17
0
 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");
     }
 }