private static function unprotectWithKeyPair()

in web_ui/src/applications/bistro/misc/BistroCurlProtection.php [241:319]


  private static function unprotectWithKeyPair($key_pair, $base64_input) {
    $keys = self::preprocessKeyPair($key_pair);
    if ($keys === null) {
      return null;  // Debug log was done above
    }
    list($encryption_key, $mac_key) = $keys;

    $input = self::base64URIDecode($base64_input);
    if (strlen($input) < 2) {  // Also fails on null, false
      self::debugLog('Input too short');
      return null;
    }

    $i1 = 1;  // version byte
    if (substr($input, 0, $i1) !== pack('C', self::PROTECTION_VERSION)) {
      self::debugLog('Bad version byte');
      return null;
    }

    $i2 = $i1 + self::MAC_SIZE;
    $mac = substr($input, $i1, self::MAC_SIZE);
    if (strlen($mac) !== self::MAC_SIZE) {
      self::debugLog('Bad MAC size');
      return null;
    }

    $i3 = $i2 + self::ENCRYPTION_IV_SIZE;
    $iv = substr($input, $i2, self::ENCRYPTION_IV_SIZE);
    if (strlen($iv) !== self::ENCRYPTION_IV_SIZE) {
      self::debugLog('Bad IV size');
      return null;
    }

    $cipher_text = substr($input, $i3);
    $len = strlen($cipher_text);
    if ($len < 1 || $len % self::ENCRYPTION_BLOCK_SIZE !== 0) {
      self::debugLog('Bad ciphertext size');
      return null;
    }

    $expected_mac = hash_hmac(
      self::MAC_ALGORITHM,
      $iv.$cipher_text,
      $mac_key,
      /* raw output = */ true
    );
    if (!self::areMACsEqual($mac, $expected_mac)) {
      self::debugLog('Invalid MAC');
      return null;
    }

    $plain_text_with_length = mcrypt_decrypt(
      self::ENCRYPTION_ALGORITHM,
      $encryption_key,
      $cipher_text,
      self::ENCRYPTION_MODE,
      $iv
    );
    if (strlen($plain_text_with_length) < 2) {  // Also fails on null, false
      self::debugLog('Plaintext too short to contain length prefix');
      return null;
    }

    list($length, $padded_text) = explode(':', $plain_text_with_length, 2);
    if ($length !== strval(intval($length))) {
      self::debugLog('Plaintext length is not an integer');
      return null;
    }
    $length = intval($length);

    $padded_len = strlen($padded_text);
    if ($padded_len < $length ||
        $padded_len > $length + self::ENCRYPTION_BLOCK_SIZE) {
      self::debugLog('Padded length does not match plaintext length');
      return null;
    }

    return substr($padded_text, 0, $length);
  }