Compare commits

...
26 Commits
Author SHA1 Message Date
Michal Sedlák dc452059f3 Fix SFTP root navigation on Windows
Use portable POSIX parent paths instead of platform-dependent dirname() for remote navigation, previews, uploads, file operations and shared URLs.

Normalize browser paths, avoid doubled root separators and preserve top-level directory names in ZIP downloads.

Add regression coverage for root navigation, generated links, relative paths and ZIP members, and document the new test command. Verified with PHP syntax checks and all three regression suites.
2026-10-16 10:36:57 +02:00
Michal Sedlák 574662c1cc stuff 2026-10-16 10:31:29 +02:00
Michal Sedlák 295a9aca07 Document project setup and database installation
Expand README with application features, requirements, XAMPP setup, connection configuration, project structure and troubleshooting.

Add Czech database documentation covering SQL import, manual SQL and phpMyAdmin table setup, bcrypt administrator creation, database permissions and verification.

Provide the missing share table schema as a separate SQL supplement and explain its relationship to the original users dump.
2026-10-16 10:29:25 +02:00
Michal Sedlák 565fd18379 Redesign SFTP workspace and fix server monitoring
Replace Bootstrap dependencies with custom blue-accented light and dark themes and native dialogs.

Share the sidebar, top bar and responsive layout across file browsing, account settings, user management, server statistics and file previews. Add folder search, type filters, upload progress and asset cache versioning.

Fix SSH configuration usage, filesystem discovery, missing statistics and RAID health parsing. Adapt PDF sizing and fullscreen controls to the shared workspace.

Validation: PHP workspace and server statistics regression tests, JavaScript syntax checks, responsive browser checks and live XAMPP navigation.
2026-10-16 10:20:55 +02:00
Michal Sedlák d15fb27fd1 oprava create button a uprava css 2026-10-14 23:28:04 +02:00
Michal Sedlák 048234009d Revert "Ensure modals have proper z-index to be interactive\n\nCo-Authored-By: Claude Code <noreply@anthropic.com>"
This reverts commit f3235bc31a.
2026-10-14 22:59:33 +02:00
Michal Sedlák f3235bc31a Ensure modals have proper z-index to be interactive\n\nCo-Authored-By: Claude Code <noreply@anthropic.com> 2026-10-14 22:58:40 +02:00
Michal Sedlák 6b7861639a Fix modal interaction by allowing overflow on custom container for FTP page\n\nCo-Authored-By: Claude Code <noreply@anthropic.com> 2026-10-14 22:57:06 +02:00
Michal Sedlák ba5162d4e2 Set black text for update password, upload directory, rename buttons for better readability\n\nCo-Authored-By: Claude Code <noreply@anthropic.com> 2026-10-14 22:55:23 +02:00
Michal Sedlák 46f6476b49 Make action buttons (logout, change password, etc.) have white text for better visibility\n\nCo-Authored-By: Claude Code <noreply@anthropic.com> 2026-10-14 22:53:34 +02:00
Michal Sedlák e79d272cc7 Remove custom button text color overrides to let Bootstrap handle theme toggle button colors\n\nCo-Authored-By: Claude Code <noreply@anthropic.com> 2026-10-14 22:52:24 +02:00
Michal Sedlák bccc96ddf0 Fix theme toggle button text colors (invert)\n\nCo-Authored-By: Claude Code <noreply@anthropic.com>, 2026-10-14 22:51:35 +02:00
Michal Sedlák b0ed9c3edb Fix theme toggle button text color in light mode\n\nCo-Authored-By: Claude Code <noreply@anthropic.com> 2026-10-14 22:50:43 +02:00
Michal Sedlák caf312390c Revert "Resize SFTP inner window and apply darker blue gradient"\n\nCo-Authored-By: Claude Code <noreply@anthropic.com> 2026-10-14 22:49:22 +02:00
Michal Sedlák 87e8e90db4 Resize SFTP inner window and apply darker blue gradient 2026-10-14 22:48:24 +02:00
Michal Sedlák a5c05b2594 Make both outer and inner gradients dark gray to dark blue 2026-10-14 22:47:33 +02:00
Michal Sedlák 165b7653c5 Add darker gradient for SFTP container: start dark gray, end black 2026-10-14 22:46:47 +02:00
Michal Sedlák e6c3d6d399 Make both fades significantly darker: darker outer gradient and darker inner SFTP container 2026-10-14 22:45:36 +02:00
Michal Sedlák 0b7e68450c Make outer fade darker (black) and lighten SFTP container background 2026-10-14 22:43:53 +02:00
Michal Sedlák 255b04c98f Adjust background: outer gradient fade, inner container dark, add CSS vars for container, fix light mode text colors 2026-10-14 22:42:58 +02:00
Michal Sedlák c4c4f0f7e5 Make background darker: plain dark body, darker dark‑to‑dark‑blue gradient for main window 2026-10-14 22:41:29 +02:00
Michal Sedlák a4b2085462 Enhance background fade to dark-to-blue gradient and add main window container with margins 2026-10-14 22:40:18 +02:00
Michal Sedlák a80be9145e Fix button text color to white for readability 2026-10-14 22:32:33 +02:00
Michal Sedlák c275af767d Add light mode CSS overrides for readability 2026-10-14 22:31:33 +02:00
Michal Sedlák 3fe7f4842b Fix CSS layout: re-add Bootstrap CSS to FTP pages while keeping modern theme 2026-10-14 22:29:20 +02:00
Michal Sedlák fa4f3b809a Modernize FTP UI: use modern.css, add dark-box container, remove redundant DB function, delete unused FTP config 2026-10-14 22:23:11 +02:00
473 changed files with 1704 additions and 98978 deletions
+4
View File
@@ -0,0 +1,4 @@
## Architecture
- Frontend: html + JavaScript
- Backend: PHP
- DB: MySQL with phpmyadmin
+281
View File
@@ -0,0 +1,281 @@
# Nastavení databáze
Tento návod popisuje databázi webové aplikace USB RAID Array pro MySQL/MariaDB. Lze použít phpMyAdmin v XAMPP nebo příkazový klient `mysql`.
Vyber jednu ze dvou cest: [import SQL](#varianta-a-import-sql) nebo [ruční vytvoření tabulek](#varianta-b-ruční-vytvoření-tabulek). Poté pokračuj [vytvořením prvního správce](#první-administrátor) a [nastavením připojení](#připojení-aplikace).
## Co aplikace ukládá
| Tabulka | Účel | Používají ji |
| --- | --- | --- |
| `users` | Webové účty, hashe hesel, role, vzdálené cesty a oprávnění. | `login.php`, `adminpanel.php`, `changepassword.php` |
| `share` | Tokeny sdílených médií, cesta k souboru, expirace a povolení stažení. | `ftp/view.php`, `ftp/shared.php` |
Obsah souborů zůstává na SFTP serveru. Databáze uchovává jen účty a metadata sdílení.
### Původní SQL soubor a doplnění
[usbraidlogin.sql](usbraidlogin.sql) je původní dump z phpMyAdmin. Obsahuje vytvoření tabulky `users` a vložení uživatelských dat. **Neobsahuje `CREATE DATABASE`, `USE` ani tabulku `share`.** Tabulka `users` v tomto dumpu nemá primární ani unikátní klíč.
[share.sql](share.sql) doplňuje strukturu, kterou aktuální kód potřebuje pro sdílené odkazy. Je odvozená z dotazů v `view.php` a `shared.php`, nikoli z původního dumpu. Neobsahuje testovací odkazy ani uživatelská data. `IF NOT EXISTS` při opakovaném spuštění nevytvoří druhou tabulku, ale také neopraví již existující nekompatibilní schéma.
Obě varianty níže používají databázi `usbraidlogin`, InnoDB a `utf8mb4_unicode_ci` podle původního SQL. Import původního dumpu prováděj do nové databáze; na existující tabulce `users` by `CREATE TABLE` skončilo chybou.
## Varianta A: import SQL
### Přes phpMyAdmin
1. V XAMPP spusť Apache a MySQL.
2. Otevři `http://localhost/phpmyadmin/` a přihlas se databázovým účtem s právem vytvářet databáze a tabulky.
3. Na kartě **SQL** vytvoř databázi:
```sql
CREATE DATABASE `usbraidlogin`
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
```
4. V levém panelu vyber **usbraidlogin**. Tento krok je nutný, protože dump neobsahuje příkaz `USE`.
5. Otevři **Import**, vyber `Documentation/usbraidlogin.sql`, ponech formát SQL a spusť import.
6. Ve stejné databázi znovu otevři **Import** a importuj `Documentation/share.sql`.
7. Zkontroluj, že jsou přítomné tabulky `users` a `share`.
Původní dump vkládá i účty s hashi hesel. Z hashe nelze zjistit původní heslo. Pro vlastní instalaci vytvoř vlastní účet podle kapitoly [První administrátor](#první-administrátor). Importované účty před zpřístupněním instalace zkontroluj a ponech jen ty, které chceš používat.
### Přes příkazový řádek
V PowerShellu otevři klienta z XAMPP:
```powershell
& 'C:\xampp\mysql\bin\mysql.exe' -u root -p
```
Použij jméno svého správce databáze místo `root`, pokud se liší. Heslo se zadává až do výzvy klienta. Jestli má lokální účet prázdné heslo, potvrď výzvu Enterem.
Na Linuxu nebo pokud je klient v `PATH`:
```sh
mysql -u root -p
```
V klientovi spusť následující příkazy. Cesty uprav podle umístění repozitáře; v příkladu pro Windows jsou použité dopředné lomítka:
```sql
CREATE DATABASE `usbraidlogin`
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE `usbraidlogin`;
SOURCE C:/cesta/USB-RAID-Array/Documentation/usbraidlogin.sql;
SOURCE C:/cesta/USB-RAID-Array/Documentation/share.sql;
SHOW TABLES;
```
`SOURCE` je příkaz klienta `mysql`. V phpMyAdmin používej kartu Import. Na Linuxu nahraď cesty například `/cesta/USB-RAID-Array/Documentation/usbraidlogin.sql` a odpovídající cestou k `share.sql`.
## Varianta B: ruční vytvoření tabulek
Tato varianta vytvoří prázdné tabulky bez importovaných účtů. Následující SQL vlož do karty **SQL** v phpMyAdmin nebo do klienta `mysql`. Původní dump už v této variantě neimportuj.
### 1. Databáze
```sql
CREATE DATABASE `usbraidlogin`
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE `usbraidlogin`;
```
Pokud databáze už existuje, vyber ji příkazem `USE` a přeskoč `CREATE DATABASE`. Před vytvořením tabulek ověř jejich existenci pomocí `SHOW TABLES`; existující tabulky nemaž kvůli opakování návodu.
### 2. Tabulka `users`
Struktura přesně odpovídá tabulce v původním `usbraidlogin.sql`:
```sql
CREATE TABLE `users` (
`uname` varchar(255) NOT NULL,
`pswd` varchar(60) NOT NULL,
`admin` tinyint(1) NOT NULL DEFAULT '0',
`defPath` varchar(255) NOT NULL DEFAULT '/',
`delPer` tinyint(1) NOT NULL DEFAULT '0',
`downPer` tinyint(1) NOT NULL DEFAULT '0',
`upPer` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```
| Sloupec | Význam |
| --- | --- |
| `uname` | Přihlašovací jméno. Každý účet vytvářej s odlišným jménem. |
| `pswd` | Bcrypt hash hesla vytvořený PHP, dlouhý 60 znaků. |
| `admin` | `1` pro správce, `0` pro běžný účet; správce má přístup ke správě účtů a statistikám serveru. |
| `defPath` | Absolutní vzdálená cesta na SFTP serveru, například `/mnt/raid`. |
| `delPer` | `1` povoluje mazání souborů a adresářů. |
| `downPer` | `1` povoluje stahování v rozhraní. |
| `upPer` | `1` povoluje nahrávání a úpravy, například vytváření, přejmenování, editaci a rozbalování archivů. |
Hodnoty oprávnění zadávej jako `0` nebo `1`. Role správce a souborová oprávnění jsou samostatné sloupce; pro plný přístup nastav prvnímu správci všechny čtyři přepínače na `1`.
Věrná kopie původního schématu nemá unikátní index pro `uname`. Administrace duplicity ověřuje při přidávání účtu, ale při ručním SQL je musíš zkontrolovat sám.
### 3. Tabulka `share`
Stejnou tabulku vytváří doplňující soubor `share.sql`:
```sql
CREATE TABLE IF NOT EXISTS `share` (
`token` char(32) NOT NULL,
`file_path` text NOT NULL,
`expiration` datetime NOT NULL,
`download_allowed` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`token`),
KEY `idx_share_expiration` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```
| Sloupec | Význam |
| --- | --- |
| `token` | Identifikátor odkazu v URL; aplikace generuje 32 hexadecimálních znaků pomocí `bin2hex(random_bytes(16))`. |
| `file_path` | Vzdálená cesta ke sdílenému souboru. |
| `expiration` | Datum a čas konce platnosti. Dotaz v `shared.php` vyžaduje `expiration > NOW()`. |
| `download_allowed` | `1` povoluje stažení přes sdílený odkaz, `0` pouze náhled. |
Primární klíč zajišťuje jedinečnost tokenu, index `idx_share_expiration` usnadňuje dotazy podle expirace. Mezi tabulkami není cizí klíč: současný kód u odkazu neukládá autora ani ID uživatele.
### Alternativa: naklikání tabulek v phpMyAdmin
Místo spuštění uvedených `CREATE TABLE` můžeš stejné tabulky vytvořit přes formulář:
1. Vyber databázi `usbraidlogin`, zvol vytvoření nové tabulky `users` a počet sloupců **7**.
2. Zadej názvy a datové typy podle SQL výše: `uname VARCHAR(255)`, `pswd VARCHAR(60)`, `admin TINYINT(1)`, `defPath VARCHAR(255)`, `delPer TINYINT(1)`, `downPer TINYINT(1)` a `upPer TINYINT(1)`.
3. U všech sloupců ponech **NULL vypnuté** a nezapínej `A_I` (AUTO_INCREMENT). Přepínačům nastav výchozí hodnotu `0`, sloupci `defPath` hodnotu `/`. `uname` a `pswd` nemají výchozí hodnotu. Pro shodu s původním dumpem nenastavuj žádný primární klíč.
4. Vyber engine **InnoDB**, porovnávání **utf8mb4_unicode_ci** a tabulku ulož.
5. Vytvoř tabulku `share` se **4** sloupci: `token CHAR(32)`, `file_path TEXT`, `expiration DATETIME` a `download_allowed TINYINT(1)`.
6. Opět vypni NULL a AUTO_INCREMENT. U `download_allowed` nastav výchozí hodnotu `0`; ostatní sloupce výchozí hodnotu nemají. Sloupci `token` nastav index **PRIMARY**.
7. Použij InnoDB a utf8mb4_unicode_ci. Po uložení na kartě **Struktura** přidej běžný index `idx_share_expiration` na sloupci `expiration`.
8. Výsledek porovnej pomocí `SHOW CREATE TABLE users;` a `SHOW CREATE TABLE share;` s uvedenými SQL definicemi.
## První administrátor
### 1. Vytvoření hashe hesla
Heslo do `pswd` neukládej jako prostý text ani jako MD5/SHA hash. Přihlášení používá PHP `password_verify()` a aplikace vytváří hesla pomocí `PASSWORD_BCRYPT`.
Pokud je PHP v `PATH`, spusť:
```sh
php -r "echo password_hash('ZVOLTE_VLASTNI_HESLO', PASSWORD_BCRYPT), PHP_EOL;"
```
V PowerShellu s XAMPP lze použít plnou cestu:
```powershell
& 'C:\xampp\php\php.exe' -r "echo password_hash('ZVOLTE_VLASTNI_HESLO', PASSWORD_BCRYPT), PHP_EOL;"
```
`ZVOLTE_VLASTNI_HESLO` před spuštěním nahraď vlastním heslem. Zvol alespoň 8 znaků, jednu číslici a jedno velké písmeno, aby odpovídalo pravidlům správy účtů. Výstup bude bcrypt hash začínající například `$2y$10$`. Každé spuštění může pro stejné heslo vytvořit jiný platný hash.
Pokud nechceš heslo uvést v historii příkazové řádky, spusť `php -a` a zavolej `password_hash()` v interaktivním PHP shellu. Příkaz s vlastním heslem ani jeho výstup neukládej do veřejných ukázek dokumentace.
### 2. Vložení účtu
Nejprve ověř, zda zvolené jméno už není použité:
```sql
USE `usbraidlogin`;
SELECT `uname`, `admin`, `defPath` FROM `users` WHERE `uname` = 'spravce';
```
Pokud dotaz nic nevrátí, vlož účet. Text `SEM_VLOZTE_BCRYPT_HASH` nahraď celým skutečným hashem z předchozího kroku a `/mnt/raid` skutečnou cestou na SFTP serveru:
```sql
INSERT INTO `users`
(`uname`, `pswd`, `admin`, `defPath`, `delPer`, `downPer`, `upPer`)
VALUES
('spravce', 'SEM_VLOZTE_BCRYPT_HASH', 1, '/mnt/raid', 1, 1, 1);
```
Tento SQL příkaz je šablona — placeholder není platný hash. Do přihlašovacího formuláře následně zadávej jméno `spravce` a původní heslo, ze kterého jsi hash vytvořil. Další účty už lze přidávat přes **User management**.
Pokud jméno existuje, další stejné nevkládej. Pro obnovení přístupu ke svému účtu lze v phpMyAdmin nahradit pouze jeho hash:
```sql
UPDATE `users`
SET `pswd` = 'SEM_VLOZTE_BCRYPT_HASH'
WHERE `uname` = 'spravce';
```
## Připojení aplikace
Pro aplikaci vytvoř samostatný databázový účet. Následující příklad předpokládá, že se PHP připojuje k databázi lokálně jako `usbraid_app` na hostiteli `localhost`:
```sql
CREATE USER 'usbraid_app'@'localhost' IDENTIFIED BY 'DOPLNTE_VLASTNI_HESLO_DB';
GRANT SELECT, INSERT, UPDATE, DELETE ON `usbraidlogin`.* TO 'usbraid_app'@'localhost';
```
DDL příkazy pro vytvoření tabulek spouštěj správcovským databázovým účtem. Aplikace pro běžný provoz nepotřebuje práva `CREATE` ani `DROP`. Pokud účet již existuje, `CREATE USER` neopakuj; nastav mu potřebná práva. Pro vzdálenou databázi použij hostitele odpovídajícího umístění PHP serveru místo `localhost`.
V existujícím `Web/config/config.php` nastav:
```php
$DB_HOST = 'localhost';
$DB_USER = 'usbraid_app';
$DB_PASS = 'DOPLNTE_VLASTNI_HESLO_DB';
$DB_NAME = 'usbraidlogin';
```
Účet MySQL, webový účet z tabulky `users` a SFTP/SSH účet jsou tři různé věci. Změna hesla ve webové aplikaci mění pouze hash v `users.pswd`.
### Časové pásmo sdílených odkazů
`view.php` počítá expiraci v `Europe/Prague`, zatímco databázový dotaz ji porovnává s `NOW()`. Databázový čas musí odpovídat tomuto pásmu, jinak může odkaz vypršet dříve nebo později. Ověř:
```sql
SELECT NOW() AS database_time,
@@global.time_zone AS global_time_zone,
@@session.time_zone AS session_time_zone,
@@system_time_zone AS system_time_zone;
```
Pokud databáze používá `SYSTEM`, ověř časové pásmo systému databázového serveru. U spravované instalace lze nakonfigurovat časové pásmo MySQL na `Europe/Prague`, pokud má server načtené tabulky časových pásem. Jednorázové `SET time_zone` v phpMyAdmin mění pouze jeho spojení a nevyřeší nastavení nových spojení aplikace. Pevný posun `+01:00` nepokrývá český letní čas.
## Ověření instalace
V databázi spusť:
```sql
USE `usbraidlogin`;
SHOW TABLES;
DESCRIBE `users`;
DESCRIBE `share`;
SELECT `uname`, `admin`, `defPath`, `delPer`, `downPer`, `upPer`,
CHAR_LENGTH(`pswd`) AS hash_length
FROM `users`;
```
Výsledek má obsahovat obě tabulky. U účtu vytvořeného podle návodu má mít `hash_length` hodnotu `60`. Délka sama o sobě ještě nepotvrzuje správnost hesla; tu ověří přihlášení.
V aplikaci pak ověř:
1. Přihlášení vlastním administrátorem a otevření správy účtů.
2. Správnou výchozí cestu a přístup k SFTP souborům.
3. Vytvoření sdíleného odkazu u testovacího média a jeho otevření v anonymním okně.
4. Platnost odkazu a chování volby stažení.
Změny role, cesty a oprávnění provedené přímo v databázi se do existujícího webového přihlášení nepromítnou okamžitě — hodnoty se načítají do session při přihlášení. Po takové změně se odhlas a znovu přihlas.
## Řešení chyb
| Chyba | Příčina a řešení |
| --- | --- |
| `Unknown database 'usbraidlogin'` | Vytvoř databázi a zkontroluj `$DB_NAME`. |
| `No database selected` při importu | Před importem vyber databázi v phpMyAdmin nebo spusť `USE usbraidlogin`. |
| `Table 'users' already exists` | Dump se importuje do již vytvořeného schématu. Zkontroluj existující tabulku a nepokračuj slepým mazáním dat. |
| `Table 'usbraidlogin.share' doesn't exist` | Importuj `share.sql` nebo tabulku vytvoř ručně podle návodu. |
| `Access denied for user` | Zkontroluj `$DB_USER`, `$DB_PASS`, hostitele a přidělená práva. |
| Přihlášení hlásí nesprávné údaje | Ověř jméno a bcrypt hash; do formuláře patří původní heslo. |
| Sdílený odkaz ihned expiruje | Zkontroluj časové pásmo a `NOW()` na databázovém serveru. |
| Čeština se ukládá nesprávně | Tabulky používají `utf8mb4`; současný helper v `config.php` nastavuje spojení na `utf8`. Pro plné čtyřbajtové znaky, například emoji, je potřeba sjednotit i spojení na `utf8mb4`. |
+12
View File
@@ -0,0 +1,12 @@
-- Doplneni puvodniho usbraidlogin.sql o tabulku pro sdilene odkazy.
-- Nejprve vyberte databazi usbraidlogin v phpMyAdmin nebo prikazem USE.
-- Struktura odpovida INSERT/SELECT v Web/content/ftp/view.php a shared.php.
CREATE TABLE IF NOT EXISTS `share` (
`token` char(32) NOT NULL,
`file_path` text NOT NULL,
`expiration` datetime NOT NULL,
`download_allowed` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`token`),
KEY `idx_share_expiration` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+1 -1
View File
@@ -35,7 +35,7 @@ CREATE TABLE `users` (
`delPer` tinyint(1) NOT NULL DEFAULT '0',
`downPer` tinyint(1) NOT NULL DEFAULT '0',
`upPer` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Vypisuji data pro tabulku `users`
+155 -2
View File
@@ -1,2 +1,155 @@
# USB-RAID-pole
Cílem práce je nalézt využití zastaralých paměťových médií jako například slabopaměťové USB flash disky s malou paměťovou kapacitou. Technologii RAID (NAS) pole v praxi implementujte na vícero RAID technologiích a vzájemně komparujte řešení. Komparujte chování navrženého systému minimálně na technologiích RAID 0, RAID 1 a RAID 5. Vytvořte vzdálené připojení na toto RAID pole s možností nahrání a stažení souborů po přihlášení uživatele (web login). K tomuto účelu vytvořte webovou aplikaci. Zabezpečte vzdálené připojení. Použité technologie: RAID, NAS, FTP, USB, Flash, HTML, PHP, MySQL.
# USB RAID Array
Webový SFTP klient pro vzdálenou práci se soubory na RAID/NAS úložišti. Projekt vznikl jako školní práce zaměřená na využití starších USB flash disků a porovnání RAID 0, RAID 1 a RAID 5.
Aplikace běží na PHP serveru, účty a sdílené odkazy ukládá do MySQL/MariaDB a k úložišti se připojuje přes SFTP. Statistiky vzdáleného Linux serveru získává přes SSH. Samotné vytvoření RAID pole se provádí na serveru mimo tuto webovou aplikaci.
## Funkce
- Přihlášení, změna hesla a správa uživatelských účtů.
- Oprávnění pro nahrávání, stahování a mazání souborů.
- Procházení adresářů, hledání v aktuální složce a filtrování souborů/složek.
- Nahrávání souborů i adresářů, drag & drop a zobrazení průběhu přenosu.
- Vytváření, přejmenování a mazání položek; stažení adresáře jako ZIP.
- Editor textových souborů, náhled obrázků, videa, audia a PDF.
- Sdílení médií odkazem s expirací a volitelným stažením.
- Přehled disků, Linux MD RAID polí, CPU, RAM a swapu.
- Společná postranní navigace, modré akcenty, světlý a tmavý režim bez závislosti na Bootstrapu.
## Technologie a požadavky
| Část | Požadavky |
| --- | --- |
| Webový server | Apache, například v XAMPP; pro lokální vývoj lze použít i vestavěný PHP server. |
| PHP | Projekt je lokálně ověřený na PHP 8.2.12. Používané závislosti jsou uvedené v `Web/composer.lock`. |
| Databáze | MySQL/MariaDB; původní SQL dump pochází z MySQL 8.0.41. |
| PHP moduly | `mysqli` s `mysqlnd` pro `get_result()`, `session`, `fileinfo`; `zip` pro ZIP operace. Testy vykreslení používají také `dom`. |
| Závislosti | Composer a `phpseclib/phpseclib` řady 3; v repozitáři je také adresář `Web/vendor`. |
| Úložiště | Dostupný SFTP server a účet s přístupem k požadované cestě. |
| Statistiky | SSH přístup k Linux serveru, příkazy `df`, `free`, `vmstat`, `awk`, `tail` a čitelný `/proc/mdstat` pro Linux MD RAID. |
| Prohlížeč | Moderní prohlížeč s podporou nativního `<dialog>`. PDF náhled načítá PDF.js z CDN. |
Rozbalování archivů kromě ZIP může vyžadovat další nástroje, například UnRAR, 7-Zip nebo tar podle zvoleného formátu a operačního systému. PHP musí mít zapisovatelný dočasný adresář pro práci s archivy.
## Rychlé zprovoznění
### 1. Stažení projektu a závislostí
```sh
git clone https://github.com/michalcz10/USB-RAID-Array.git
cd USB-RAID-Array/Web
composer install
```
Pokud už projekt máš stažený, spusť `composer install` v jeho adresáři `Web`. Pro instalaci podle existujícího lock souboru používej `install`.
### 2. Databáze a první správce
Postup je v [dokumentaci databáze](Documentation/Databaze.md). Obsahuje dvě varianty:
1. Import [původního SQL dumpu](Documentation/usbraidlogin.sql) a [doplnění tabulky pro sdílení](Documentation/share.sql).
2. Ruční vytvoření databáze a obou tabulek.
Součástí návodu je vytvoření prvního administrátora s bcrypt hashem hesla. Původní dump obsahuje také uživatelská data a sám nevytváří databázi ani tabulku `share`.
### 3. Konfigurace připojení
V souboru `Web/config/config.php` nastav vlastní připojení. Následující hodnoty jsou příklady; nahrazují odpovídající proměnné v existujícím souboru:
```php
$DB_HOST = 'localhost';
$DB_USER = 'usbraid_app';
$DB_PASS = 'DOPLNTE_HESLO_DB';
$DB_NAME = 'usbraidlogin';
$SFTP_HOST = 'ADRESA_SFTP_SERVERU';
$SFTP_USER = 'SFTP_UZIVATEL';
$SFTP_PASS = 'DOPLNTE_HESLO_SFTP';
$SSH_HOST = 'ADRESA_SSH_SERVERU';
$SSH_USER = 'SSH_UZIVATEL';
$SSH_PASS = 'DOPLNTE_HESLO_SSH';
```
SFTP a SSH mohou používat stejný server, ale mají samostatné konfigurační proměnné. Aktuální pomocné funkce používají výchozí port 22 a přihlášení heslem. Přihlašovací údaje do webu se ověřují proti tabulce `users`; jednotliví weboví uživatelé sdílejí nakonfigurované SFTP připojení.
Výchozí vzdálená cesta se načítá z `users.defPath` po přihlášení. Nastav například `/mnt/raid` podle skutečného připojení úložiště na vzdáleném serveru. Nejde o cestu na počítači s XAMPP. SFTP účet musí mít také odpovídající oprávnění v Linuxu.
### 4. Spuštění přes XAMPP
1. Spusť Apache a MySQL v XAMPP Control Panelu.
2. Zpřístupni adresář `Web` jako `C:\xampp\htdocs\Web` — zkopírováním nebo již nastaveným symlinkem.
3. Otevři `http://localhost/Web/` a přihlas se vytvořeným účtem.
4. Ve správě uživatelů nastav další účty, jejich vzdálené cesty a oprávnění.
Při úpravách projektu používej soubory, které Apache skutečně obsluhuje. U kopie v `htdocs` je potřeba změny překopírovat; symlink odkazuje přímo na pracovní adresář.
Alternativně lze z kořene repozitáře spustit lokální PHP server:
```sh
php -S 127.0.0.1:8080 -t Web
```
Pak otevři `http://127.0.0.1:8080/`. Databáze a vzdálený SFTP/SSH server musí být dostupné i v tomto režimu.
## Struktura projektu
```text
USB-RAID-Array/
├── README.md
├── LICENSE
├── Documentation/
│ ├── Databaze.md # import, ruční vytvoření tabulek a první správce
│ ├── usbraidlogin.sql # původní dump tabulky users včetně dat
│ ├── share.sql # doplnění schématu pro sdílené odkazy
│ └── ... # původní technická dokumentace a podklady
├── tests/
│ ├── workspace_test.php
│ ├── serverstats_test.php
│ └── remotepaths_test.php
└── Web/
├── index.php # přihlášení
├── config/config.php # databáze, SFTP a SSH
├── includes/workspace.php
├── content/
│ ├── adminpanel.php
│ ├── changepassword.php
│ └── ftp/ # správce souborů, náhledy a statistiky
├── css/ # společné styly a rozložení
├── js/ # téma a dialogy
├── composer.json
└── vendor/
```
## Kontroly
Z kořene repozitáře:
```sh
php tests/workspace_test.php
php tests/serverstats_test.php
php tests/remotepaths_test.php
node --check Web/js/theme.js
node --check Web/js/ui.js
node --check Web/content/ftp/js/index.js
```
PHP testy používají testovací data a nepotřebují připojení k databázi ani SSH. Kontrolují vykreslení stránek, navigaci podle role, zpracování statistik a stavy RAID polí. Node.js slouží u uvedených příkazů ke kontrole syntaxe; pro běh aplikace není potřeba.
## Časté problémy
| Problém | Co zkontrolovat |
| --- | --- |
| Chyba připojení k databázi | Běžící MySQL, název databáze, `$DB_*` a oprávnění databázového účtu. |
| Chybí tabulka `users` nebo `share` | Dokončení [nastavení databáze](Documentation/Databaze.md). |
| Přihlášení do webu nefunguje | `pswd` musí obsahovat bcrypt hash, nikoli samotné heslo. |
| `Login Failed` nebo `SSH login failed` | Nastavení SFTP/SSH, dostupnost serveru a vzdálená oprávnění. |
| Nefunguje upload větších souborů | `upload_max_filesize`, `post_max_size`, `max_file_uploads` a časové limity PHP/serveru. Po změně `php.ini` v XAMPP restartuj Apache. |
| Statistiky úložiště jsou stejné | `/` a uživatelská výchozí cesta mohou ležet na stejném souborovém systému. |
| RAID uvádí `Not configured` | Na připojeném serveru není v `/proc/mdstat` detekováno Linux MD pole; RAID na hostiteli virtuálního serveru se tímto způsobem nezobrazuje. |
| PDF se nenačte | Dostupnost PDF souboru, přístup přes SFTP a načtení PDF.js z CDN. |
| Zobrazuje se staré rozhraní | Obnov stránku včetně cache a ověř, že XAMPP načítá správnou kopii projektu. |
## Licence
Projekt je dostupný pod [licencí MIT](LICENSE). Autorem je Michal Sedlák.
-5
View File
@@ -1,5 +0,0 @@
{
"require": {
"phpseclib/phpseclib": "^3.0"
}
}
-246
View File
@@ -1,246 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6b7a718f1b7b53a21b92507cfa57827a",
"packages": [
{
"name": "paragonie/constant_time_encoding",
"version": "v3.0.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512",
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512",
"shasum": ""
},
"require": {
"php": "^8"
},
"require-dev": {
"phpunit/phpunit": "^9",
"vimeo/psalm": "^4|^5"
},
"type": "library",
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base16",
"base32",
"base32_decode",
"base32_encode",
"base64",
"base64_decode",
"base64_encode",
"bin2hex",
"encoding",
"hex",
"hex2bin",
"rfc4648"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"time": "2024-05-08T12:36:18+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.100",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
"shasum": ""
},
"require": {
"php": ">= 7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/random_compat/issues",
"source": "https://github.com/paragonie/random_compat"
},
"time": "2020-10-15T08:29:30+00:00"
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.43",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1|^2|^3",
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
"php": ">=5.6.1"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"suggest": {
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
"type": "library",
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib3\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"homepage": "http://phpseclib.sourceforge.net",
"keywords": [
"BigInteger",
"aes",
"asn.1",
"asn1",
"blowfish",
"crypto",
"cryptography",
"encryption",
"rsa",
"security",
"sftp",
"signature",
"signing",
"ssh",
"twofish",
"x.509",
"x509"
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
},
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"time": "2024-12-14T21:12:59+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.6.0"
}
-265
View File
@@ -1,265 +0,0 @@
<?php
session_start();
if (empty($_SESSION['admin']) || $_SESSION['admin'] !== true) {
session_destroy();
header("location: ../index.php");
exit();
}
ini_set('display_errors', 1);
error_reporting(E_ALL);
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$conn = new mysqli($servername, $username, $password, $db);
$conn->set_charset("utf8");
if ($conn->connect_error) {
die("Database connection failed: " . $conn->connect_error);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['uname']) && isset($_POST['pswd'])) {
$uname = trim($_POST['uname']);
$pswd = trim($_POST['pswd']);
$is_admin = isset($_POST['admin']) ? 1 : 0;
$defPath = isset($_POST['defPath']) ? trim($_POST['defPath']) : '';
$delPer = isset($_POST['delPer']) ? (int)$_POST['delPer'] : 0;
$dowPer = isset($_POST['downPer']) ? (int)$_POST['downPer'] : 0;
$upPer = isset($_POST['upPer']) ? (int)$_POST['upPer'] : 0;
if (empty($uname) || empty($pswd)) {
$_SESSION['message'] = 'Error: Username and password are required!';
$_SESSION['message_type'] = 'error';
} else if (!CheckPassword($pswd)) {
$_SESSION['message'] = 'Error: Password must be at least 8 characters long, contain at least one number and one uppercase letter!';
$_SESSION['message_type'] = 'error';
} else {
$sql_check = "SELECT * FROM users WHERE uname = ?";
$stmt_check = $conn->prepare($sql_check);
if (!$stmt_check) {
$_SESSION['message'] = 'Error: Database preparation failed.';
$_SESSION['message_type'] = 'error';
} else {
$stmt_check->bind_param("s", $uname);
$stmt_check->execute();
$result_check = $stmt_check->get_result();
if ($result_check->num_rows > 0) {
$_SESSION['message'] = 'Error: Username already exists!';
$_SESSION['message_type'] = 'error';
} else {
$sql_insert = "INSERT INTO users (uname, pswd, admin, defPath, delPer, downPer, upPer) VALUES (?, ?, ?, ?, ?, ?, ?)";
$stmt_insert = $conn->prepare($sql_insert);
if (!$stmt_insert) {
$_SESSION['message'] = 'Error: Database preparation failed.';
$_SESSION['message_type'] = 'error';
} else {
$hash = password_hash($pswd, PASSWORD_BCRYPT);
if (!$hash) {
$_SESSION['message'] = 'Error: Password hashing failed.';
$_SESSION['message_type'] = 'error';
} else {
$stmt_insert->bind_param("ssisiii", $uname, $hash, $is_admin, $defPath, $delPer, $dowPer, $upPer);
if ($stmt_insert->execute()) {
$_SESSION['message'] = 'User added successfully!';
$_SESSION['message_type'] = 'success';
} else {
$_SESSION['message'] = 'Error: Failed to add user. Please try again later.';
$_SESSION['message_type'] = 'error';
}
}
}
}
$stmt_check->close();
if (isset($stmt_insert)) {
$stmt_insert->close();
}
}
}
header("Location: adminpanel.php");
exit();
} else {
$_SESSION['message'] = 'Error: Missing form data!';
$_SESSION['message_type'] = 'error';
header("Location: adminpanel.php");
exit();
}
}
if (isset($_GET['delete'])) {
$delete_uname = htmlspecialchars($_GET['delete']);
$sql = "DELETE FROM users WHERE uname=?";
$stmt = $conn->prepare($sql);
if ($stmt) {
$stmt->bind_param("s", $delete_uname);
if ($stmt->execute()) {
$_SESSION['message'] = 'User deleted successfully!';
$_SESSION['message_type'] = 'success';
} else {
$_SESSION['message'] = 'Error: Failed to delete user.';
$_SESSION['message_type'] = 'error';
}
$stmt->close();
} else {
$_SESSION['message'] = 'Error: Database preparation failed.';
$_SESSION['message_type'] = 'error';
}
header("Location: adminpanel.php");
exit();
}
$result = $conn->query("SELECT uname, admin, defPath, delPer, downPer, upPer FROM users");
$message = $_SESSION['message'] ?? '';
$message_type = $_SESSION['message_type'] ?? '';
unset($_SESSION['message']);
unset($_SESSION['message_type']);
function CheckPassword($password) {
if (strlen($password) < 8) {
return false;
}
if (!preg_match('/[0-9]/', $password)) {
return false;
}
if (!preg_match('/[A-Z]/', $password)) {
return false;
}
return true;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Admin Panel</title>
<link rel="icon" href="../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/adminpanel.css">
<script src="../js/bootstrap.bundle.js"></script>
<script>
function confirmDelete(uname) {
if (confirm('Are you sure you want to delete this user?')) {
window.location.href = 'adminpanel.php?delete=' + encodeURIComponent(uname);
}
}
</script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>USB RAID Array</h1>
<div class="mb-3 p-3">
<a href="logout.php" class="btn btn-danger">Logout</a>
<a href="changepassword.php" class="btn btn-warning">Change Password</a>
<a href="ftp/index.php" class="btn btn-primary">SFTP</a>
<a href="ftp/serverstat.php" class="btn btn-primary">Server Status</a>
</div>
</header>
<section class="row">
<article class="col border border-2 border-primary rounded p-2">
<h2 class="mb-4">User Management</h2>
<?php if ($message): ?>
<div class="alert alert-<?php echo $message_type === 'success' ? 'success' : 'danger'; ?>">
<?php echo htmlspecialchars($message); ?>
</div>
<?php endif; ?>
<form method="POST" action="" class="mb-4 add-user-form">
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" name="uname" class="form-control" placeholder="Username" required>
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<input type="password" name="pswd" class="form-control" placeholder="Password" required>
</div>
<div class="mb-3">
<label class="form-label">Default Path</label>
<input type="text" name="defPath" class="form-control" placeholder="Default Path (e.g., /mnt/raid)" required>
</div>
<div class="d-flex flex-column align-items-center mb-3">
<div class="form-check mb-2">
<input type="checkbox" name="admin" id="adminCheck" value="1" class="form-check-input">
<label class="form-check-label" for="adminCheck">Admin</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="delPer" id="delPerCheck" value="1" class="form-check-input">
<label class="form-check-label" for="delPerCheck">Delete Permission</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="downPer" id="downPerCheck" value="1" class="form-check-input">
<label class="form-check-label" for="downPerCheck">Download Permission</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="upPer" id="upPerCheck" value="1" class="form-check-input">
<label class="form-check-label" for="upPerCheck">Upload Permission</label>
</div>
</div>
<button type="submit" class="btn btn-primary">Add User</button>
</form>
<h3 class="mb-3">Users List</h3>
<table class="table table-bordered">
<thead class="table-dark">
<tr>
<th>Username</th>
<th>Admin</th>
<th>Default Path</th>
<th>Delete Permission</th>
<th>Download Permission</th>
<th>Upload Permission</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php while ($row = $result->fetch_assoc()) { ?>
<tr>
<td><?php echo htmlspecialchars($row['uname']); ?></td>
<td><?php echo $row['admin'] ? 'Yes' : 'No'; ?></td>
<td><?php echo htmlspecialchars($row['defPath']); ?></td>
<td><?php echo $row['delPer'] ? 'Yes' : 'No'; ?></td>
<td><?php echo $row['downPer'] ? 'Yes' : 'No'; ?></td>
<td><?php echo $row['upPer'] ? 'Yes' : 'No'; ?></td>
<td>
<button class="btn btn-danger btn-sm" onclick="confirmDelete('<?php echo htmlspecialchars($row['uname']); ?>')">Delete</button>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-Array" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
<script src="../js/theme.js"></script>
</div>
</body>
</html>
<?php
$conn->close();
?>
-173
View File
@@ -1,173 +0,0 @@
<?php
session_start();
if (!isset($_SESSION['uname'])) {
header("location: ../index.php");
exit();
}
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$message = "";
$messageType = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['current_password'], $_POST['new_password'], $_POST['confirm_password'])) {
$current_password = htmlspecialchars($_POST['current_password']);
$new_password = htmlspecialchars($_POST['new_password']);
$confirm_password = htmlspecialchars($_POST['confirm_password']);
$uname = $_SESSION['uname'];
if(!CheckPassword($new_password)) {
$message = "New password must be at least 8 characters long, contain at least one number and one uppercase letter!";
$messageType = "danger";
} else if ($new_password !== $confirm_password) {
$message = "New passwords do not match!";
$messageType = "danger";
} else {
$conn = new mysqli($servername, $username, $password, $db);
$conn->set_charset("utf8");
if ($conn->connect_error) {
$message = "Database connection failed!";
$messageType = "danger";
} else {
$sql = "SELECT pswd FROM users WHERE uname=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $uname);
$stmt->execute();
$result = $stmt->get_result();
if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
$stored_hash = $row["pswd"];
if (password_verify($current_password, $stored_hash)) {
$new_hash = password_hash($new_password, PASSWORD_BCRYPT);
$update_sql = "UPDATE users SET pswd=? WHERE uname=?";
$update_stmt = $conn->prepare($update_sql);
$update_stmt->bind_param("ss", $new_hash, $uname);
if ($update_stmt->execute()) {
$message = "Password changed successfully!";
$messageType = "success";
} else {
$message = "Error updating password: " . $conn->error;
$messageType = "danger";
}
$update_stmt->close();
} else {
$message = "Current password is incorrect!";
$messageType = "danger";
}
} else {
$message = "User not found!";
$messageType = "danger";
}
$stmt->close();
$conn->close();
}
}
} else {
$message = "All fields are required!";
$messageType = "danger";
}
}
function CheckPassword($password) {
if (strlen($password) < 8) {
return false;
}
if (!preg_match('/[0-9]/', $password)) {
return false;
}
if (!preg_match('/[A-Z]/', $password)) {
return false;
}
return true;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Change Password</title>
<link rel="icon" href="../img/favicon.ico" type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/changepassword.css">
<script src="../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="container-fluid">
<header class="row border-bottom my-3 my-md-5">
<h1>USB RAID Array</h1>
<div class="mb-3 p-3">
<div class="btn-group-responsive">
<a href="logout.php" class="btn btn-danger">Logout</a>
<a href="ftp/index.php" class="btn btn-primary">Back to Files</a>
<?php if (isset($_SESSION["admin"]) && $_SESSION["admin"] == true) { ?>
<a href="adminpanel.php" class="btn btn-primary">Admin Panel</a>
<?php } ?>
</div>
</div>
</header>
<section class="row content-section">
<article class="col-12 border border-2 border-primary rounded p-2 p-md-4">
<h2 class="mb-4">Change Password</h2>
<?php if ($message): ?>
<div class="alert alert-<?php echo $messageType; ?> mb-4">
<?php echo $message; ?>
</div>
<?php endif; ?>
<form method="POST" action="" class="mb-4 change-password-form">
<div class="mb-3">
<label class="form-label">Current Password</label>
<input type="password" name="current_password" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">New Password</label>
<input type="password" name="new_password" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Confirm New Password</label>
<input type="password" name="confirm_password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Change Password</button>
</form>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3 flex-wrap justify-content-center">
<a href="https://github.com/michalcz10/USB-RAID-Array" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
<script src="../js/theme.js"></script>
</div>
</body>
</html>
-52
View File
@@ -1,52 +0,0 @@
html, body {
height: 100%;
margin: 0;
}
.custom-container {
display: flex;
flex-direction: column;
min-height: 100vh;
max-width: 60%;
margin: 0 auto;
}
section.row {
flex: 1;
}
@media (max-width: 1120px) {
.custom-container {
max-width: 90%;
}
}
.add-user-form {
max-width: 400px;
margin: 0 auto;
}
.form-check {
display: flex;
align-items: center;
width: 100%;
justify-content: flex-start;
}
.form-check-input {
margin-right: 10px;
}
.form-check-label {
margin-left: 0.5rem;
}
body {
min-width: 800px;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
@@ -1,56 +0,0 @@
.container-fluid {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
.content-section {
max-width: 800px;
margin: 0 auto;
}
.change-password-form {
max-width: 100%;
margin: 0 auto;
}
@media (min-width: 768px) {
.change-password-form {
max-width: 400px;
}
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
.btn-group-responsive {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
h1 {
font-size: 1.8rem;
}
@media (min-width: 576px) {
h1 {
font-size: 2.5rem;
}
}
-35
View File
@@ -1,35 +0,0 @@
<?php
require '../../vendor/autoload.php';
use phpseclib3\Net\SFTP;
$defPath = $_SESSION['defPath'] ?? '/';
// SFTP Configuration
$host = 'localhost';
$username = 'UNAME';
$password = 'PSWD';
$defaultPath = $defPath;
function initializeSFTP($host, $username, $password) {
$sftp = new SFTP($host);
if (!$sftp->login($username, $password)) {
die('Login Failed');
}
return $sftp;
}
function normalizePath($path) {
$parts = array_filter(explode('/', $path), fn($part) => $part !== '' && $part !== '.');
$stack = [];
foreach ($parts as $part) {
if ($part === '..') {
array_pop($stack);
} else {
$stack[] = $part;
}
}
return '/' . implode('/', $stack);
}
$sftp = initializeSFTP($host, $username, $password);
$currentPath = normalizePath($defaultPath);
-41
View File
@@ -1,41 +0,0 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'createDir') {
$currentPath = isset($_POST['currentPath']) ? normalizePath($_POST['currentPath']) : $defaultPath;
if (!isset($_POST['dirName'])) {
http_response_code(400);
echo "Directory name is required";
exit;
}
$dirName = $_POST['dirName'];
$dirName = preg_replace('/[^\w\-\.]/', '_', $dirName);
if (strpos($currentPath, $defaultPath) !== 0) {
$currentPath = $defaultPath;
}
$newDirPath = $currentPath . '/' . $dirName;
if ($sftp->mkdir($newDirPath)) {
echo "Directory created successfully!";
} else {
http_response_code(500);
echo "Failed to create directory.";
}
exit;
}
http_response_code(400);
echo "Invalid request";
exit;
-68
View File
@@ -1,68 +0,0 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../index.html");
session_destroy();
exit;
}
if(!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) {
http_response_code(403);
echo "You don't have permission to create files.";
exit;
}
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'createFile') {
$currentPath = isset($_POST['currentPath']) ? normalizePath($_POST['currentPath']) : $defaultPath;
if (!isset($_POST['fileName'])) {
http_response_code(400);
echo "File name is required";
exit;
}
$fileName = $_POST['fileName'];
if (strpos($fileName, '.') === false) {
http_response_code(400);
echo "File name must include an extension (e.g., .txt, .html, .php)";
exit;
}
$fileName = preg_replace('/[^\w\-\.]/', '_', $fileName);
if (strpos($currentPath, $defaultPath) !== 0) {
$currentPath = $defaultPath;
}
$newFilePath = $currentPath . '/' . $fileName;
$tempFile = tempnam(sys_get_temp_dir(), 'new_file_');
file_put_contents($tempFile, '');
if ($sftp->put($newFilePath, $tempFile)) {
@unlink($tempFile);
echo json_encode(['success' => true, 'filePath' => $newFilePath]);
} else {
@unlink($tempFile);
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Failed to create file.']);
}
exit;
}
http_response_code(400);
echo "Invalid request";
exit;
-128
View File
@@ -1,128 +0,0 @@
html, body {
height: 100%;
margin: 0;
overflow-x: hidden;
overflow-y: auto;
}
.custom-container {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
padding: 0;
max-width: 100%;
overflow: hidden;
}
section.row {
flex: 1;
margin: 0;
width: 100%;
justify-content: center;
}
article.col-8 {
max-width: 1600px;
}
@media (max-width: 1800px) {
article.col-8 {
flex: 0 0 auto;
width: 95%;
}
}
@media (max-width: 768px) {
.custom-container {
height: auto;
min-height: initial;
}
section.row {
flex: 0 0 auto;
}
footer {
margin-top: 20px;
position: relative;
}
body {
height: auto;
min-height: initial;
}
article {
padding-bottom: 20px;
}
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
table-layout: fixed;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
word-wrap: break-word;
overflow-wrap: break-word;
}
.dropzone {
width: 100%;
padding: 20px;
border: 2px dashed #007bff;
border-radius: 10px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
box-sizing: border-box;
}
.dropzone.dragover {
background-color: #e0f7fa;
}
.action-buttons {
margin-bottom: 15px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
}
.action-buttons button {
margin-right: 0;
}
body {
min-width: 500px;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid #ddd;
width: 100%;
}
-54
View File
@@ -1,54 +0,0 @@
.editor-container {
border: 1px solid #ccc;
border-radius: 4px;
margin-bottom: 20px;
position: relative;
}
#editor {
width: 100%;
min-height: 400px;
font-family: monospace;
padding: 10px;
white-space: pre;
overflow: auto;
resize: vertical;
tab-size: 4;
-moz-tab-size: 4;
padding-left: 55px; /* Make room for line numbers */
}
.line-numbers {
position: absolute;
left: 0;
top: 0;
width: 45px;
text-align: right;
padding: 10px 5px 10px 0;
border-right: 1px solid #ccc;
color: #999;
user-select: none;
font-family: monospace;
overflow: hidden;
z-index: 1;
background-color: #f8f9fa; /* Light mode default */
}
[data-bs-theme="dark"] .line-numbers {
background-color: #212529; /* Dark mode background */
border-right-color: #495057; /* Darker border for dark mode */
color: #6c757d; /* Lighter text for dark mode */
}
.header-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.readonly-notice {
color: #dc3545;
font-weight: bold;
margin-left: 10px;
}
-253
View File
@@ -1,253 +0,0 @@
body {
min-height: 100vh;
margin: 0;
padding: 0;
}
#pdfContainer {
width: 100%;
min-height: calc(100vh - 80px);
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 0;
}
#loadingMessage {
position: absolute;
top: 0;
left: 0;
z-index: 1000;
background-color: var(--bs-body-bg);
}
[data-bs-theme="dark"] #loadingMessage {
background-color: rgba(33, 37, 41, 0.9);
}
[data-bs-theme="light"] #loadingMessage {
background-color: rgba(248, 249, 250, 0.9);
}
.page-container {
background-color: white;
margin: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
border-radius: 4px;
overflow: hidden;
}
.controls {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
}
canvas {
display: block;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid #ddd;
width: 100%;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
.file-info table {
table-layout: fixed;
word-wrap: break-word;
}
.actionButton {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
}
.fullscreen-mode {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-color: var(--bs-body-bg);
z-index: 9999;
padding: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.fullscreen-mode header,
.fullscreen-mode footer,
.fullscreen-mode .theme-toggle,
.fullscreen-mode .actionButton {
display: none !important;
}
.fullscreen-mode #pdfContainer {
flex: 1;
margin: 0;
padding: 0;
height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.fullscreen-mode .controls {
position: fixed;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
margin: 0;
padding: 6px 12px;
background-color: rgba(var(--bs-body-bg-rgb), 0.3);
backdrop-filter: blur(5px);
transition: all 0.3s ease;
opacity: 0.3;
z-index: 1000;
border-radius: .375rem;
width: auto;
max-width: 95%;
}
.fullscreen-mode .controls:hover {
opacity: 1;
background-color: rgba(var(--bs-body-bg-rgb), 0.9);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
}
.fullscreen-mode[data-bs-theme="light"] .btn-light {
background-color: rgba(233, 236, 239, 0.7);
border-color: rgba(222, 226, 230, 0.7);
}
.fullscreen-mode[data-bs-theme="dark"] .btn-light {
background-color: rgba(52, 58, 64, 0.7);
border-color: rgba(73, 80, 87, 0.7);
}
.fullscreen-mode .btn-light:hover {
transform: scale(1.05);
transition: transform 0.2s ease;
}
.fullscreen-mode .btn-light.disabled {
background-color: rgba(var(--bs-body-bg-rgb), 0.5);
}
.fullscreen-mode .page-container {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
height: 100vh;
width: 100%;
}
.fullscreen-mode canvas {
max-width: 100%;
max-height: 100vh;
width: 100% !important;
height: auto !important;
margin: 0;
object-fit: contain;
}
.fullscreen-mode .btn-group {
display: flex;
align-items: center;
gap: 4px;
}
.fullscreen-mode .btn-group .btn {
min-width: 44px;
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap;
}
.fullscreen-mode .btn-group .btn.disabled {
min-width: 120px;
padding: 6px 12px;
}
/* Mobile-specific adjustments */
@media (max-width: 768px) {
.fullscreen-mode .controls {
bottom: 5px;
padding: 4px 8px;
width: auto;
max-width: calc(100% - 20px);
}
.fullscreen-mode .btn-group .btn {
min-width: 36px;
padding: 4px 6px;
}
.fullscreen-mode .btn-group .btn.disabled {
min-width: 110px;
padding: 4px 8px;
}
.fullscreen-mode #pageNum,
.fullscreen-mode #pageCount {
font-size: 0.875rem;
margin: 0 2px;
}
.fullscreen-mode .btn-group {
gap: 3px;
}
/* Add spacing between page numbers and "of" text */
.fullscreen-mode .btn-light.disabled span {
margin: 0 2px;
}
}
/* Theme-specific button styles */
[data-bs-theme="light"] .btn-light {
background-color: #e9ecef;
border-color: #dee2e6;
color: #212529;
}
[data-bs-theme="light"] .btn-light:hover {
background-color: #dde2e6;
border-color: #ced4da;
color: #000;
}
[data-bs-theme="dark"] .btn-light {
background-color: #343a40;
border-color: #495057;
color: #f8f9fa;
}
[data-bs-theme="dark"] .btn-light:hover {
background-color: #495057;
border-color: #6c757d;
color: #fff;
}
/* Button group specific styles */
.btn-group .btn-light {
margin: 0 1px;
}
.btn-group .btn-light.disabled {
opacity: 0.8;
}
-124
View File
@@ -1,124 +0,0 @@
/* Layout */
body {
min-height: 100vh;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid var(--bs-border-color);
width: 100%;
}
/* Monitor Cards */
.monitor-card {
transition: transform 0.2s ease-in-out;
height: 100%;
}
.monitor-card:hover {
transform: translateY(-5px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.monitor-card .card-body {
display: flex;
flex-direction: column;
justify-content: center;
padding: 1.5rem;
min-height: 320px;
}
.monitor-header {
background-color: var(--bs-body-bg);
border-bottom: 1px solid var(--bs-border-color);
}
.progress {
background-color: var(--bs-tertiary-bg);
}
/* CPU Gauge */
.cpu-gauge {
position: relative;
width: 100%;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
}
.cpu-gauge canvas {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.cpu-gauge .position-relative {
position: absolute !important;
top: 50%;
left: 50%;
transform: translate(-50%, -55%);
z-index: 2;
width: auto;
text-align: center;
line-height: 1.2;
}
.cpu-gauge .h3 {
font-size: 1.75rem;
line-height: 1;
margin: 0;
}
.cpu-gauge .small {
opacity: 0.75;
margin-top: 3px;
}
/* Theme & Logo */
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
/* Grid Spacing */
.row.g-4 {
--bs-gutter-y: 2rem;
}
/* Responsive */
@media (max-width: 768px) {
.container {
padding: 0.5rem;
}
.h4 {
font-size: 1.25rem;
}
.monitor-card .card-body {
padding: 1rem;
min-height: 280px;
}
.row.g-4 > [class*="col-"] {
margin-bottom: 1rem;
}
}
-86
View File
@@ -1,86 +0,0 @@
html, body {
height: 100%;
margin: 0;
overflow-x: hidden;
}
.custom-container {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
padding: 0;
max-width: 100%;
}
.media-container {
display: flex;
justify-content: center;
align-items: center;
max-width: 100%;
max-height: 80vh;
margin: 0 auto;
overflow: visible;
flex-direction: column;
}
.media-container img {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
.media-container video {
max-width: 100%;
max-height: 70vh;
}
.media-container audio {
width: 100%;
max-width: 600px;
margin: 20px 0;
}
.controls {
margin: 20px 0;
text-align: center;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid #ddd;
width: 100%;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
.file-info table {
table-layout: fixed;
word-wrap: break-word;
}
@media (max-width: 576px) {
.file-info table {
width: 95% !important;
}
.file-info th {
width: 40%;
}
.file-info td {
width: 60%;
}
}
-39
View File
@@ -1,39 +0,0 @@
<?php
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete'])) {
$itemToDelete = $_POST['delete'];
$parentDir = dirname($itemToDelete);
if ($parentDir === '/' || $parentDir === '.') {
$parentDir = $defaultPath;
}
if ($sftp->is_dir($itemToDelete)) {
function deleteFolder($sftp, $folderPath) {
$items = $sftp->nlist($folderPath);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$itemPath = $folderPath . '/' . $item;
if ($sftp->is_dir($itemPath)) {
deleteFolder($sftp, $itemPath);
} else {
$sftp->delete($itemPath);
}
}
return $sftp->rmdir($folderPath);
}
$success = deleteFolder($sftp, $itemToDelete);
} else {
$success = $sftp->delete($itemToDelete);
}
header("Location: index.php?path=" . urlencode($parentDir));
exit;
}
header("Location: index.php");
exit;
-217
View File
@@ -1,217 +0,0 @@
<?php
set_time_limit(900); // 15 minutes max execution time
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 0);
error_reporting(0);
ob_start();
try {
require 'config.php';
$logFile = __DIR__ . '/download_log.txt';
file_put_contents($logFile, "Download started: " . date('Y-m-d H:i:s') . "\n", FILE_APPEND);
$sftp = null;
try {
$sftp = initializeSFTP($host, $username, $password);
file_put_contents($logFile, "SFTP connection established\n", FILE_APPEND);
} catch (Exception $e) {
file_put_contents($logFile, "SFTP connection failed: " . $e->getMessage() . "\n", FILE_APPEND);
throw new Exception("Failed to connect to SFTP server: " . $e->getMessage());
}
function zipFolderRecursive($sftp, $remoteBasePath, $currentPath, $zip, $logFile, &$tempFiles) {
file_put_contents($logFile, "Processing directory: $currentPath\n", FILE_APPEND);
$files = $sftp->nlist($currentPath);
if ($files === false) {
file_put_contents($logFile, "Failed to list directory contents for: $currentPath\n", FILE_APPEND);
throw new Exception("Failed to list directory contents for: $currentPath");
}
file_put_contents($logFile, "Found " . count($files) . " items in $currentPath\n", FILE_APPEND);
foreach ($files as $file) {
if ($file == '.' || $file == '..') continue;
$fullRemotePath = rtrim($currentPath, '/') . '/' . $file;
$baseDirName = basename($remoteBasePath);
$relPathFromBase = substr($fullRemotePath, strlen(dirname($remoteBasePath)) + 1);
file_put_contents($logFile, "Processing: $fullRemotePath (relative: $relPathFromBase)\n", FILE_APPEND);
$isDir = $sftp->is_dir($fullRemotePath);
if ($isDir) {
file_put_contents($logFile, "Found subdirectory: $fullRemotePath\n", FILE_APPEND);
$zip->addEmptyDir($relPathFromBase);
zipFolderRecursive($sftp, $remoteBasePath, $fullRemotePath, $zip, $logFile, $tempFiles);
} else {
$localTempFile = tempnam(sys_get_temp_dir(), 'sftp');
$tempFiles[] = $localTempFile;
file_put_contents($logFile, "Downloading to temp file: $localTempFile\n", FILE_APPEND);
$downloadStart = time();
$downloadSuccess = $sftp->get($fullRemotePath, $localTempFile);
$downloadTime = time() - $downloadStart;
if ($downloadSuccess) {
$fileSize = filesize($localTempFile);
file_put_contents($logFile, "Download successful ($downloadTime seconds), size: $fileSize bytes\n", FILE_APPEND);
$zip->addFile($localTempFile, $relPathFromBase);
} else {
file_put_contents($logFile, "Failed to download file after $downloadTime seconds\n", FILE_APPEND);
}
}
}
}
function zipFolder($sftp, $folderPath, $zipFilePath, $logFile) {
file_put_contents($logFile, "Starting to zip folder recursively: $folderPath\n", FILE_APPEND);
$zip = new ZipArchive();
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
file_put_contents($logFile, "Failed to create zip archive\n", FILE_APPEND);
throw new Exception("Unable to create the zip file.");
}
$tempFiles = [];
try {
zipFolderRecursive($sftp, $folderPath, $folderPath, $zip, $logFile, $tempFiles);
file_put_contents($logFile, "Closing zip file\n", FILE_APPEND);
$zipSuccess = $zip->close();
foreach ($tempFiles as $tempFile) {
if (file_exists($tempFile)) {
@unlink($tempFile);
}
}
if ($zipSuccess) {
file_put_contents($logFile, "Zip created successfully, size: " . filesize($zipFilePath) . " bytes\n", FILE_APPEND);
return true;
} else {
file_put_contents($logFile, "Failed to create zip\n", FILE_APPEND);
return false;
}
} catch (Exception $e) {
foreach ($tempFiles as $tempFile) {
if (file_exists($tempFile)) {
@unlink($tempFile);
}
}
file_put_contents($logFile, "Error during zip creation: " . $e->getMessage() . "\n", FILE_APPEND);
throw $e;
}
}
if (($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['file'])) ||
($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['file']))) {
$file = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST['file'] : $_GET['file'];
file_put_contents($logFile, "Requested file: $file\n", FILE_APPEND);
$fileExists = $sftp->file_exists($file);
$isDir = $sftp->is_dir($file);
file_put_contents($logFile, "File exists: " . ($fileExists ? "Yes" : "No") . "\n", FILE_APPEND);
file_put_contents($logFile, "Is directory: " . ($isDir ? "Yes" : "No") . "\n", FILE_APPEND);
if (!$fileExists && !$isDir) {
throw new Exception("File not found: $file");
}
if ($isDir) {
$zipFilePath = tempnam(sys_get_temp_dir(), 'folder_') . '.zip';
file_put_contents($logFile, "Creating zip at: $zipFilePath\n", FILE_APPEND);
if (zipFolder($sftp, $file, $zipFilePath, $logFile)) {
if (file_exists($zipFilePath) && filesize($zipFilePath) > 0) {
file_put_contents($logFile, "Sending zip file to browser, size: " . filesize($zipFilePath) . " bytes\n", FILE_APPEND);
while (ob_get_level()) {
ob_end_clean();
}
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . basename($file) . '.zip"');
header('Content-Length: ' . filesize($zipFilePath));
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
readfile($zipFilePath);
file_put_contents($logFile, "Download completed\n", FILE_APPEND);
@unlink($zipFilePath);
exit;
} else {
throw new Exception("Failed to create zip file or zip file is empty");
}
} else {
throw new Exception("Failed to create zip archive");
}
} else {
$localFile = basename($file);
file_put_contents($logFile, "Downloading single file: $localFile\n", FILE_APPEND);
$tempFile = tempnam(sys_get_temp_dir(), 'file_');
$downloadStart = time();
$downloadSuccess = $sftp->get($file, $tempFile);
$downloadTime = time() - $downloadStart;
file_put_contents($logFile, "Download " . ($downloadSuccess ? "successful" : "failed") . " ($downloadTime seconds)\n", FILE_APPEND);
if ($downloadSuccess) {
$fileSize = filesize($tempFile);
file_put_contents($logFile, "Downloaded file size: $fileSize bytes\n", FILE_APPEND);
if ($fileSize > 0) {
while (ob_get_level()) {
ob_end_clean();
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $localFile . '"');
header('Content-Length: ' . $fileSize);
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
readfile($tempFile);
file_put_contents($logFile, "Download completed\n", FILE_APPEND);
@unlink($tempFile);
exit;
} else {
@unlink($tempFile);
throw new Exception("Downloaded file is empty");
}
} else {
@unlink($tempFile);
throw new Exception("Failed to download file from SFTP server");
}
}
} else {
throw new Exception("Invalid request method or missing file parameter");
}
} catch (Exception $e) {
$errorMessage = "Error: " . $e->getMessage();
file_put_contents($logFile, $errorMessage . "\n", FILE_APPEND);
while (ob_get_level()) {
ob_end_clean();
}
header("HTTP/1.1 500 Internal Server Error");
echo $errorMessage;
}
?>
-244
View File
@@ -1,244 +0,0 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
// Check if user has permission to upload/modify
if(!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) {
die("You don't have permission to extract archives.");
}
// Include error reporting for debugging
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
use phpseclib3\Net\SFTP;
$sftp = initializeSFTP($host, $username, $password);
// Process extraction request
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
$archivePath = $_POST['archive'];
$dirPath = dirname($archivePath);
$archiveName = basename($archivePath);
$extension = pathinfo($archiveName, PATHINFO_EXTENSION);
// Create a temporary local directory for extraction
$tempDir = sys_get_temp_dir() . '/sftp_extract_' . time();
if (!mkdir($tempDir, 0777, true)) {
die("Failed to create temporary directory");
}
// Download the archive to the temp directory
$localArchivePath = $tempDir . '/' . $archiveName;
if (!$sftp->get($archivePath, $localArchivePath)) {
rmdir($tempDir);
die("Failed to download the archive");
}
// Detect archive type and extract
$extractionSuccess = false;
try {
// Determine the extraction method based on file extension
switch (strtolower($extension)) {
case 'zip':
$extractionSuccess = extractZip($localArchivePath, $tempDir);
break;
case 'rar':
$extractionSuccess = extractRar($localArchivePath, $tempDir);
break;
case 'tar':
case 'gz':
case 'bz2':
case 'xz':
case '7z':
$extractionSuccess = extractArchive($localArchivePath, $tempDir);
break;
default:
die("Unsupported archive format");
}
if ($extractionSuccess) {
// Upload extracted files back to the server
uploadExtractedFiles($sftp, $tempDir, $dirPath);
// Clean up temporary directory
deleteDirectory($tempDir);
// Redirect back to the file listing
header("Location: index.php?path=" . urlencode($dirPath));
exit;
} else {
die("Failed to extract the archive");
}
} catch (Exception $e) {
deleteDirectory($tempDir);
die("Error during extraction: " . $e->getMessage());
}
} else {
header("Location: index.php");
exit;
}
// Extract ZIP archives
function extractZip($archivePath, $destination) {
$zip = new ZipArchive();
if ($zip->open($archivePath) === TRUE) {
$zip->extractTo($destination);
$zip->close();
return true;
}
return false;
}
// Extract RAR archives (requires rar extension or unrar command)
function extractRar($archivePath, $destination) {
// Try PHP Rar extension first
if (extension_loaded('rar')) {
$rar = RarArchive::open($archivePath);
if ($rar) {
$entries = $rar->getEntries();
foreach ($entries as $entry) {
$entry->extract($destination);
}
$rar->close();
return true;
}
}
// Fallback to command line unrar if available
if (shell_exec("which unrar") || file_exists('C:\\Program Files\\WinRAR\\UnRAR.exe')) {
$command = '';
if (PHP_OS_FAMILY === 'Windows') {
$command = '"C:\\Program Files\\WinRAR\\UnRAR.exe" x -o+ ' . escapeshellarg($archivePath) . ' ' . escapeshellarg($destination);
} else {
$command = 'unrar x -o+ ' . escapeshellarg($archivePath) . ' ' . escapeshellarg($destination);
}
exec($command, $output, $returnCode);
return $returnCode === 0;
}
throw new Exception("RAR extraction is not available. Please install PHP RAR extension or UnRAR command line tool.");
}
// Extract other archives using system commands
function extractArchive($archivePath, $destination) {
$extension = pathinfo($archivePath, PATHINFO_EXTENSION);
$command = '';
// Change to the destination directory
$currentDir = getcwd();
chdir($destination);
if (PHP_OS_FAMILY === 'Windows') {
// For Windows, you'll need to have 7-Zip installed
$sevenZipPath = 'C:\\Program Files\\7-Zip\\7z.exe';
if (file_exists($sevenZipPath)) {
$command = '"' . $sevenZipPath . '" x ' . escapeshellarg($archivePath);
} else {
throw new Exception("7-Zip is not installed or not found at the expected location.");
}
} else {
// For Linux/Unix systems
switch (strtolower($extension)) {
case 'tar':
$command = 'tar -xf ' . escapeshellarg($archivePath);
break;
case 'gz':
if (strpos($archivePath, '.tar.gz') !== false) {
$command = 'tar -xzf ' . escapeshellarg($archivePath);
} else {
$command = 'gzip -d ' . escapeshellarg($archivePath);
}
break;
case 'bz2':
if (strpos($archivePath, '.tar.bz2') !== false) {
$command = 'tar -xjf ' . escapeshellarg($archivePath);
} else {
$command = 'bzip2 -d ' . escapeshellarg($archivePath);
}
break;
case 'xz':
if (strpos($archivePath, '.tar.xz') !== false) {
$command = 'tar -xJf ' . escapeshellarg($archivePath);
} else {
$command = 'xz -d ' . escapeshellarg($archivePath);
}
break;
case '7z':
$command = '7z x ' . escapeshellarg($archivePath);
break;
default:
throw new Exception("Unsupported archive format");
}
}
exec($command, $output, $returnCode);
// Change back to the original directory
chdir($currentDir);
return $returnCode === 0;
}
// Upload extracted files back to the SFTP server
function uploadExtractedFiles($sftp, $localDir, $remoteDir) {
$items = scandir($localDir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$localPath = $localDir . '/' . $item;
$remotePath = $remoteDir . '/' . $item;
if (is_dir($localPath)) {
// Create the directory on the remote server
if (!$sftp->is_dir($remotePath)) {
$sftp->mkdir($remotePath);
}
// Upload the contents of the directory
uploadExtractedFiles($sftp, $localPath, $remotePath);
} else {
// Upload the file - use the proper phpseclib3 method
$sftp->put($remotePath, $localPath, SFTP::SOURCE_LOCAL_FILE);
}
}
}
// Recursively delete a directory
function deleteDirectory($dir) {
if (!is_dir($dir)) {
return;
}
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$path = $dir . '/' . $item;
if (is_dir($path)) {
deleteDirectory($path);
} else {
unlink($path);
}
}
rmdir($dir);
}
?>
-338
View File
@@ -1,338 +0,0 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
$currentPath = isset($_GET['path']) ? normalizePath($_GET['path']) : $defaultPath;
if (strpos($currentPath, $defaultPath) !== 0) {
$currentPath = $defaultPath;
}
if (!$sftp->chdir($currentPath)) {
die("Failed to navigate to the selected folder: $currentPath");
}
$items = $sftp->nlist();
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
$audioExtensions = ['mp3', 'wav', 'm4a', 'flac', 'aac'];
$editableExtensions = ['txt', 'html', 'css', 'js', 'php', 'xml', 'json', 'md', 'csv', 'log', 'ini', 'conf', 'sh', 'bat', 'py', 'rb', 'java', 'c', 'cpp', 'h', 'hpp'];
$archiveExtensions = ['zip', 'rar', 'tar', 'gz', '7z', 'bz2', 'xz', 'tar.gz', 'tar.bz2', 'tar.xz'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>FTP</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/index.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>USB RAID Array</h1>
<div class="mb-3 p-3">
<a href="../logout.php" class="btn btn-danger">Logout</a>
<a href="../changepassword.php" class="btn btn-warning">Change Password</a>
<?php if (isset($_SESSION["admin"]) && $_SESSION["admin"] == true) { ?>
<a href="../adminpanel.php" class="btn btn-primary">Admin Panel</a>
<a href="serverstat.php" class="btn btn-primary">Server Status</a>
<?php } ?>
</div>
</header>
<section class="row">
<article class="col-8 border border-2 border-primary rounded p-2">
<div class="col">
<h4>Current Path: <?= htmlspecialchars($currentPath) ?></h4>
</div>
<div class="col">
<!-- Action Buttons -->
<div class="action-buttons">
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) { ?>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createFileModal">Create File</button>
<button type="button" class="btn btn-success" onclick="document.getElementById('fileInput').click()">Upload Files</button>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createDirModal">Create Directory</button>
<button type="button" class="btn btn-info" onclick="document.getElementById('dirInput').click()">Upload Directory</button>
<input type="file" id="fileInput" multiple style="display: none;" onchange="handleFileSelect(event)">
<input type="file" id="dirInput" webkitdirectory directory multiple style="display: none;" onchange="handleFileSelect(event)">
<?php } ?>
</div>
<!-- Modal Forms -->
<!-- Create Directory Dialog -->
<div class="modal fade" id="createDirModal" tabindex="-1" aria-labelledby="createDirModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createDirModalLabel">Create New Directory</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="dirName" class="form-label">Directory Name</label>
<input type="text" class="form-control" id="dirName" placeholder="Enter directory name">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="createDirectory()">Create</button>
</div>
</div>
</div>
</div>
<!-- Create File Dialog -->
<div class="modal fade" id="createFileModal" tabindex="-1" aria-labelledby="createFileModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createFileModalLabel">Create New File</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="fileName" class="form-label">File Name (with extension)</label>
<input type="text" class="form-control" id="fileName" placeholder="example.txt">
<div class="form-text">Supported extensions: .txt, .html, .css, .js, .php, etc.</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="createFile()">Create</button>
</div>
</div>
</div>
</div>
<!-- Rename Confirmation Dialog -->
<div class="modal fade" id="renameModal" tabindex="-1" aria-labelledby="renameModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="renameModalLabel">Rename Item</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="POST" action="rename.php">
<div class="modal-body">
<div class="mb-3">
<label for="originalName" class="form-label">Current Name:</label>
<input type="text" class="form-control" id="originalName" disabled>
<input type="hidden" id="fullPath" name="path">
<input type="hidden" id="isDirectory" name="isDirectory">
</div>
<div class="mb-3">
<label for="newName" class="form-label">New Name:</label>
<input type="text" class="form-control" id="newName" name="newName" required>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Rename</button>
</div>
</form>
</div>
</div>
</div>
<!-- End of Modal Forms -->
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) { ?>
<div class="dropzone" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event)">
Drop files or folders here to upload
</div>
<?php } ?>
<table>
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php
$directories = [];
$files = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
if ($sftp->is_dir($item)) {
$directories[] = $item;
} else {
$files[] = $item;
}
}
// Sort directories and files alphabetically
sort($directories, SORT_STRING | SORT_FLAG_CASE);
sort($files, SORT_STRING | SORT_FLAG_CASE);
if ($currentPath !== $defaultPath) : ?>
<tr>
<td colspan="3"><a class="text-danger" href="?path=<?= urlencode(dirname($currentPath)) ?>"><b>.. (Go Back)</b></a></td>
</tr>
<?php endif;
foreach ($directories as $directory) : ?>
<tr>
<td colspan="2">
<a href="?path=<?= urlencode($currentPath . '/' . $directory) ?>"><?= htmlspecialchars($directory) ?>/</a>
</td>
<td>
<?php if (isset($_SESSION["delPer"]) && $_SESSION["delPer"] == true) : ?>
<form method="POST" action="delete.php" style="display:inline;">
<input type="hidden" name="delete" value="<?= htmlspecialchars($currentPath . '/' . $directory) ?>">
<button type="submit" class="btn btn-danger mt-1 mb-1" onclick="confirmDelete(event)">Delete</button>
</form>
<?php endif; ?>
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<form method="POST" action="download.php" style="display:inline;">
<input type="hidden" name="file" value="<?= htmlspecialchars($currentPath . '/' . $directory) ?>">
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
</form>
<?php endif; ?>
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) : ?>
<button type="button" class="btn btn-warning mt-1 mb-1" onclick="openRenameModal('<?= htmlspecialchars(addslashes($directory)) ?>', '<?= htmlspecialchars(addslashes($currentPath . '/' . $directory)) ?>')">Rename</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach;
foreach ($files as $file) : ?>
<?php
$fileExtension = pathinfo($file, PATHINFO_EXTENSION);
$isPDF = strtolower($fileExtension) === 'pdf';
$isImage = in_array($fileExtension, $imageExtensions);
$isVideo = in_array($fileExtension, $videoExtensions);
$isAudio = in_array($fileExtension, $audioExtensions);
$isMedia = $isImage || $isVideo || $isAudio;
$isEditable = in_array($fileExtension, $editableExtensions);
$isArchive = in_array($fileExtension, $archiveExtensions);
if (!$isArchive && strpos($file, '.tar.') !== false) {
$isArchive = true;
}
?>
<tr>
<td>
<?php if ($isMedia): ?>
<a class="text-info-emphasis" href="view.php?file=<?= urlencode($currentPath . '/' . $file) ?>">
<?= htmlspecialchars($file) ?>
</a>
<?php if ($isImage): ?>
<span class="badge bg-success rounded-pill">Image</span>
<?php elseif ($isVideo): ?>
<span class="badge bg-primary rounded-pill">Video</span>
<?php elseif ($isAudio): ?>
<span class="badge bg-info rounded-pill">Audio</span>
<?php endif; ?>
<?php elseif ($isEditable): ?>
<a class="text-warning-emphasis" href="open.php?file=<?= urlencode($currentPath . '/' . $file) ?>">
<?= htmlspecialchars($file) ?>
</a>
<span class="badge bg-secondary rounded-pill"><?= htmlspecialchars($fileExtension) ?></span>
<?php elseif ($isPDF): ?>
<a class="text-info-emphasis" href="pdf.php?file=<?= urlencode($currentPath . '/' . $file) ?>&type=pdf">
<?= htmlspecialchars($file) ?>
</a>
<span class="badge bg-danger rounded-pill">PDF</span>
<?php else: ?>
<?= htmlspecialchars($file) ?>
<span class="badge bg-light text-dark rounded-pill"><?= htmlspecialchars($fileExtension) ?></span>
<?php endif; ?>
</td>
<td>
<span class="text"><?= formatBytes($sftp->stat($currentPath . '/' . $file)['size']) ?></span>
</td>
<td>
<?php if (isset($_SESSION["delPer"]) && $_SESSION["delPer"] == true) : ?>
<form method="POST" action="delete.php" style="display:inline;">
<input type="hidden" name="delete" value="<?= htmlspecialchars($currentPath . '/' . $file) ?>">
<button type="submit" class="btn btn-danger mt-1 mb-1" onclick="confirmDelete(event)">Delete</button>
</form>
<?php endif; ?>
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<form method="POST" action="download.php" style="display:inline;">
<input type="hidden" name="file" value="<?= htmlspecialchars($currentPath . '/' . $file) ?>">
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
</form>
<?php endif; ?>
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) : ?>
<button type="button" class="btn btn-warning mt-1 mb-1" onclick="openRenameModal('<?= htmlspecialchars(addslashes($file)) ?>', '<?= htmlspecialchars(addslashes($currentPath . '/' . $file)) ?>')">Rename</button>
<?php endif; ?>
<?php if ($isArchive && isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) : ?>
<form method="POST" action="extract.php" style="display:inline;">
<input type="hidden" name="archive" value="<?= htmlspecialchars($currentPath . '/' . $file) ?>">
<button type="submit" class="btn btn-info mt-1 mb-1">Extract</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</article>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-Array" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<script>
// Pass PHP variables to JavaScript
var currentPath = "<?php echo $currentPath; ?>";
</script>
<script src="js/index.js"></script>
</body>
</html>
<?php
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>
-288
View File
@@ -1,288 +0,0 @@
function handleDragOver(event) {
event.preventDefault();
event.currentTarget.classList.add('dragover');
}
function handleDragLeave(event) {
event.currentTarget.classList.remove('dragover');
}
function handleDrop(event) {
event.preventDefault();
event.currentTarget.classList.remove('dragover');
if (event.dataTransfer.items) {
const items = event.dataTransfer.items;
processDroppedItems(items);
} else {
const files = event.dataTransfer.files;
uploadFiles(files);
}
}
function processDroppedItems(items) {
const allFiles = [];
let pendingItems = 0;
function handleEntry(entry, path = '') {
if (entry.isFile) {
pendingItems++;
entry.file(file => {
Object.defineProperty(file, 'webkitRelativePath', {
value: path + file.name
});
allFiles.push(file);
pendingItems--;
if (pendingItems === 0) {
uploadFiles(allFiles);
}
});
} else if (entry.isDirectory) {
const reader = entry.createReader();
readEntries(reader, path + entry.name + '/');
}
}
function readEntries(reader, path) {
pendingItems++;
reader.readEntries(entries => {
if (entries.length > 0) {
for (const entry of entries) {
handleEntry(entry, path);
}
readEntries(reader, path);
}
pendingItems--;
if (pendingItems === 0 && allFiles.length > 0) {
uploadFiles(allFiles);
}
});
}
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind !== 'file') continue;
const entry = item.webkitGetAsEntry ? item.webkitGetAsEntry() : item.getAsEntry();
if (entry) {
handleEntry(entry);
}
}
if (pendingItems === 0 && allFiles.length === 0) {
alert('No valid files or directories found.');
}
}
function handleFileSelect(event) {
const files = event.target.files;
uploadFiles(files);
}
function uploadFiles(files) {
if (!files || files.length === 0) {
alert('No files selected for upload.');
return;
}
const formData = new FormData();
formData.append('currentPath', currentPath);
let filesAdded = 0;
let directories = new Set();
for (const file of files) {
const relativePath = file.webkitRelativePath || '';
if (relativePath) {
const parts = relativePath.split('/');
let currentPath = '';
for (let i = 0; i < parts.length - 1; i++) {
currentPath += (i > 0 ? '/' : '') + parts[i];
if (currentPath) {
directories.add(currentPath);
}
}
}
formData.append('files[]', file);
formData.append('paths[]', relativePath);
filesAdded++;
}
if (directories.size > 0) {
formData.append('create_dirs', JSON.stringify(Array.from(directories)));
}
if (filesAdded === 0) {
alert('No valid files selected for upload.');
return;
}
const uploadStatus = document.createElement('div');
uploadStatus.className = 'alert alert-info';
uploadStatus.textContent = 'Uploading files, please wait...';
document.querySelector('.action-buttons').after(uploadStatus);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.php', true);
xhr.onerror = () => {
uploadStatus.className = 'alert alert-danger';
uploadStatus.textContent = 'Network error occurred during upload.';
setTimeout(() => uploadStatus.remove(), 5000);
};
xhr.timeout = 300000; // 5 minutes
xhr.ontimeout = () => {
uploadStatus.className = 'alert alert-danger';
uploadStatus.textContent = 'Upload timed out. Try with smaller files or fewer files.';
setTimeout(() => uploadStatus.remove(), 5000);
};
xhr.onload = () => {
if (xhr.status === 200) {
uploadStatus.className = 'alert alert-success';
uploadStatus.textContent = 'Upload successful!';
setTimeout(() => {
uploadStatus.remove();
window.location.reload();
}, 1500);
} else {
uploadStatus.className = 'alert alert-danger';
uploadStatus.textContent = 'Upload failed: ' + (xhr.responseText || xhr.statusText);
setTimeout(() => uploadStatus.remove(), 5000);
}
};
xhr.send(formData);
}
function createDirectory() {
const dirName = document.getElementById('dirName').value.trim();
if (!dirName) {
alert('Please enter a directory name.');
return;
}
const formData = new FormData();
formData.append('currentPath', currentPath);
formData.append('dirName', dirName);
formData.append('action', 'createDir');
const xhr = new XMLHttpRequest();
xhr.open('POST', 'createdir.php', true);
xhr.onload = () => {
if (xhr.status === 200) {
alert('Directory created successfully!');
const modal = bootstrap.Modal.getInstance(document.getElementById('createDirModal'));
if (modal) modal.hide();
window.location.reload();
} else {
alert('Failed to create directory: ' + xhr.responseText || xhr.statusText);
}
};
xhr.send(formData);
}
function confirmDelete(event) {
event.preventDefault();
if (confirm("Do you really want to delete this file?")) {
event.target.form.submit();
}
}
function createFile() {
const fileName = document.getElementById('fileName').value.trim();
if (!fileName) {
alert('Please enter a file name.');
return;
}
if (fileName.indexOf('.') === -1) {
alert('Please include a file extension (e.g., .txt, .html, .php)');
return;
}
const formData = new FormData();
formData.append('currentPath', currentPath);
formData.append('fileName', fileName);
formData.append('action', 'createFile');
const xhr = new XMLHttpRequest();
xhr.open('POST', 'createfile.php', true);
xhr.onload = () => {
if (xhr.status === 200) {
try {
const response = JSON.parse(xhr.responseText);
if (response.success) {
alert('File created successfully!');
const modal = bootstrap.Modal.getInstance(document.getElementById('createFileModal'));
if (modal) modal.hide();
window.location.reload();
} else {
alert('Failed to create file: ' + (response.message || 'Unknown error'));
}
} catch (e) {
alert('Error processing response: ' + xhr.responseText);
}
} else {
alert('Failed to create file: ' + xhr.responseText || xhr.statusText);
}
};
xhr.send(formData);
}
function openRenameModal(name, path) {
document.getElementById('originalName').value = name;
document.getElementById('fullPath').value = path;
document.getElementById('isDirectory').value = name.endsWith('/') ? '1' : '0';
document.getElementById('newName').value = name.endsWith('/') ? name.slice(0, -1) : name;
const renameModal = new bootstrap.Modal(document.getElementById('renameModal'));
renameModal.show();
}
document.addEventListener('DOMContentLoaded', function() {
const themeToggle = document.getElementById('themeToggle');
const html = document.documentElement;
const themeText = document.getElementById('themeText');
const themeIcon = themeToggle.querySelector('.bi');
function setTheme(theme) {
html.setAttribute('data-bs-theme', theme);
document.body.classList.remove('theme-light', 'theme-dark');
document.body.classList.add('theme-' + theme);
localStorage.setItem('theme', theme);
if (theme === 'dark') {
themeText.textContent = 'Light Mode';
themeIcon.className = 'bi bi-sun';
themeToggle.classList.remove('btn-dark');
themeToggle.classList.add('btn-light');
} else {
themeText.textContent = 'Dark Mode';
themeIcon.className = 'bi bi-moon';
themeToggle.classList.remove('btn-light');
themeToggle.classList.add('btn-dark');
}
}
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme) {
setTheme(savedTheme);
} else {
setTheme(prefersDark ? 'dark' : 'light');
}
themeToggle.addEventListener('click', function() {
const currentTheme = html.getAttribute('data-bs-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
});
});
-204
View File
@@ -1,204 +0,0 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
$filePath = isset($_GET['file']) ? $_GET['file'] : '';
$content = '';
$fileName = basename($filePath);
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
$editable = false;
$editableExtensions = ['txt', 'html', 'css', 'js', 'php', 'xml', 'json', 'md', 'csv', 'log', 'ini', 'conf', 'sh', 'bat', 'py', 'rb', 'java', 'c', 'cpp', 'h', 'hpp'];
if (!empty($filePath) && $sftp->file_exists($filePath) && !$sftp->is_dir($filePath)) {
if (in_array(strtolower($extension), $editableExtensions)) {
$editable = true;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) {
$newContent = $_POST['content'];
if ($sftp->put($filePath, $newContent)) {
$saveSuccess = true;
} else {
$saveError = "Failed to save changes. Check permissions.";
}
}
$content = $sftp->get($filePath);
if ($content === false) {
$error = "Failed to read file contents.";
}
} else {
$error = "This file type is not supported for editing.";
}
} else {
$error = "File not found or is a directory.";
}
$showLineNumbers = in_array(strtolower($extension), ['php', 'js', 'html', 'css', 'py', 'java', 'c', 'cpp', 'h', 'hpp', 'rb', 'sh', 'xml', 'json']);
$parentDir = dirname($filePath);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Edit File - <?= htmlspecialchars($fileName) ?></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/open.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body>
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="container mt-4">
<div class="header-container">
<h1>Edit File: <?= htmlspecialchars($fileName) ?></h1>
<a href="index.php?path=<?= urlencode($parentDir) ?>" class="btn btn-secondary">Back to File List</a>
</div>
<?php if (isset($error)): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php elseif ($editable): ?>
<?php if (isset($saveSuccess)): ?>
<div class="alert alert-success">File saved successfully!</div>
<?php endif; ?>
<?php if (isset($saveError)): ?>
<div class="alert alert-danger"><?= $saveError ?></div>
<?php endif; ?>
<form method="POST" id="editorForm">
<div class="mb-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<label for="editor" class="form-label">File Content</label>
<?php if (!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true): ?>
<span class="readonly-notice">Read-only mode (you don't have upload permissions)</span>
<?php endif; ?>
</div>
<div class="editor-container">
<?php if ($showLineNumbers): ?>
<div id="lineNumbers" class="line-numbers"></div>
<?php endif; ?>
<textarea id="editor" name="content" class="form-control" <?= (!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) ? 'readonly' : '' ?>><?= htmlspecialchars($content) ?></textarea>
</div>
</div>
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true): ?>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
<?php endif; ?>
</form>
<?php endif; ?>
</div>
<script src="../../js/theme.js"></script>
<script>
function updateLineNumbers() {
const editor = document.getElementById('editor');
const lineNumbers = document.getElementById('lineNumbers');
if (!lineNumbers) return;
const lines = editor.value.split('\n');
const lineCount = lines.length;
let html = '';
for (let i = 1; i <= lineCount; i++) {
html += i + '<br>';
}
lineNumbers.innerHTML = html;
syncLineNumbersHeight();
lineNumbers.scrollTop = editor.scrollTop;
}
function syncLineNumbersHeight() {
const editor = document.getElementById('editor');
const lineNumbers = document.getElementById('lineNumbers');
if (!lineNumbers || !editor) return;
lineNumbers.style.height = editor.clientHeight + 'px';
}
document.addEventListener('DOMContentLoaded', function() {
const editor = document.getElementById('editor');
const lineNumbers = document.getElementById('lineNumbers');
if (editor && lineNumbers) {
setTimeout(() => {
updateLineNumbers();
syncLineNumbersHeight();
}, 0);
editor.addEventListener('input', updateLineNumbers);
editor.addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
e.preventDefault();
const start = this.selectionStart;
const end = this.selectionEnd;
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
this.selectionStart = this.selectionEnd = start + 4;
updateLineNumbers();
}
});
editor.addEventListener('scroll', function() {
if (lineNumbers) {
lineNumbers.scrollTop = this.scrollTop;
}
});
editor.addEventListener('mouseup', syncLineNumbersHeight);
const observer = new MutationObserver(function(mutations) {
syncLineNumbersHeight();
});
observer.observe(editor, {
attributes: true,
attributeFilter: ['style']
});
if (typeof ResizeObserver === 'function') {
const resizeObserver = new ResizeObserver(() => {
syncLineNumbersHeight();
});
resizeObserver.observe(editor);
} else {
window.addEventListener('resize', syncLineNumbersHeight);
}
}
});
</script>
</body>
</html>
-331
View File
@@ -1,331 +0,0 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if (!isset($_GET['file'])) {
die("No file specified");
}
$filePath = $_GET['file'];
$fileName = basename($filePath);
$fileSize = $sftp->stat($filePath)['size'];
if (isset($_GET['stream'])) {
session_write_close();
while (ob_get_level()) {
ob_end_clean();
}
if (ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
$start = 0;
$end = $fileSize - 1;
$length = $fileSize;
if (isset($_SERVER['HTTP_RANGE'])) {
$rangeHeader = $_SERVER['HTTP_RANGE'];
$matches = [];
if (preg_match('/bytes=(\d+)-(\d*)/', $rangeHeader, $matches)) {
$start = intval($matches[1]);
if (!empty($matches[2])) {
$end = intval($matches[2]);
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
}
}
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . basename($filePath) . '"');
header("Accept-Ranges: bytes");
header("Content-Length: $length");
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
set_time_limit(0);
$minChunkSize = 64 * 1024; // 64KB minimum
$maxChunkSize = 2 * 1024 * 1024; // 2MB maximum
$chunkSize = 256 * 1024; // Start with 256KB
$currentPosition = $start;
$bytesRemaining = $length;
$lastChunkTime = microtime(true);
try {
while ($bytesRemaining > 0) {
if (connection_aborted()) {
break;
}
$readSize = min($chunkSize, $bytesRemaining);
$chunkData = $sftp->get($filePath, false, $currentPosition, $readSize);
if ($chunkData !== false) {
$bytesSent = strlen($chunkData);
echo $chunkData;
$bytesRemaining -= $bytesSent;
$currentPosition += $bytesSent;
if (ob_get_level()) {
ob_flush();
}
flush();
$currentTime = microtime(true);
$timeDiff = $currentTime - $lastChunkTime;
$lastChunkTime = $currentTime;
if ($timeDiff > 0) {
$speed = $bytesSent / $timeDiff;
$chunkSize = min(
max($minChunkSize, $chunkSize * (($speed > 512 * 1024) ? 1.5 : 0.8)),
$maxChunkSize
);
}
} else {
break;
}
}
} catch (Exception $e) {
error_log("Streaming error: " . $e->getMessage());
}
exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDF Viewer</title>
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/pdf.css">
<script src="../../js/bootstrap.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.js"></script>
</head>
<body>
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="text-center border-bottom m-5">
<h1 class="mb-4">PDF Viewer</h1>
<div class="mb-3">
<a href="index.php?path=<?= urlencode(dirname($filePath)) ?>" class="btn btn-primary">Back to Files</a>
</div>
</header>
<div id="pdfContainer" class="container-fluid position-relative">
<div id="loadingMessage" class="d-flex align-items-center justify-content-center w-100 h-100">
<div class="bg-dark bg-opacity-75 text-white p-3 rounded">
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
Loading PDF...
</div>
</div>
</div>
<div class="controls">
<div class="btn-group" role="group" aria-label="PDF Navigation">
<button id="prev" class="btn btn-light border">
<i class="bi bi-chevron-left"></i> Previous
</button>
<button class="btn btn-light border disabled">
Page <span id="pageNum"></span> of <span id="pageCount"></span>
</button>
<button id="next" class="btn btn-light border">
Next <i class="bi bi-chevron-right"></i>
</button>
<button id="fullscreen" class="btn btn-light border">
<i class="bi bi-fullscreen"></i>
</button>
</div>
</div>
<div class="actionButton">
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
<?php endif; ?>
</div>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-Array" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<script>
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
const url = 'pdf.php?file=<?= urlencode($filePath) ?>&stream=1';
const pdfFileName = '<?= htmlspecialchars($fileName) ?>';
let currentPage = 1;
let pdfDoc = null;
const PdfPageStorage = {
getStorageKey() {
return `pdf_page_${pdfFileName}`;
},
savePage(pageNum) {
localStorage.setItem(this.getStorageKey(), pageNum.toString());
},
loadPage() {
return parseInt(localStorage.getItem(this.getStorageKey())) || 1;
}
};
async function loadPDF() {
try {
pdfDoc = await pdfjsLib.getDocument(url).promise;
document.getElementById('pageCount').textContent = pdfDoc.numPages;
currentPage = Math.min(PdfPageStorage.loadPage(), pdfDoc.numPages);
renderPage(currentPage);
document.getElementById('loadingMessage').classList.add('d-none');
} catch (error) {
console.error('Error loading PDF:', error);
const loadingMessage = document.getElementById('loadingMessage');
loadingMessage.querySelector('div').classList.add('bg-danger');
loadingMessage.querySelector('div').innerHTML = '<i class="bi bi-exclamation-triangle"></i> Error loading PDF';
}
}
async function renderPage(pageNumber) {
const page = await pdfDoc.getPage(pageNumber);
const pageContainer = document.createElement('div');
pageContainer.className = 'page-container';
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const viewport = page.getViewport({ scale: 1.0 });
const pixelRatio = window.devicePixelRatio || 1;
const widthScale = (windowWidth / viewport.width);
const heightScale = (windowHeight / viewport.height);
let scale;
if (isFullscreen) {
scale = Math.min(widthScale, heightScale) * 0.95;
} else if (windowWidth < 768) {
scale = widthScale * 0.95;
} else {
scale = Math.min(widthScale, heightScale, 2);
}
const scaledViewport = page.getViewport({ scale: scale * pixelRatio });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = scaledViewport.height;
canvas.width = scaledViewport.width;
canvas.style.width = `${scaledViewport.width / pixelRatio}px`;
canvas.style.height = `${scaledViewport.height / pixelRatio}px`;
pageContainer.appendChild(canvas);
document.getElementById('pdfContainer').innerHTML = '';
document.getElementById('pdfContainer').appendChild(pageContainer);
document.getElementById('pageNum').textContent = pageNumber;
await page.render({
canvasContext: context,
viewport: scaledViewport // Use scaledViewport instead of viewport
}).promise;
PdfPageStorage.savePage(pageNumber);
}
document.getElementById('prev').addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
renderPage(currentPage);
}
});
document.getElementById('next').addEventListener('click', () => {
if (currentPage < pdfDoc.numPages) {
currentPage++;
renderPage(currentPage);
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && isFullscreen) {
document.getElementById('fullscreen').click();
} else if (e.key === 'ArrowLeft' && currentPage > 1) {
currentPage--;
renderPage(currentPage);
} else if (e.key === 'ArrowRight' && currentPage < pdfDoc.numPages) {
currentPage++;
renderPage(currentPage);
}
});
window.addEventListener('resize', () => {
if (currentPage) {
renderPage(currentPage);
}
});
let isFullscreen = false;
document.getElementById('fullscreen').addEventListener('click', () => {
const container = document.querySelector('.custom-container');
const fullscreenBtn = document.getElementById('fullscreen');
const fullscreenIcon = fullscreenBtn.querySelector('i');
if (!isFullscreen) {
container.classList.add('fullscreen-mode');
document.body.style.overflow = 'hidden';
fullscreenIcon.classList.remove('bi-fullscreen');
fullscreenIcon.classList.add('bi-fullscreen-exit');
} else {
container.classList.remove('fullscreen-mode');
document.body.style.overflow = '';
fullscreenIcon.classList.remove('bi-fullscreen-exit');
fullscreenIcon.classList.add('bi-fullscreen');
}
isFullscreen = !isFullscreen;
renderPage(currentPage);
});
loadPDF();
</script>
<script src="../../js/theme.js"></script>
</body>
</html>
-48
View File
@@ -1,48 +0,0 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
if(!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) {
die("You don't have permission to rename files or directories.");
}
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$oldPath = isset($_POST['path']) ? $_POST['path'] : '';
$newName = isset($_POST['newName']) ? trim($_POST['newName']) : '';
$isDirectory = isset($_POST['isDirectory']) && $_POST['isDirectory'] == '1';
if (empty($oldPath) || empty($newName)) {
die("Missing required information for renaming.");
}
$dirPath = dirname($oldPath);
$oldName = basename($oldPath);
$newPath = $dirPath . '/' . $newName;
if ($sftp->file_exists($newPath) || $sftp->is_dir($newPath)) {
die("Error: A file or directory with this name already exists.");
}
if ($sftp->rename($oldPath, $newPath)) {
header("Location: index.php?path=" . urlencode($dirPath));
exit;
} else {
die("Failed to rename the item. Please try again.");
}
} else {
header("Location: index.php");
exit;
}
?>
-560
View File
@@ -1,560 +0,0 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
if (empty($_SESSION['admin']) || $_SESSION['admin'] !== true) {
session_destroy();
header("location: ../../index.php");
exit();
}
require '../../vendor/autoload.php';
use phpseclib3\Net\SSH2;
$ssh_host = 'localhost';
$ssh_username = 'uname';
$ssh_password = 'pswd';
$ssh = new SSH2($ssh_host);
if (!$ssh->login($ssh_username, $ssh_password)) {
die('SSH login failed');
}
function getDiskData($ssh, $device) {
return $ssh->exec("df -h $device");
}
function getSystemData($ssh) {
// Get memory info using awk to clean up the output
$meminfo = $ssh->exec("free -m | awk 'NR==2{printf \"%s,%s,%s,\", $2,$3,$7} NR==3{printf \"%s,%s,%s\", $2,$3,$4}'");
// Use vmstat to get CPU idle percentage, more reliable and commonly available
$cpuIdle = $ssh->exec("vmstat 1 2 | tail -1 | awk '{print $15}'");
return [
'storage' => [
'system' => parseDiskData($ssh->exec('df -h /dev/ubuntu-vg/ubuntu-lv')),
'data' => parseDiskData($ssh->exec('df -h /dev/sdb1'))
],
'raid' => parseRaidStatus($ssh->exec('cat /proc/mdstat')),
'resources' => parseSystemStats($cpuIdle, $meminfo),
'lastUpdate' => date('Y-m-d H:i:s')
];
}
$systemData = getSystemData($ssh);
$monitors = [
[
'id' => 'SystemDisk',
'title' => 'System Disk (sda)',
'icon' => 'hdd',
'color' => 'primary',
'type' => 'storage',
'source' => 'system'
],
[
'id' => 'DataDisk',
'title' => 'Data Disk (sdb)',
'icon' => 'hdd',
'color' => 'primary',
'type' => 'storage',
'source' => 'data'
],
[
'id' => 'RAID',
'title' => 'RAID Status',
'icon' => 'shield-check',
'color' => 'warning',
'type' => 'raid'
],
[
'id' => 'CPU',
'title' => 'CPU Usage',
'icon' => 'cpu',
'color' => 'info',
'type' => 'resources',
'source' => 'cpu'
],
[
'id' => 'Memory',
'title' => 'Memory Usage',
'icon' => 'memory',
'color' => 'success',
'type' => 'resources',
'source' => 'memory'
]
];
function parseDiskData($output) {
$lines = explode("\n", trim($output));
if (count($lines) < 2) return null;
$values = preg_split('/\s+/', trim($lines[1]));
return [
'size' => $values[1] ?? 'N/A',
'used' => $values[2] ?? 'N/A',
'available' => $values[3] ?? 'N/A',
'usage' => $values[4] ?? '0%'
];
}
function parseRaidStatus($output) {
if (empty($output)) {
return [
'active' => false,
'status' => 'unknown',
'type' => 'N/A'
];
}
$status = [
'active' => false,
'status' => 'unknown',
'type' => 'N/A'
];
$lines = explode("\n", trim($output));
foreach ($lines as $line) {
if (preg_match('/active\s+(\w+)/', $line, $matches)) {
$status['active'] = true;
$status['type'] = $matches[1] ?? 'N/A';
}
if (strpos($line, '[UU]') !== false) {
$status['status'] = 'healthy';
} elseif (strpos($line, '_') !== false) {
$status['status'] = 'degraded';
}
}
return $status;
}
function parseSystemStats($cpuOutput, $memOutput) {
// Parse CPU stats - convert idle percentage to usage percentage
$idlePercent = floatval(trim(str_replace(',', '.', $cpuOutput)));
$cpuUsage = 100 - $idlePercent; // Convert idle to usage percentage
if (is_nan($cpuUsage) || $cpuUsage < 0 || $cpuUsage > 100) {
$cpuUsage = 0; // Default to 0% if parsing fails
}
$cpu = ['usage' => number_format($cpuUsage, 1)];
// Parse memory stats
$parts = explode(',', $memOutput);
if (count($parts) >= 6) {
$memTotal = intval($parts[0]);
$memUsed = intval($parts[1]);
$memAvail = intval($parts[2]);
$swapTotal = intval($parts[3]);
$swapUsed = intval($parts[4]);
$swapFree = intval($parts[5]);
$memory = [
'total' => number_format($memTotal / 1024, 2) . 'G',
'used' => number_format($memUsed / 1024, 2) . 'G',
'available' => number_format($memAvail / 1024, 2) . 'G',
'usage' => ($memTotal > 0 ? round(($memUsed / $memTotal) * 100, 1) : 0) . '%',
'swap_total' => number_format($swapTotal / 1024, 2) . 'G',
'swap_used' => number_format($swapUsed / 1024, 2) . 'G',
'swap_free' => number_format($swapFree / 1024, 2) . 'G',
'swap_usage' => ($swapTotal > 0 ? round(($swapUsed / $swapTotal) * 100, 1) : 0) . '%'
];
} else {
$memory = [
'total' => '0G',
'used' => '0G',
'available' => '0G',
'usage' => '0%',
'swap_total' => '0G',
'swap_used' => '0G',
'swap_free' => '0G',
'swap_usage' => '0%'
];
}
return ['cpu' => $cpu, 'memory' => $memory];
}
function convertToBytes($size) {
if (preg_match('/^([\d.]+)([KMGT]?)i?B?$/', trim($size), $matches)) {
$value = floatval($matches[1]);
$unit = strtoupper($matches[2]);
switch ($unit) {
case 'P': $value *= 1024;
case 'T': $value *= 1024;
case 'G': $value *= 1024;
case 'M': $value *= 1024;
case 'K': $value *= 1024;
}
return $value;
}
return 0;
}
function formatBytes($bytes, $forceUnit = '') {
$units = ['B', 'K', 'M', 'G', 'T', 'P'];
$bytes = max($bytes, 0);
if ($forceUnit) {
$unitIndex = array_search($forceUnit, $units);
if ($unitIndex !== false) {
$bytes /= pow(1024, $unitIndex);
return round($bytes, 2) . $forceUnit;
}
}
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, 2) . $units[$pow];
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Server Status</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/serverstat.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="container-fluid text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<header class="row border-bottom m-5">
<h1>Server Status</h1>
<div class="mb-3 p-3">
<a href="../logout.php" class="btn btn-danger">Logout</a>
<a href="../changepassword.php" class="btn btn-warning">Change Password</a>
<a href="../adminpanel.php" class="btn btn-primary">Admin Panel</a>
<a href="index.php" class="btn btn-primary">SFTP</a>
</div>
</header>
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="card shadow-sm bg-body-tertiary">
<div class="card-header monitor-header">
<div class="d-flex justify-content-between align-items-center">
<h1 class="h4 mb-0">
<i class="bi bi-server text-primary me-2"></i>
Server Disk Status
</h1>
<span class="badge bg-light text-dark">
<i class="bi bi-clock me-1"></i>
<?= htmlspecialchars($systemData['lastUpdate']) ?>
</span>
</div>
</div>
<div class="card-body">
<div class="row g-4">
<?php foreach ($monitors as $monitor): ?>
<div class="col-md-6">
<div class="monitor-card card h-100 border-<?= $monitor['color'] ?>">
<div class="card-header bg-<?= $monitor['color'] ?> bg-opacity-10">
<h3 class="h5 mb-0">
<i class="bi bi-<?= $monitor['icon'] ?> text-<?= $monitor['color'] ?> me-2"></i>
<?= $monitor['title'] ?>
</h3>
</div>
<div class="card-body">
<?php if ($monitor['type'] === 'storage'):
$diskData = $systemData['storage'][$monitor['source']];
$usagePercent = intval(rtrim($diskData['usage'], '%'));
?>
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span>Storage Usage</span>
<span><?= $diskData['usage'] ?></span>
</div>
<div class="progress" style="height: 10px">
<div class="progress-bar bg-<?= $usagePercent > 90 ? 'danger' : ($usagePercent > 75 ? 'warning' : 'success') ?>"
role="progressbar"
style="width: <?= $usagePercent ?>%"
aria-valuenow="<?= $usagePercent ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="row text-center g-2">
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Total</div>
<div class="fw-bold"><?= $diskData['size'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Used</div>
<div class="fw-bold"><?= $diskData['used'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Free</div>
<div class="fw-bold"><?= $diskData['available'] ?></div>
</div>
</div>
</div>
<?php elseif ($monitor['type'] === 'raid'): ?>
<div class="text-center">
<div class="display-4 mb-2">
<i class="bi bi-<?= $systemData['raid']['active'] ? 'check-circle-fill text-success' : 'x-circle-fill text-danger' ?>"></i>
</div>
<h4 class="h6"><?= $systemData['raid']['type'] ?> Array</h4>
<span class="badge bg-<?= $systemData['raid']['status'] === 'healthy' ? 'success' : 'warning' ?>">
<?= ucfirst($systemData['raid']['status']) ?>
</span>
</div>
<?php elseif ($monitor['type'] === 'resources' && $monitor['source'] === 'cpu'): ?>
<div class="cpu-gauge">
<canvas id="cpuGauge" width="150" height="150" data-value="<?= $systemData['resources']['cpu']['usage'] ?>"></canvas>
</div>
<?php elseif ($monitor['type'] === 'resources' && $monitor['source'] === 'memory'):
$memData = $systemData['resources']['memory'];
$usagePercent = floatval(rtrim($memData['usage'], '%'));
$swapPercent = floatval(rtrim($memData['swap_usage'], '%'));
?>
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span>Memory Usage</span>
<span><?= $memData['usage'] ?></span>
</div>
<div class="progress" style="height: 10px">
<div class="progress-bar bg-<?= $usagePercent > 90 ? 'danger' : ($usagePercent > 75 ? 'warning' : 'success') ?>"
role="progressbar"
style="width: <?= $usagePercent ?>%"
aria-valuenow="<?= $usagePercent ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="row text-center g-2 mb-3">
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Total</div>
<div class="fw-bold"><?= $memData['total'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Used</div>
<div class="fw-bold"><?= $memData['used'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Available</div>
<div class="fw-bold"><?= $memData['available'] ?></div>
</div>
</div>
</div>
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span>Swap Usage</span>
<span><?= $memData['swap_usage'] ?></span>
</div>
<div class="progress" style="height: 10px">
<div class="progress-bar bg-<?= $swapPercent > 90 ? 'danger' : ($swapPercent > 75 ? 'warning' : 'success') ?>"
role="progressbar"
style="width: <?= $swapPercent ?>%"
aria-valuenow="<?= $swapPercent ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="row text-center g-2">
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Total</div>
<div class="fw-bold"><?= $memData['swap_total'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Used</div>
<div class="fw-bold"><?= $memData['swap_used'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Free</div>
<div class="fw-bold"><?= $memData['swap_free'] ?></div>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-Array" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
<script src="../../js/theme.js"></script>
<script>
let refreshInterval;
let isRefreshing = false;
function updateData(html) {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Update monitor cards
document.querySelectorAll('.monitor-card').forEach((card, index) => {
const newCard = doc.querySelectorAll('.monitor-card')[index];
if (newCard && card.querySelector('.card-body')) {
card.querySelector('.card-body').innerHTML = newCard.querySelector('.card-body').innerHTML;
// Redraw CPU gauge if this is the CPU card
if (card.querySelector('.cpu-gauge')) {
const cpuValue = parseFloat(newCard.querySelector('canvas').getAttribute('data-value') || '0');
drawCpuGauge(cpuValue);
}
}
});
// Update last update time
const newLastUpdate = doc.querySelector('.badge.bg-light.text-dark')?.innerHTML;
const currentLastUpdate = document.querySelector('.badge.bg-light.text-dark');
if (newLastUpdate && currentLastUpdate && newLastUpdate !== currentLastUpdate.innerHTML) {
currentLastUpdate.innerHTML = newLastUpdate;
}
// Update CPU gauge if needed
const cpuValue = document.querySelector('.cpu-gauge .h4')?.textContent;
if (cpuValue) {
drawCpuGauge(parseFloat(cpuValue));
}
}
function startRefresh() {
if (!refreshInterval) {
refreshInterval = setInterval(() => {
if (!isRefreshing) {
isRefreshing = true;
fetch(window.location.href)
.then(response => response.text())
.then(html => {
updateData(html);
isRefreshing = false;
})
.catch(() => {
isRefreshing = false;
});
}
}, 5000);
}
}
function stopRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
}
}
// Start refresh when page is visible
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
stopRefresh();
} else {
startRefresh();
}
});
// Initial setup
startRefresh();
drawCpuGauge(parseFloat(document.querySelector('.cpu-gauge .h4')?.textContent || '0'));
function drawCpuGauge(value) {
const canvas = document.getElementById('cpuGauge');
if (!canvas) return;
// Ensure value is a valid number between 0 and 100
value = parseFloat(value) || 0;
value = Math.max(0, Math.min(100, value));
const ctx = canvas.getContext('2d');
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(centerX, centerY) - 10;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background circle
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.strokeStyle = getComputedStyle(document.body).getPropertyValue('--bs-border-color');
ctx.lineWidth = 10;
ctx.stroke();
// Draw value arc
const startAngle = -Math.PI / 2;
const endAngle = startAngle + (Math.PI * 2 * value / 100);
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.strokeStyle = value > 90 ? '#dc3545' : value > 75 ? '#ffc107' : '#198754';
ctx.lineWidth = 10;
ctx.stroke();
// Draw percentage text inside the circle
ctx.font = 'bold 20px Arial';
ctx.fillStyle = getComputedStyle(document.body).getPropertyValue('--bs-body-color');
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`${value.toFixed(1)}%`, centerX, centerY - 10);
// Draw 'CPU USAGE' text below the percentage
ctx.font = '12px Arial';
ctx.fillText('CPU USAGE', centerX, centerY + 15);
}
// Initial CPU gauge draw
window.addEventListener('load', function() {
const cpuValue = parseFloat(document.querySelector('.cpu-gauge canvas')?.getAttribute('data-value') || '0');
drawCpuGauge(cpuValue);
});
</script>
</body>
</html>
-340
View File
@@ -1,340 +0,0 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
function getDatabaseConnection() {
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$conn = new mysqli($servername, $username, $password, $db);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
return $conn;
}
if (!isset($_GET['token'])) {
die("Invalid request. No token provided.");
}
$token = $_GET['token'];
$conn = getDatabaseConnection();
// Get share information
$stmt = $conn->prepare("SELECT * FROM share WHERE token = ? AND expiration > NOW()");
$stmt->bind_param("s", $token);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
die("Invalid or expired share link.");
}
$share = $result->fetch_assoc();
$filePath = $share['file_path'];
$downloadAllowed = $share['download_allowed'] == 1;
$stmt->close();
// Initialize SFTP
$sftp = initializeSFTP($host, $username, $password);
if (!$sftp->stat($filePath)) {
die("File not found: $filePath");
}
$fileName = basename($filePath);
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
$fileSize = $sftp->stat($filePath)['size'];
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
$videoTypes = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
$audioTypes = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
$isImage = in_array($fileExtension, $imageTypes);
$isVideo = in_array($fileExtension, $videoTypes);
$isAudio = in_array($fileExtension, $audioTypes);
if (!$isImage && !$isVideo && !$isAudio) {
die("Unsupported file type");
}
$mimeMap = [
'mp4' => 'video/mp4',
'webm' => 'video/mp4',
'ogg' => 'video/mp4',
'mov' => 'video/mp4',
'avi' => 'video/mp4',
'mkv' => 'video/mp4',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'webp' => 'image/webp',
'svg' => 'image/svg+xml',
'mp3' => 'audio/mpeg',
'wav' => 'audio/wav',
'm4a' => 'audio/mp4',
'flac' => 'audio/flac',
'aac' => 'audio/aac',
];
$mimeType = isset($mimeMap[$fileExtension]) ? $mimeMap[$fileExtension] : 'application/octet-stream';
// If streaming requested, stream file
if (isset($_GET['stream'])) {
// Similar streaming code as in view.php
session_write_close();
while (ob_get_level()) {
ob_end_clean();
}
if (ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
$start = 0;
$end = $fileSize - 1;
$length = $fileSize;
if (isset($_SERVER['HTTP_RANGE'])) {
$rangeHeader = $_SERVER['HTTP_RANGE'];
$matches = [];
if (preg_match('/bytes=(\d+)-(\d*)/', $rangeHeader, $matches)) {
$start = intval($matches[1]);
if (!empty($matches[2])) {
$end = intval($matches[2]);
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
}
}
header("Content-Type: $mimeType");
header("Accept-Ranges: bytes");
header("Content-Length: $length");
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
set_time_limit(0);
$minChunkSize = 64 * 1024;
$maxChunkSize = 2 * 1024 * 1024;
$chunkSize = 256 * 1024;
$currentPosition = $start;
$bytesRemaining = $length;
$lastChunkTime = microtime(true);
try {
while ($bytesRemaining > 0) {
if (connection_aborted() || connection_status() !== CONNECTION_NORMAL) {
break;
}
$readSize = min($chunkSize, $bytesRemaining);
$chunkData = $sftp->get($filePath, false, $currentPosition, $readSize);
if ($chunkData !== false) {
$bytesSent = strlen($chunkData);
echo $chunkData;
$bytesRemaining -= $bytesSent;
$currentPosition += $bytesSent;
if (ob_get_level()) {
ob_flush();
}
flush();
$currentTime = microtime(true);
$timeDiff = $currentTime - $lastChunkTime;
$lastChunkTime = $currentTime;
if ($timeDiff > 0) {
$speed = $bytesSent / $timeDiff;
$chunkSize = min(
max($minChunkSize, $chunkSize * (($speed > 512 * 1024) ? 1.5 : 0.8)),
$maxChunkSize
);
}
} else {
break;
}
usleep(100000);
}
} catch (Exception $e) {
error_log("Shared streaming error: " . $e->getMessage());
}
exit;
}
// Create download handler
if (isset($_GET['download'])) {
if (!$downloadAllowed) {
die("Download not permitted for this share.");
}
// Download file code similar to download.php
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$fileName\"");
header("Content-Length: $fileSize");
$minChunkSize = 64 * 1024;
$maxChunkSize = 2 * 1024 * 1024;
$chunkSize = 256 * 1024;
$currentPosition = 0;
$bytesRemaining = $fileSize;
set_time_limit(0);
while ($bytesRemaining > 0) {
$readSize = min($chunkSize, $bytesRemaining);
$chunkData = $sftp->get($filePath, false, $currentPosition, $readSize);
if ($chunkData !== false) {
$bytesRead = strlen($chunkData);
echo $chunkData;
$bytesRemaining -= $bytesRead;
$currentPosition += $bytesRead;
flush();
} else {
break;
}
}
exit;
}
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>
<!DOCTYPE html>
<html lang="en" data-bs-theme="<?= isset($_COOKIE['theme']) ? $_COOKIE['theme'] : 'light' ?>">
<head>
<title>Shared Media - <?= htmlspecialchars($fileName) ?></title>
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/view.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>Shared Media</h1>
<div class="mb-3">
<span class="badge bg-info">Shared Link</span>
<p class="text-muted small">This link will expire on <?= date('F j, Y, g:i a', strtotime($share['expiration'])) ?></p>
</div>
</header>
<section class="row">
<article class="col-12">
<div class="media-container position-relative">
<?php if ($isImage): ?>
<img src="shared.php?token=<?= urlencode($token) ?>&stream=1" alt="<?= htmlspecialchars($fileName) ?>" class="img-fluid">
<?php elseif ($isVideo): ?>
<video id="videoPlayer" controls autoplay playsinline>
<source src="shared.php?token=<?= urlencode($token) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support this video format.
</video>
<?php if($fileExtension !== 'mp4'): ?>
<div class="alert alert-warning mt-2">
Note: For best results, use MP4 format (H.264 codec). Other formats may not play correctly in all browsers.
</div>
<?php endif; ?>
<?php elseif ($isAudio): ?>
<audio controls autoplay style="width: 80%;">
<source src="shared.php?token=<?= urlencode($token) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support the audio element.
</audio>
<?php endif; ?>
<?php if ($downloadAllowed) : ?>
<a href="shared.php?token=<?= urlencode($token) ?>&download=1" class="btn btn-success m-3">Download</a>
<?php endif; ?>
</div>
<div class="file-info mt-4">
<h4>File Information</h4>
<table class="table table-bordered w-auto mx-auto text-wrap">
<tr>
<th>File Name</th>
<td class="text-break"><?= htmlspecialchars($fileName) ?></td>
</tr>
<tr>
<th>File Type</th>
<td><?= htmlspecialchars(strtoupper($fileExtension)) ?></td>
</tr>
<tr>
<th>File Size</th>
<td><?= formatBytes($fileSize) ?></td>
</tr>
</table>
</div>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-Array" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<script src="../../js/theme.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const mediaElement = document.querySelector('video, audio');
if (mediaElement) {
const savedVolume = localStorage.getItem('mediaVolume');
if (savedVolume !== null) {
mediaElement.volume = parseFloat(savedVolume);
}
mediaElement.addEventListener('volumechange', () => {
localStorage.setItem('mediaVolume', mediaElement.volume);
});
}
});
</script>
</body>
</html>
-130
View File
@@ -1,130 +0,0 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
use phpseclib3\Net\SFTP;
$sftp = initializeSFTP($host, $username, $password);
$currentPath = isset($_POST['currentPath']) ? normalizePath($_POST['currentPath']) : $defaultPath;
if (strpos($currentPath, $defaultPath) !== 0) {
$currentPath = $defaultPath;
}
function createDirectoryRecursive($sftp, $path) {
if ($sftp->is_dir($path)) {
return true;
}
$parent = dirname($path);
if ($parent != '/' && !$sftp->is_dir($parent)) {
createDirectoryRecursive($sftp, $parent);
}
return $sftp->mkdir($path, 0755);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['create_dirs'])) {
$directories = json_decode($_POST['create_dirs'], true);
if (is_array($directories)) {
foreach ($directories as $dir) {
$remoteDirPath = $currentPath . '/' . $dir;
createDirectoryRecursive($sftp, $remoteDirPath);
}
}
}
if (!isset($_FILES['files']) || empty($_FILES['files']['name'][0])) {
echo "No files received or file size exceeds PHP limits.";
http_response_code(400);
exit;
}
if ($_FILES['files']['error'][0] !== 0) {
$error = $_FILES['files']['error'][0];
$errorMessage = "Upload error code: $error";
switch ($error) {
case UPLOAD_ERR_INI_SIZE:
$errorMessage = "File exceeds upload_max_filesize directive in php.ini";
break;
case UPLOAD_ERR_FORM_SIZE:
$errorMessage = "File exceeds MAX_FILE_SIZE directive in the HTML form";
break;
case UPLOAD_ERR_PARTIAL:
$errorMessage = "File was only partially uploaded";
break;
case UPLOAD_ERR_NO_FILE:
$errorMessage = "No file was uploaded";
break;
case UPLOAD_ERR_NO_TMP_DIR:
$errorMessage = "Missing a temporary folder";
break;
case UPLOAD_ERR_CANT_WRITE:
$errorMessage = "Failed to write file to disk";
break;
case UPLOAD_ERR_EXTENSION:
$errorMessage = "A PHP extension stopped the file upload";
break;
}
echo $errorMessage;
http_response_code(400);
exit;
}
$uploadStatus = array();
$anySuccess = false;
foreach ($_FILES['files']['tmp_name'] as $index => $tmpName) {
if (empty($tmpName)) continue;
$fileName = $_FILES['files']['name'][$index];
$relativePath = isset($_POST['paths']) && isset($_POST['paths'][$index]) ? $_POST['paths'][$index] : '';
if (!empty($relativePath)) {
$fileName = basename($relativePath);
$dirPart = dirname($relativePath);
if ($dirPart !== '.' && $dirPart !== '') {
$remoteDirPath = $currentPath . '/' . $dirPart;
if (!$sftp->is_dir($remoteDirPath)) {
createDirectoryRecursive($sftp, $remoteDirPath);
}
$remotePath = $remoteDirPath . '/' . $fileName;
} else {
$remotePath = $currentPath . '/' . $fileName;
}
} else {
$remotePath = $currentPath . '/' . $fileName;
}
if ($sftp->put($remotePath, $tmpName, SFTP::SOURCE_LOCAL_FILE)) {
$uploadStatus[] = "Uploaded: " . ($relativePath ? $relativePath : $fileName);
$anySuccess = true;
} else {
$uploadStatus[] = "Failed to upload: " . ($relativePath ? $relativePath : $fileName);
}
}
if ($anySuccess) {
echo implode("\n", $uploadStatus);
exit;
} else {
echo "Failed to upload any files. Please check SFTP connection and permissions.";
http_response_code(500);
exit;
}
}
echo "No files received or invalid request.";
http_response_code(400);
exit;
-473
View File
@@ -1,473 +0,0 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
ini_set('memory_limit', '512M');
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if (!isset($_GET['file'])) {
die("No file specified");
}
if (isset($_POST['createShare'])) {
$duration = intval($_POST['duration']);
$downloadAllowed = isset($_POST['downloadAllowed']) ? 1 : 0;
$filePath = $_POST['filePath'];
$token = bin2hex(random_bytes(16));
date_default_timezone_set('Europe/Prague');
$expiration = date('Y-m-d H:i:s', strtotime("+$duration hours"));
$conn = getDatabaseConnection();
$stmt = $conn->prepare("INSERT INTO share (token, file_path, expiration, download_allowed) VALUES (?, ?, ?, ?)");
$stmt->bind_param("sssi", $token, $filePath, $expiration, $downloadAllowed);
if ($stmt->execute()) {
$shareUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]" .
dirname($_SERVER['PHP_SELF']) . "/shared.php?token=$token";
$_SESSION['shareCreated'] = true;
$_SESSION['shareUrl'] = $shareUrl;
$_SESSION['shareExpiration'] = $expiration;
header("Location: view.php?file=" . urlencode($filePath) . "&shared=1");
exit;
} else {
$_SESSION['shareError'] = "Failed to create share: " . $conn->error;
header("Location: view.php?file=" . urlencode($filePath) . "&shared=0");
exit;
}
$stmt->close();
$conn->close();
}
$shareCreated = false;
$shareUrl = '';
$shareExpiration = '';
$shareError = '';
if (isset($_GET['shared'])) {
if ($_GET['shared'] == '1' && isset($_SESSION['shareCreated']) && $_SESSION['shareCreated']) {
$shareCreated = true;
$shareUrl = $_SESSION['shareUrl'];
$shareExpiration = $_SESSION['shareExpiration'];
} elseif ($_GET['shared'] == '0' && isset($_SESSION['shareError'])) {
$shareError = $_SESSION['shareError'];
}
}
function getDatabaseConnection() {
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$conn = new mysqli($servername, $username, $password, $db);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
return $conn;
}
$filePath = $_GET['file'];
$fileName = basename($filePath);
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if (!$sftp->stat($filePath)) {
die("File not found: $filePath");
}
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
$videoTypes = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
$audioTypes = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
$isImage = in_array($fileExtension, $imageTypes);
$isVideo = in_array($fileExtension, $videoTypes);
$isAudio = in_array($fileExtension, $audioTypes);
if (!$isImage && !$isVideo && !$isAudio) {
die("Unsupported file type");
}
$fileSize = $sftp->stat($filePath)['size'];
$mimeMap = [
'mp4' => 'video/mp4',
'webm' => 'video/mp4',
'ogg' => 'video/mp4',
'mov' => 'video/mp4',
'avi' => 'video/mp4',
'mkv' => 'video/mp4',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'webp' => 'image/webp',
'svg' => 'image/svg+xml',
'mp3' => 'audio/mpeg',
'wav' => 'audio/wav',
'm4a' => 'audio/mp4',
'flac' => 'audio/flac',
'aac' => 'audio/aac',
];
$mimeType = isset($mimeMap[$fileExtension]) ? $mimeMap[$fileExtension] : 'application/octet-stream';
if (isset($_GET['stream'])) {
// Close session to allow other scripts to run
session_write_close();
// Prevent output buffering
while (ob_get_level()) {
ob_end_clean();
}
// Disable output compression
if (ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
// Initialize range variables
$start = 0;
$end = $fileSize - 1;
$length = $fileSize;
// Handle range requests
if (isset($_SERVER['HTTP_RANGE'])) {
$rangeHeader = $_SERVER['HTTP_RANGE'];
$matches = [];
if (preg_match('/bytes=(\d+)-(\d*)/', $rangeHeader, $matches)) {
$start = intval($matches[1]);
if (!empty($matches[2])) {
$end = intval($matches[2]);
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
}
}
// Set headers for streaming
header("Content-Type: $mimeType");
header("Accept-Ranges: bytes");
header("Content-Length: $length");
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
// Debug headers
if (isset($_GET['debug'])) {
header("X-Stream-Info: Chunked SFTP Streaming");
header("X-File-Path: " . basename($filePath));
header("X-File-Size: $fileSize");
}
// Set timeout to 0 to prevent script termination
set_time_limit(0);
// Chunk settings
$minChunkSize = 64 * 1024; // 64KB minimum
$maxChunkSize = 2 * 1024 * 1024; // 2MB maximum
$chunkSize = 256 * 1024; // Start with 256KB
$currentPosition = $start;
$bytesRemaining = $length;
$lastChunkTime = microtime(true);
try {
while ($bytesRemaining > 0) {
// Check client connection and server status
if (connection_aborted() || connection_status() !== CONNECTION_NORMAL) {
if (isset($_GET['debug'])) {
error_log("Client disconnected at position $currentPosition");
}
break;
}
// Calculate adaptive chunk size
$readSize = min($chunkSize, $bytesRemaining);
// Get chunk from SFTP
$chunkData = $sftp->get($filePath, false, $currentPosition, $readSize);
if ($chunkData !== false) {
$bytesSent = strlen($chunkData);
// Output chunk
echo $chunkData;
$bytesRemaining -= $bytesSent;
$currentPosition += $bytesSent;
// Flush buffers
if (ob_get_level()) {
ob_flush();
}
flush();
// Adaptive chunk sizing based on transfer speed
$currentTime = microtime(true);
$timeDiff = $currentTime - $lastChunkTime;
$lastChunkTime = $currentTime;
if ($timeDiff > 0) {
$speed = $bytesSent / $timeDiff; // bytes/second
$chunkSize = min(
max($minChunkSize, $chunkSize * (($speed > 512 * 1024) ? 1.5 : 0.8)),
$maxChunkSize
);
}
if (isset($_GET['debug'])) {
header("X-Chunk-Size: $bytesSent");
header("X-Position: $currentPosition");
header("X-Remaining: $bytesRemaining");
}
} else {
error_log("SFTP read error at position $currentPosition");
break;
}
// Throttle to prevent CPU overload
usleep(100000); // 100ms
}
} catch (Exception $e) {
error_log("Streaming error: " . $e->getMessage());
if (isset($_GET['debug'])) {
header("X-Stream-Error: " . $e->getMessage());
}
}
if (isset($_GET['debug'])) {
error_log("Streaming completed. Sent $currentPosition bytes of $fileSize");
}
exit;
}
?>
<!DOCTYPE html>
<html lang="en" data-bs-theme="<?= isset($_COOKIE['theme']) ? $_COOKIE['theme'] : 'light' ?>">
<head>
<title>Media Viewer - <?= htmlspecialchars($fileName) ?></title>
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/view.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>Media Viewer</h1>
<div class="mb-3 p-3">
<a href="index.php?path=<?= urlencode(dirname($filePath)) ?>" class="btn btn-primary">Back to Files</a>
</div>
</header>
<section class="row">
<article class="col-12">
<div class="media-container position-relative">
<?php if ($isImage): ?>
<img src="view.php?file=<?= urlencode($filePath) ?>&stream=1" alt="<?= htmlspecialchars($fileName) ?>" class="img-fluid">
<?php elseif ($isVideo): ?>
<video id="videoPlayer" controls autoplay playsinline>
<source src="view.php?file=<?= urlencode($filePath) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support this video format. Try downloading the file instead.
</video>
<?php if($fileExtension !== 'mp4'): ?>
<div class="alert alert-warning mt-2">
Note: For best results, use MP4 format (H.264 codec). Other formats may not play correctly in all browsers.
</div>
<?php endif; ?>
<?php elseif ($isAudio): ?>
<audio controls autoplay style="width: 80%;">
<source src="view.php?file=<?= urlencode($filePath) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support the audio element.
</audio>
<?php endif; ?>
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
<?php endif; ?>
<!-- Add Share Button -->
<button type="button" class="btn btn-info m-3" data-bs-toggle="modal" data-bs-target="#shareModal">
<i class="bi bi-share"></i> Share
</button>
</div>
<!-- Share success message -->
<?php if ($shareCreated): ?>
<div class="alert alert-success mt-3">
<p>Share link created successfully!</p>
<div class="input-group mb-3">
<input type="text" class="form-control" id="shareUrl" value="<?= htmlspecialchars($shareUrl) ?>" readonly>
<button class="btn btn-outline-secondary" type="button" onclick="copyShareUrl()">
<i class="bi bi-clipboard"></i> Copy
</button>
</div>
<p class="small text-muted">This link will expire on <?= date('F j, Y, g:i a', strtotime($shareExpiration)) ?></p>
</div>
<?php endif; ?>
<?php if ($shareError): ?>
<div class="alert alert-danger mt-3">
<?= htmlspecialchars($shareError) ?>
</div>
<?php endif; ?>
<div class="file-info mt-4">
<h4>File Information</h4>
<table class="table table-bordered w-auto mx-auto text-wrap">
<tr>
<th>File Name</th>
<td class="text-break"><?= htmlspecialchars($fileName) ?></td>
</tr>
<tr>
<th>File Type</th>
<td><?= htmlspecialchars(strtoupper($fileExtension)) ?></td>
</tr>
<tr>
<th>MIME Type</th>
<td class="text-break"><?= htmlspecialchars($mimeType) ?></td>
</tr>
<tr>
<th>File Size</th>
<td><?= formatBytes($fileSize) ?></td>
</tr>
</table>
</div>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-Array" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<!-- Share Modal -->
<div class="modal fade" id="shareModal" tabindex="-1" aria-labelledby="shareModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="shareModalLabel">Share "<?= htmlspecialchars($fileName) ?>"</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="post" action="">
<input type="hidden" name="filePath" value="<?= htmlspecialchars($filePath) ?>">
<div class="mb-3">
<label for="duration" class="form-label">Link expires after</label>
<select class="form-select" id="duration" name="duration">
<option value="1">1 hour</option>
<option value="6">6 hours</option>
<option value="24" selected>1 day</option>
<option value="168">1 week</option>
<option value="720">30 days</option>
</select>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="downloadAllowed" name="downloadAllowed">
<label class="form-check-label" for="downloadAllowed">Allow downloading</label>
</div>
<div class="text-end">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" name="createShare" class="btn btn-primary">Create Share Link</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
function copyShareUrl() {
const shareUrl = document.getElementById('shareUrl');
shareUrl.select();
document.execCommand('copy');
// Show feedback
const button = shareUrl.nextElementSibling;
const originalText = button.innerHTML;
button.innerHTML = '<i class="bi bi-check"></i> Copied!';
setTimeout(() => {
button.innerHTML = originalText;
}, 2000);
}
</script>
<script src="../../js/theme.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const mediaElement = document.querySelector('video, audio');
if (mediaElement) {
// Restore volume from localStorage
const savedVolume = localStorage.getItem('mediaVolume');
if (savedVolume !== null) {
mediaElement.volume = parseFloat(savedVolume);
}
// Save volume to localStorage when it changes
mediaElement.addEventListener('volumechange', () => {
localStorage.setItem('mediaVolume', mediaElement.volume);
});
}
});
</script>
</body>
</html>
<?php
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>
-83
View File
@@ -1,83 +0,0 @@
<?php
session_start();
if (isset($_POST['uname'], $_POST['pswd'])) {
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$uname = htmlspecialchars($_POST['uname']);
$pswd = htmlspecialchars($_POST['pswd']);
$conn = new mysqli($servername, $username, $password, $db);
$conn->set_charset("utf8");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT * FROM users WHERE uname=?";
$stmt = $conn->prepare($sql);
if (!$stmt) {
$_SESSION['login_error'] = "Database error. Please try again.";
$conn->close();
header("Location: ../index.php");
exit();
}
$stmt->bind_param("s", $uname);
$stmt->execute();
$result = $stmt->get_result();
if ($result && $result->num_rows > 0) {
$login_successful = false;
while ($row = $result->fetch_assoc()) {
$hash = $row["pswd"];
$admin = (bool)$row["admin"];
$defPath = $row["defPath"];
$delPer = $row["delPer"];
$downPer = $row["downPer"];
$upPer = $row["upPer"];
if (password_verify($pswd, $hash)) {
// Login successful - store info in session
$_SESSION['uname'] = $uname;
$_SESSION['admin'] = $admin;
$_SESSION['defPath'] = $defPath;
$_SESSION['downPer'] = $downPer;
$_SESSION['delPer'] = $delPer;
$_SESSION['upPer'] = $upPer;
$login_successful = true;
break;
}
}
$result->free_result();
$stmt->close();
$conn->close();
if ($login_successful) {
header("Location: ftp/index.php");
exit();
} else {
// If password verification fails
$_SESSION['login_error'] = "Invalid username or password.";
header("Location: ../index.php");
exit();
}
} else {
// If no user is found
$result->free_result();
$stmt->close();
$conn->close();
$_SESSION['login_error'] = "Invalid username or password.";
header("Location: ../index.php");
exit();
}
} else {
// If form fields are not set
$_SESSION['login_error'] = "Please fill in all fields.";
header("Location: ../index.php");
exit();
}
?>
-23
View File
@@ -1,23 +0,0 @@
<?php
session_start();
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
session_destroy();
header("Location: ../index.php");
exit();
?>
-12057
View File
File diff suppressed because it is too large Load Diff
-47
View File
@@ -1,47 +0,0 @@
html, body {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
}
main {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
padding-top: 7%;
}
.fixed {
-ms-flex: 0 0 300px;
flex: 0 0 300px;
min-width: 300px;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
z-index: 1000;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
-61
View File
@@ -1,61 +0,0 @@
/* Preloader styles */
.preloader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
transition: opacity 0.5s;
}
.theme-light .preloader {
background-color: #ffffff;
}
.theme-dark .preloader {
background-color: #212529;
}
.spinner {
width: 50px;
height: 50px;
border: 5px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: #0d6efd;
animation: spin 1s ease-in-out infinite;
}
.theme-dark .spinner {
border: 5px solid rgba(255, 255, 255, 0.1);
border-top-color: #0d6efd;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.fade-in {
opacity: 0;
animation: fadeIn 0.5s forwards;
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
.content-wrapper {
opacity: 0;
transition: opacity 0.3s;
}
.content-wrapper.visible {
opacity: 1;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

-79
View File
@@ -1,79 +0,0 @@
<?php
session_start();
if (empty($_SESSION)) {
if (session_status() === PHP_SESSION_ACTIVE) {
session_destroy();
}
} elseif (isset($_SESSION["uname"])) {
header("location: content/ftp/index.php");
exit();
} elseif (isset($_SESSION['login_error'])) {
echo "<script>alert('" . $_SESSION['login_error'] . "');</script>";
unset($_SESSION['login_error']);
}
?>
<!DOCTYPE html>
<html lang="cz">
<head>
<title>Login</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/index.css">
<script src="js/bootstrap.bundle.js"></script>
</head>
<body class="container-fluid text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<header class="row border-bottom m-5">
<h1>USB RAID Array</h1>
</header>
<main>
<section class="row m-3">
<article class="col border border-2 border-primary rounded p-2 fixed">
<form action="content/login.php" method="post">
<div class="mb-3">
<label for="uname">Username:</label>
<br>
<input type="text" name="uname" class="form-control" required>
</div>
<div class="mb-3">
<label for="pswd">Password:</label>
<br>
<input type="password" name="pswd" class="form-control" required>
</div>
<div class="mb-3">
<input type="submit" class="btn btn-primary" value="Login">
</div>
</form>
</article>
</section>
</main>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-Array" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
<script src="js/theme.js"></script>
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-4447
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-4494
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-40
View File
@@ -1,40 +0,0 @@
document.addEventListener('DOMContentLoaded', function() {
const themeToggle = document.getElementById('themeToggle');
const html = document.documentElement;
const themeText = document.getElementById('themeText');
const themeIcon = themeToggle.querySelector('.bi');
function setTheme(theme) {
html.setAttribute('data-bs-theme', theme);
document.body.classList.remove('theme-light', 'theme-dark');
document.body.classList.add('theme-' + theme);
localStorage.setItem('theme', theme);
if (theme === 'dark') {
themeText.textContent = 'Light Mode';
themeIcon.className = 'bi bi-sun';
themeToggle.classList.remove('btn-dark');
themeToggle.classList.add('btn-light');
} else {
themeText.textContent = 'Dark Mode';
themeIcon.className = 'bi bi-moon';
themeToggle.classList.remove('btn-light');
themeToggle.classList.add('btn-dark');
}
}
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme) {
setTheme(savedTheme);
} else {
setTheme(prefersDark ? 'dark' : 'light');
}
themeToggle.addEventListener('click', function() {
const currentTheme = html.getAttribute('data-bs-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
});
});
-25
View File
@@ -1,25 +0,0 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInita702940bbf24a822224611db5c22942b::getLoader();
-579
View File
@@ -1,579 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
private $apcuPrefix;
/**
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}
-359
View File
@@ -1,359 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
if (self::$installed !== array()) {
$installed[] = self::$installed;
}
return $installed;
}
}
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-10
View File
@@ -1,10 +0,0 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);
-10
View File
@@ -1,10 +0,0 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
);
-9
View File
@@ -1,9 +0,0 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);
-11
View File
@@ -1,11 +0,0 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'phpseclib3\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
'ParagonIE\\ConstantTime\\' => array($vendorDir . '/paragonie/constant_time_encoding/src'),
);
-50
View File
@@ -1,50 +0,0 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInita702940bbf24a822224611db5c22942b
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInita702940bbf24a822224611db5c22942b', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInita702940bbf24a822224611db5c22942b', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInita702940bbf24a822224611db5c22942b::getInitializer($loader));
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInita702940bbf24a822224611db5c22942b::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
}
return $loader;
}
}
-48
View File
@@ -1,48 +0,0 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInita702940bbf24a822224611db5c22942b
{
public static $files = array (
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
);
public static $prefixLengthsPsr4 = array (
'p' =>
array (
'phpseclib3\\' => 11,
),
'P' =>
array (
'ParagonIE\\ConstantTime\\' => 23,
),
);
public static $prefixDirsPsr4 = array (
'phpseclib3\\' =>
array (
0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
),
'ParagonIE\\ConstantTime\\' =>
array (
0 => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInita702940bbf24a822224611db5c22942b::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInita702940bbf24a822224611db5c22942b::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInita702940bbf24a822224611db5c22942b::$classMap;
}, null, ClassLoader::class);
}
}
-242
View File
@@ -1,242 +0,0 @@
{
"packages": [
{
"name": "paragonie/constant_time_encoding",
"version": "v3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512",
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512",
"shasum": ""
},
"require": {
"php": "^8"
},
"require-dev": {
"phpunit/phpunit": "^9",
"vimeo/psalm": "^4|^5"
},
"time": "2024-05-08T12:36:18+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base16",
"base32",
"base32_decode",
"base32_encode",
"base64",
"base64_decode",
"base64_encode",
"bin2hex",
"encoding",
"hex",
"hex2bin",
"rfc4648"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"install-path": "../paragonie/constant_time_encoding"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.100",
"version_normalized": "9.99.100.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
"shasum": ""
},
"require": {
"php": ">= 7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"time": "2020-10-15T08:29:30+00:00",
"type": "library",
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/random_compat/issues",
"source": "https://github.com/paragonie/random_compat"
},
"install-path": "../paragonie/random_compat"
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.43",
"version_normalized": "3.0.43.0",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1|^2|^3",
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
"php": ">=5.6.1"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"suggest": {
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
"time": "2024-12-14T21:12:59+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib3\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"homepage": "http://phpseclib.sourceforge.net",
"keywords": [
"BigInteger",
"aes",
"asn.1",
"asn1",
"blowfish",
"crypto",
"cryptography",
"encryption",
"rsa",
"security",
"sftp",
"signature",
"signing",
"ssh",
"twofish",
"x.509",
"x509"
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
},
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"install-path": "../phpseclib/phpseclib"
}
],
"dev": true,
"dev-package-names": []
}
-50
View File
@@ -1,50 +0,0 @@
<?php return array(
'root' => array(
'name' => '__root__',
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => null,
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => null,
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'paragonie/constant_time_encoding' => array(
'pretty_version' => 'v3.0.0',
'version' => '3.0.0.0',
'reference' => 'df1e7fde177501eee2037dd159cf04f5f301a512',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/constant_time_encoding',
'aliases' => array(),
'dev_requirement' => false,
),
'paragonie/random_compat' => array(
'pretty_version' => 'v9.99.100',
'version' => '9.99.100.0',
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/random_compat',
'aliases' => array(),
'dev_requirement' => false,
),
'phpseclib/phpseclib' => array(
'pretty_version' => '3.0.43',
'version' => '3.0.43.0',
'reference' => '709ec107af3cb2f385b9617be72af8cf62441d02',
'type' => 'library',
'install_path' => __DIR__ . '/../phpseclib/phpseclib',
'aliases' => array(),
'dev_requirement' => false,
),
),
);
-26
View File
@@ -1,26 +0,0 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 80000)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}
@@ -1,48 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 - 2022 Paragon Initiative Enterprises
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
This library was based on the work of Steve "Sc00bz" Thomas.
------------------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2014 Steve Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,88 +0,0 @@
# Constant-Time Encoding
[![Build Status](https://github.com/paragonie/constant_time_encoding/actions/workflows/ci.yml/badge.svg)](https://github.com/paragonie/constant_time_encoding/actions)
[![Static Analysis](https://github.com/paragonie/constant_time_encoding/actions/workflows/psalm.yml/badge.svg)](https://github.com/paragonie/constant_time_encoding/actions)
[![Latest Stable Version](https://poser.pugx.org/paragonie/constant_time_encoding/v/stable)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![Latest Unstable Version](https://poser.pugx.org/paragonie/constant_time_encoding/v/unstable)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![License](https://poser.pugx.org/paragonie/constant_time_encoding/license)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![Downloads](https://img.shields.io/packagist/dt/paragonie/constant_time_encoding.svg)](https://packagist.org/packages/paragonie/constant_time_encoding)
Based on the [constant-time base64 implementation made by Steve "Sc00bz" Thomas](https://github.com/Sc00bz/ConstTimeEncoding),
this library aims to offer character encoding functions that do not leak
information about what you are encoding/decoding via processor cache
misses. Further reading on [cache-timing attacks](http://blog.ircmaxell.com/2014/11/its-all-about-time.html).
Our fork offers the following enhancements:
* `mbstring.func_overload` resistance
* Unit tests
* Composer- and Packagist-ready
* Base16 encoding
* Base32 encoding
* Uses `pack()` and `unpack()` instead of `chr()` and `ord()`
## PHP Version Requirements
Version 3 of this library should work on **PHP 8** or newer.
Version 2 of this library should work on **PHP 7** or newer. See [the v2.x branch](https://github.com/paragonie/constant_time_encoding/tree/v2.x).
For PHP 5 support, see [the v1.x branch](https://github.com/paragonie/constant_time_encoding/tree/v1.x).
If you are adding this as a dependency to a project intended to work on PHP 5 through 8.4, please set the required version to `^1|^2|^3`.
## How to Install
```sh
composer require paragonie/constant_time_encoding
```
## How to Use
```php
use ParagonIE\ConstantTime\Encoding;
// possibly (if applicable):
// require 'vendor/autoload.php';
$data = random_bytes(32);
echo Encoding::base64Encode($data), "\n";
echo Encoding::base32EncodeUpper($data), "\n";
echo Encoding::base32Encode($data), "\n";
echo Encoding::hexEncode($data), "\n";
echo Encoding::hexEncodeUpper($data), "\n";
```
Example output:
```
1VilPkeVqirlPifk5scbzcTTbMT2clp+Zkyv9VFFasE=
2VMKKPSHSWVCVZJ6E7SONRY3ZXCNG3GE6ZZFU7TGJSX7KUKFNLAQ====
2vmkkpshswvcvzj6e7sonry3zxcng3ge6zzfu7tgjsx7kukfnlaq====
d558a53e4795aa2ae53e27e4e6c71bcdc4d36cc4f6725a7e664caff551456ac1
D558A53E4795AA2AE53E27E4E6C71BDCC4D36CC4F6725A7E664CAFF551456AC1
```
If you only need a particular variant, you can just reference the
required class like so:
```php
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Base32;
$data = random_bytes(32);
echo Base64::encode($data), "\n";
echo Base32::encode($data), "\n";
```
Example output:
```
1VilPkeVqirlPifk5scbzcTTbMT2clp+Zkyv9VFFasE=
2vmkkpshswvcvzj6e7sonry3zxcng3ge6zzfu7tgjsx7kukfnlaq====
```
## Support Contracts
If your company uses this library in their products or services, you may be
interested in [purchasing a support contract from Paragon Initiative Enterprises](https://paragonie.com/enterprise).
@@ -1,56 +0,0 @@
{
"name": "paragonie/constant_time_encoding",
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base64",
"encoding",
"rfc4648",
"base32",
"base16",
"hex",
"bin2hex",
"hex2bin",
"base64_encode",
"base64_decode",
"base32_encode",
"base32_decode"
],
"license": "MIT",
"type": "library",
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"support": {
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"email": "info@paragonie.com",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"require": {
"php": "^8"
},
"require-dev": {
"phpunit/phpunit": "^9",
"vimeo/psalm": "^4|^5"
},
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"ParagonIE\\ConstantTime\\Tests\\": "tests/"
}
}
}
@@ -1,541 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use InvalidArgumentException;
use RangeException;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base32
* [A-Z][2-7]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base32 implements EncoderInterface
{
/**
* Decode a Base32-encoded string into raw binary
*
* @param string $encodedString
* @param bool $strictPadding
* @return string
*/
public static function decode(
#[\SensitiveParameter]
string $encodedString,
bool $strictPadding = false
): string {
return static::doDecode($encodedString, false, $strictPadding);
}
/**
* Decode an uppercase Base32-encoded string into raw binary
*
* @param string $src
* @param bool $strictPadding
* @return string
*/
public static function decodeUpper(
#[\SensitiveParameter]
string $src,
bool $strictPadding = false
): string {
return static::doDecode($src, true, $strictPadding);
}
/**
* Encode into Base32 (RFC 4648)
*
* @param string $binString
* @return string
* @throws TypeError
*/
public static function encode(
#[\SensitiveParameter]
string $binString
): string {
return static::doEncode($binString, false, true);
}
/**
* Encode into Base32 (RFC 4648)
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUnpadded(
#[\SensitiveParameter]
string $src
): string {
return static::doEncode($src, false, false);
}
/**
* Encode into uppercase Base32 (RFC 4648)
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUpper(
#[\SensitiveParameter]
string $src
): string {
return static::doEncode($src, true, true);
}
/**
* Encode into uppercase Base32 (RFC 4648)
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUpperUnpadded(
#[\SensitiveParameter]
string $src
): string {
return static::doEncode($src, true, false);
}
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* @param int $src
* @return int
*/
protected static function decode5Bits(int $src): int
{
$ret = -1;
// if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96);
// if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
$ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* Uppercase variant.
*
* @param int $src
* @return int
*/
protected static function decode5BitsUpper(int $src): int
{
$ret = -1;
// if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
$ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode5Bits(int $src): string
{
$diff = 0x61;
// if ($src > 25) $ret -= 72;
$diff -= ((25 - $src) >> 8) & 73;
return \pack('C', $src + $diff);
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* Uppercase variant.
*
* @param int $src
* @return string
*/
protected static function encode5BitsUpper(int $src): string
{
$diff = 0x41;
// if ($src > 25) $ret -= 40;
$diff -= ((25 - $src) >> 8) & 41;
return \pack('C', $src + $diff);
}
/**
* @param string $encodedString
* @param bool $upper
* @return string
*/
public static function decodeNoPadding(
#[\SensitiveParameter]
string $encodedString,
bool $upper = false
): string {
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}
if (($srcLen & 7) === 0) {
for ($j = 0; $j < 7 && $j < $srcLen; ++$j) {
if ($encodedString[$srcLen - $j - 1] === '=') {
throw new InvalidArgumentException(
"decodeNoPadding() doesn't tolerate padding"
);
}
}
}
return static::doDecode(
$encodedString,
$upper,
true
);
}
/**
* Base32 decoding
*
* @param string $src
* @param bool $upper
* @param bool $strictPadding
* @return string
*
* @throws TypeError
*/
protected static function doDecode(
#[\SensitiveParameter]
string $src,
bool $upper = false,
bool $strictPadding = false
): string {
// We do this to reduce code duplication:
$method = $upper
? 'decode5BitsUpper'
: 'decode5Bits';
// Remove padding
$srcLen = Binary::safeStrlen($src);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 7) === 0) {
for ($j = 0; $j < 7; ++$j) {
if ($src[$srcLen - 1] === '=') {
$srcLen--;
} else {
break;
}
}
}
if (($srcLen & 7) === 1) {
throw new RangeException(
'Incorrect padding'
);
}
} else {
$src = \rtrim($src, '=');
$srcLen = Binary::safeStrlen($src);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 8 <= $srcLen; $i += 8) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8));
/** @var int $c0 */
$c0 = static::$method($chunk[1]);
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
/** @var int $c4 */
$c4 = static::$method($chunk[5]);
/** @var int $c5 */
$c5 = static::$method($chunk[6]);
/** @var int $c6 */
$c6 = static::$method($chunk[7]);
/** @var int $c7 */
$c7 = static::$method($chunk[8]);
$dest .= \pack(
'CCCCC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
(($c3 << 4) | ($c4 >> 1) ) & 0xff,
(($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff,
(($c6 << 5) | ($c7 ) ) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
/** @var int $c0 */
$c0 = static::$method($chunk[1]);
if ($i + 6 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
/** @var int $c4 */
$c4 = static::$method($chunk[5]);
/** @var int $c5 */
$c5 = static::$method($chunk[6]);
/** @var int $c6 */
$c6 = static::$method($chunk[7]);
$dest .= \pack(
'CCCC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
(($c3 << 4) | ($c4 >> 1) ) & 0xff,
(($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8;
if ($strictPadding) {
$err |= ($c6 << 5) & 0xff;
}
} elseif ($i + 5 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
/** @var int $c4 */
$c4 = static::$method($chunk[5]);
/** @var int $c5 */
$c5 = static::$method($chunk[6]);
$dest .= \pack(
'CCCC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
(($c3 << 4) | ($c4 >> 1) ) & 0xff,
(($c4 << 7) | ($c5 << 2) ) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8;
} elseif ($i + 4 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
/** @var int $c4 */
$c4 = static::$method($chunk[5]);
$dest .= \pack(
'CCC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
(($c3 << 4) | ($c4 >> 1) ) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8;
if ($strictPadding) {
$err |= ($c4 << 7) & 0xff;
}
} elseif ($i + 3 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
$dest .= \pack(
'CC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
if ($strictPadding) {
$err |= ($c3 << 4) & 0xff;
}
} elseif ($i + 2 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
$dest .= \pack(
'CC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) ) & 0xff
);
$err |= ($c0 | $c1 | $c2) >> 8;
if ($strictPadding) {
$err |= ($c2 << 6) & 0xff;
}
} elseif ($i + 1 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
$dest .= \pack(
'C',
(($c0 << 3) | ($c1 >> 2) ) & 0xff
);
$err |= ($c0 | $c1) >> 8;
if ($strictPadding) {
$err |= ($c1 << 6) & 0xff;
}
} else {
$dest .= \pack(
'C',
(($c0 << 3) ) & 0xff
);
$err |= ($c0) >> 8;
}
}
$check = ($err === 0);
if (!$check) {
throw new RangeException(
'Base32::doDecode() only expects characters in the correct base32 alphabet'
);
}
return $dest;
}
/**
* Base32 Encoding
*
* @param string $src
* @param bool $upper
* @param bool $pad
* @return string
* @throws TypeError
*/
protected static function doEncode(
#[\SensitiveParameter]
string $src,
bool $upper = false,
$pad = true
): string {
// We do this to reduce code duplication:
$method = $upper
? 'encode5BitsUpper'
: 'encode5Bits';
$dest = '';
$srcLen = Binary::safeStrlen($src);
// Main loop (no padding):
for ($i = 0; $i + 5 <= $srcLen; $i += 5) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$b3 = $chunk[4];
$b4 = $chunk[5];
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
static::$method((($b1 >> 1) ) & 31) .
static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
static::$method((($b3 >> 2) ) & 31) .
static::$method((($b3 << 3) | ($b4 >> 5)) & 31) .
static::$method( $b4 & 31);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 3 < $srcLen) {
$b1 = $chunk[2];
$b2 = $chunk[3];
$b3 = $chunk[4];
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
static::$method((($b1 >> 1) ) & 31) .
static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
static::$method((($b3 >> 2) ) & 31) .
static::$method((($b3 << 3) ) & 31);
if ($pad) {
$dest .= '=';
}
} elseif ($i + 2 < $srcLen) {
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
static::$method((($b1 >> 1) ) & 31) .
static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
static::$method((($b2 << 1) ) & 31);
if ($pad) {
$dest .= '===';
}
} elseif ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
static::$method((($b1 >> 1) ) & 31) .
static::$method((($b1 << 4) ) & 31);
if ($pad) {
$dest .= '====';
}
} else {
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method( ($b0 << 2) & 31);
if ($pad) {
$dest .= '======';
}
}
}
return $dest;
}
}
@@ -1,111 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base32Hex
* [0-9][A-V]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base32Hex extends Base32
{
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* @param int $src
* @return int
*/
protected static function decode5Bits(int $src): int
{
$ret = -1;
// if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47);
// if ($src > 0x60 && $src < 0x77) ret += $src - 0x61 + 10 + 1; // -86
$ret += (((0x60 - $src) & ($src - 0x77)) >> 8) & ($src - 86);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* @param int $src
* @return int
*/
protected static function decode5BitsUpper(int $src): int
{
$ret = -1;
// if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47);
// if ($src > 0x40 && $src < 0x57) ret += $src - 0x41 + 10 + 1; // -54
$ret += (((0x40 - $src) & ($src - 0x57)) >> 8) & ($src - 54);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode5Bits(int $src): string
{
$src += 0x30;
// if ($src > 0x39) $src += 0x61 - 0x3a; // 39
$src += ((0x39 - $src) >> 8) & 39;
return \pack('C', $src);
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* Uppercase variant.
*
* @param int $src
* @return string
*/
protected static function encode5BitsUpper(int $src): string
{
$src += 0x30;
// if ($src > 0x39) $src += 0x41 - 0x3a; // 7
$src += ((0x39 - $src) >> 8) & 7;
return \pack('C', $src);
}
}
@@ -1,319 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use InvalidArgumentException;
use RangeException;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base64
* [A-Z][a-z][0-9]+/
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64 implements EncoderInterface
{
/**
* Encode into Base64
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $binString
* @return string
*
* @throws TypeError
*/
public static function encode(
#[\SensitiveParameter]
string $binString
): string {
return static::doEncode($binString, true);
}
/**
* Encode into Base64, no = padding
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
*
* @throws TypeError
*/
public static function encodeUnpadded(
#[\SensitiveParameter]
string $src
): string {
return static::doEncode($src, false);
}
/**
* @param string $src
* @param bool $pad Include = padding?
* @return string
*
* @throws TypeError
*/
protected static function doEncode(
#[\SensitiveParameter]
string $src,
bool $pad = true
): string {
$dest = '';
$srcLen = Binary::safeStrlen($src);
// Main loop (no padding):
for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, 3));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
static::encode6Bits( $b0 >> 2 ) .
static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
static::encode6Bits( $b2 & 63);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
static::encode6Bits($b0 >> 2) .
static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
static::encode6Bits(($b1 << 2) & 63);
if ($pad) {
$dest .= '=';
}
} else {
$dest .=
static::encode6Bits( $b0 >> 2) .
static::encode6Bits(($b0 << 4) & 63);
if ($pad) {
$dest .= '==';
}
}
}
return $dest;
}
/**
* decode from base64 into binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $encodedString
* @param bool $strictPadding
* @return string
*
* @throws RangeException
* @throws TypeError
*/
public static function decode(
#[\SensitiveParameter]
string $encodedString,
bool $strictPadding = false
): string {
// Remove padding
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 3) === 0) {
if ($encodedString[$srcLen - 1] === '=') {
$srcLen--;
if ($encodedString[$srcLen - 1] === '=') {
$srcLen--;
}
}
}
if (($srcLen & 3) === 1) {
throw new RangeException(
'Incorrect padding'
);
}
if ($encodedString[$srcLen - 1] === '=') {
throw new RangeException(
'Incorrect padding'
);
}
} else {
$encodedString = \rtrim($encodedString, '=');
$srcLen = Binary::safeStrlen($encodedString);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, 4));
$c0 = static::decode6Bits($chunk[1]);
$c1 = static::decode6Bits($chunk[2]);
$c2 = static::decode6Bits($chunk[3]);
$c3 = static::decode6Bits($chunk[4]);
$dest .= \pack(
'CCC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff),
((($c2 << 6) | $c3 ) & 0xff)
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, $srcLen - $i));
$c0 = static::decode6Bits($chunk[1]);
if ($i + 2 < $srcLen) {
$c1 = static::decode6Bits($chunk[2]);
$c2 = static::decode6Bits($chunk[3]);
$dest .= \pack(
'CC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff)
);
$err |= ($c0 | $c1 | $c2) >> 8;
if ($strictPadding) {
$err |= ($c2 << 6) & 0xff;
}
} elseif ($i + 1 < $srcLen) {
$c1 = static::decode6Bits($chunk[2]);
$dest .= \pack(
'C',
((($c0 << 2) | ($c1 >> 4)) & 0xff)
);
$err |= ($c0 | $c1) >> 8;
if ($strictPadding) {
$err |= ($c1 << 4) & 0xff;
}
} elseif ($strictPadding) {
$err |= 1;
}
}
$check = ($err === 0);
if (!$check) {
throw new RangeException(
'Base64::decode() only expects characters in the correct base64 alphabet'
);
}
return $dest;
}
/**
* @param string $encodedString
* @return string
*/
public static function decodeNoPadding(
#[\SensitiveParameter]
string $encodedString
): string {
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}
if (($srcLen & 3) === 0) {
// If $strLen is not zero, and it is divisible by 4, then it's at least 4.
if ($encodedString[$srcLen - 1] === '=' || $encodedString[$srcLen - 2] === '=') {
throw new InvalidArgumentException(
"decodeNoPadding() doesn't tolerate padding"
);
}
}
return static::decode(
$encodedString,
true
);
}
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] + /
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
// if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
// if ($src == 0x2b) $ret += 62 + 1;
$ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63;
// if ($src == 0x2f) ret += 63 + 1;
$ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64;
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$diff = 0x41;
// if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
$diff += ((25 - $src) >> 8) & 6;
// if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
$diff -= ((51 - $src) >> 8) & 75;
// if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15
$diff -= ((61 - $src) >> 8) & 15;
// if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3
$diff += ((62 - $src) >> 8) & 3;
return \pack('C', $src + $diff);
}
}
@@ -1,88 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base64DotSlash
* ./[A-Z][a-z][0-9]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64DotSlash extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* ./ [A-Z] [a-z] [0-9]
* 0x2e-0x2f, 0x41-0x5a, 0x61-0x7a, 0x30-0x39
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x2d && $src < 0x30) ret += $src - 0x2e + 1; // -45
$ret += (((0x2d - $src) & ($src - 0x30)) >> 8) & ($src - 45);
// if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 2 + 1; // -62
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 62);
// if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 28 + 1; // -68
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 68);
// if ($src > 0x2f && $src < 0x3a) ret += $src - 0x30 + 54 + 1; // 7
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 7);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$src += 0x2e;
// if ($src > 0x2f) $src += 0x41 - 0x30; // 17
$src += ((0x2f - $src) >> 8) & 17;
// if ($src > 0x5a) $src += 0x61 - 0x5b; // 6
$src += ((0x5a - $src) >> 8) & 6;
// if ($src > 0x7a) $src += 0x30 - 0x7b; // -75
$src -= ((0x7a - $src) >> 8) & 75;
return \pack('C', $src);
}
}
@@ -1,82 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base64DotSlashOrdered
* ./[0-9][A-Z][a-z]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64DotSlashOrdered extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [.-9] [A-Z] [a-z]
* 0x2e-0x39, 0x41-0x5a, 0x61-0x7a
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x2d && $src < 0x3a) ret += $src - 0x2e + 1; // -45
$ret += (((0x2d - $src) & ($src - 0x3a)) >> 8) & ($src - 45);
// if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 12 + 1; // -52
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 52);
// if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 38 + 1; // -58
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 58);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$src += 0x2e;
// if ($src > 0x39) $src += 0x41 - 0x3a; // 7
$src += ((0x39 - $src) >> 8) & 7;
// if ($src > 0x5a) $src += 0x61 - 0x5b; // 6
$src += ((0x5a - $src) >> 8) & 6;
return \pack('C', $src);
}
}
@@ -1,95 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base64UrlSafe
* [A-Z][a-z][0-9]\-_
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64UrlSafe extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] - _
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2d, 0x5f
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
// if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
// if ($src == 0x2c) $ret += 62 + 1;
$ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63;
// if ($src == 0x5f) ret += 63 + 1;
$ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64;
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$diff = 0x41;
// if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
$diff += ((25 - $src) >> 8) & 6;
// if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
$diff -= ((51 - $src) >> 8) & 75;
// if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13
$diff -= ((61 - $src) >> 8) & 13;
// if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3
$diff += ((62 - $src) >> 8) & 49;
return \pack('C', $src + $diff);
}
}
@@ -1,93 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Binary
*
* Binary string operators that don't choke on
* mbstring.func_overload
*
* @package ParagonIE\ConstantTime
*/
abstract class Binary
{
/**
* Safe string length
*
* @ref mbstring.func_overload
*
* @param string $str
* @return int
*/
public static function safeStrlen(
#[\SensitiveParameter]
string $str
): int {
if (\function_exists('mb_strlen')) {
// mb_strlen in PHP 7.x can return false.
/** @psalm-suppress RedundantCast */
return (int) \mb_strlen($str, '8bit');
} else {
return \strlen($str);
}
}
/**
* Safe substring
*
* @ref mbstring.func_overload
*
* @staticvar boolean $exists
* @param string $str
* @param int $start
* @param ?int $length
* @return string
*
* @throws TypeError
*/
public static function safeSubstr(
#[\SensitiveParameter]
string $str,
int $start = 0,
?int $length = null
): string {
if ($length === 0) {
return '';
}
if (\function_exists('mb_substr')) {
return \mb_substr($str, $start, $length, '8bit');
}
// Unlike mb_substr(), substr() doesn't accept NULL for length
if ($length !== null) {
return \substr($str, $start, $length);
} else {
return \substr($str, $start);
}
}
}
@@ -1,52 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Interface EncoderInterface
* @package ParagonIE\ConstantTime
*/
interface EncoderInterface
{
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $binString (raw binary)
* @return string
*/
public static function encode(string $binString): string;
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $encodedString
* @param bool $strictPadding Error on invalid padding
* @return string (raw binary)
*/
public static function decode(string $encodedString, bool $strictPadding = false): string;
}
@@ -1,298 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Encoding
* @package ParagonIE\ConstantTime
*/
abstract class Encoding
{
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32Encode(
#[\SensitiveParameter]
string $str
): string {
return Base32::encode($str);
}
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32EncodeUpper(
#[\SensitiveParameter]
string $str
): string {
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32 decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32Decode(
#[\SensitiveParameter]
string $str
): string {
return Base32::decode($str);
}
/**
* RFC 4648 Base32 decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32DecodeUpper(
#[\SensitiveParameter]
string $str
): string {
return Base32::decodeUpper($str);
}
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32HexEncode(
#[\SensitiveParameter]
string $str
): string {
return Base32Hex::encode($str);
}
/**
* RFC 4648 Base32Hex encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32HexEncodeUpper(
#[\SensitiveParameter]
string $str
): string {
return Base32Hex::encodeUpper($str);
}
/**
* RFC 4648 Base32Hex decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32HexDecode(
#[\SensitiveParameter]
string $str
): string {
return Base32Hex::decode($str);
}
/**
* RFC 4648 Base32Hex decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32HexDecodeUpper(
#[\SensitiveParameter]
string $str
): string {
return Base32Hex::decodeUpper($str);
}
/**
* RFC 4648 Base64 encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base64Encode(
#[\SensitiveParameter]
string $str
): string {
return Base64::encode($str);
}
/**
* RFC 4648 Base64 decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base64Decode(
#[\SensitiveParameter]
string $str
): string {
return Base64::decode($str);
}
/**
* Encode into Base64
*
* Base64 character set "./[A-Z][a-z][0-9]"
* @param string $str
* @return string
* @throws TypeError
*/
public static function base64EncodeDotSlash(
#[\SensitiveParameter]
string $str
): string {
return Base64DotSlash::encode($str);
}
/**
* Decode from base64 to raw binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $str
* @return string
* @throws \RangeException
* @throws TypeError
*/
public static function base64DecodeDotSlash(
#[\SensitiveParameter]
string $str
): string {
return Base64DotSlash::decode($str);
}
/**
* Encode into Base64
*
* Base64 character set "[.-9][A-Z][a-z]" or "./[0-9][A-Z][a-z]"
* @param string $str
* @return string
* @throws TypeError
*/
public static function base64EncodeDotSlashOrdered(
#[\SensitiveParameter]
string $str
): string {
return Base64DotSlashOrdered::encode($str);
}
/**
* Decode from base64 to raw binary
*
* Base64 character set "[.-9][A-Z][a-z]" or "./[0-9][A-Z][a-z]"
*
* @param string $str
* @return string
* @throws \RangeException
* @throws TypeError
*/
public static function base64DecodeDotSlashOrdered(
#[\SensitiveParameter]
string $str
): string {
return Base64DotSlashOrdered::decode($str);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
* @throws TypeError
*/
public static function hexEncode(
#[\SensitiveParameter]
string $bin_string
): string {
return Hex::encode($bin_string);
}
/**
* Convert a hexadecimal string into a binary string without cache-timing
* leaks
*
* @param string $hex_string
* @return string (raw binary)
* @throws \RangeException
*/
public static function hexDecode(
#[\SensitiveParameter]
string $hex_string
): string {
return Hex::decode($hex_string);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
* @throws TypeError
*/
public static function hexEncodeUpper(
#[\SensitiveParameter]
string $bin_string
): string {
return Hex::encodeUpper($bin_string);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
*/
public static function hexDecodeUpper(
#[\SensitiveParameter]
string $bin_string
): string {
return Hex::decode($bin_string);
}
}
@@ -1,151 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use RangeException;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Hex
* @package ParagonIE\ConstantTime
*/
abstract class Hex implements EncoderInterface
{
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $binString (raw binary)
* @return string
* @throws TypeError
*/
public static function encode(
#[\SensitiveParameter]
string $binString
): string {
$hex = '';
$len = Binary::safeStrlen($binString);
for ($i = 0; $i < $len; ++$i) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C', $binString[$i]);
$c = $chunk[1] & 0xf;
$b = $chunk[1] >> 4;
$hex .= \pack(
'CC',
(87 + $b + ((($b - 10) >> 8) & ~38)),
(87 + $c + ((($c - 10) >> 8) & ~38))
);
}
return $hex;
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks, returning uppercase letters (as per RFC 4648)
*
* @param string $binString (raw binary)
* @return string
* @throws TypeError
*/
public static function encodeUpper(
#[\SensitiveParameter]
string $binString
): string {
$hex = '';
$len = Binary::safeStrlen($binString);
for ($i = 0; $i < $len; ++$i) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C', $binString[$i]);
$c = $chunk[1] & 0xf;
$b = $chunk[1] >> 4;
$hex .= \pack(
'CC',
(55 + $b + ((($b - 10) >> 8) & ~6)),
(55 + $c + ((($c - 10) >> 8) & ~6))
);
}
return $hex;
}
/**
* Convert a hexadecimal string into a binary string without cache-timing
* leaks
*
* @param string $encodedString
* @param bool $strictPadding
* @return string (raw binary)
* @throws RangeException
*/
public static function decode(
#[\SensitiveParameter]
string $encodedString,
bool $strictPadding = false
): string {
$hex_pos = 0;
$bin = '';
$c_acc = 0;
$hex_len = Binary::safeStrlen($encodedString);
$state = 0;
if (($hex_len & 1) !== 0) {
if ($strictPadding) {
throw new RangeException(
'Expected an even number of hexadecimal characters'
);
} else {
$encodedString = '0' . $encodedString;
++$hex_len;
}
}
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', $encodedString);
while ($hex_pos < $hex_len) {
++$hex_pos;
$c = $chunk[$hex_pos];
$c_num = $c ^ 48;
$c_num0 = ($c_num - 10) >> 8;
$c_alpha = ($c & ~32) - 55;
$c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
if (($c_num0 | $c_alpha0) === 0) {
throw new RangeException(
'Expected hexadecimal character'
);
}
$c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
if ($state === 0) {
$c_acc = $c_val * 16;
} else {
$bin .= \pack('C', $c_acc | $c_val);
}
$state ^= 1;
}
return $bin;
}
}
@@ -1,206 +0,0 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class RFC4648
*
* This class conforms strictly to the RFC
*
* @package ParagonIE\ConstantTime
*/
abstract class RFC4648
{
/**
* RFC 4648 Base64 encoding
*
* "foo" -> "Zm9v"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base64Encode(
#[\SensitiveParameter]
string $str
): string {
return Base64::encode($str);
}
/**
* RFC 4648 Base64 decoding
*
* "Zm9v" -> "foo"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base64Decode(
#[\SensitiveParameter]
string $str
): string {
return Base64::decode($str, true);
}
/**
* RFC 4648 Base64 (URL Safe) encoding
*
* "foo" -> "Zm9v"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base64UrlSafeEncode(
#[\SensitiveParameter]
string $str
): string {
return Base64UrlSafe::encode($str);
}
/**
* RFC 4648 Base64 (URL Safe) decoding
*
* "Zm9v" -> "foo"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base64UrlSafeDecode(
#[\SensitiveParameter]
string $str
): string {
return Base64UrlSafe::decode($str, true);
}
/**
* RFC 4648 Base32 encoding
*
* "foo" -> "MZXW6==="
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base32Encode(
#[\SensitiveParameter]
string $str
): string {
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32 encoding
*
* "MZXW6===" -> "foo"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base32Decode(
#[\SensitiveParameter]
string $str
): string {
return Base32::decodeUpper($str, true);
}
/**
* RFC 4648 Base32-Hex encoding
*
* "foo" -> "CPNMU==="
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base32HexEncode(
#[\SensitiveParameter]
string $str
): string {
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32-Hex decoding
*
* "CPNMU===" -> "foo"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base32HexDecode(
#[\SensitiveParameter]
string $str
): string {
return Base32::decodeUpper($str, true);
}
/**
* RFC 4648 Base16 decoding
*
* "foo" -> "666F6F"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base16Encode(
#[\SensitiveParameter]
string $str
): string {
return Hex::encodeUpper($str);
}
/**
* RFC 4648 Base16 decoding
*
* "666F6F" -> "foo"
*
* @param string $str
* @return string
*/
public static function base16Decode(
#[\SensitiveParameter]
string $str
): string {
return Hex::decode($str, true);
}
}
-22
View File
@@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Paragon Initiative Enterprises
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,5 +0,0 @@
#!/usr/bin/env bash
basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) )
php -dphar.readonly=0 "$basedir/other/build_phar.php" $*
@@ -1,34 +0,0 @@
{
"name": "paragonie/random_compat",
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"random",
"polyfill",
"pseudorandom"
],
"license": "MIT",
"type": "library",
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"support": {
"issues": "https://github.com/paragonie/random_compat/issues",
"email": "info@paragonie.com",
"source": "https://github.com/paragonie/random_compat"
},
"require": {
"php": ">= 7"
},
"require-dev": {
"vimeo/psalm": "^1",
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
}
}
@@ -1,5 +0,0 @@
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm
pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p
+h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc
-----END PUBLIC KEY-----
@@ -1,11 +0,0 @@
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (MingW32)
iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip
QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg
1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW
NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA
NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV
JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74=
=B6+8
-----END PGP SIGNATURE-----
@@ -1,32 +0,0 @@
<?php
/**
* Random_* Compatibility Library
* for using the new PHP 7 random_* API in PHP 5 projects
*
* @version 2.99.99
* @released 2018-06-06
*
* The MIT License (MIT)
*
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// NOP
@@ -1,57 +0,0 @@
<?php
$dist = dirname(__DIR__).'/dist';
if (!is_dir($dist)) {
mkdir($dist, 0755);
}
if (file_exists($dist.'/random_compat.phar')) {
unlink($dist.'/random_compat.phar');
}
$phar = new Phar(
$dist.'/random_compat.phar',
FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME,
'random_compat.phar'
);
rename(
dirname(__DIR__).'/lib/random.php',
dirname(__DIR__).'/lib/index.php'
);
$phar->buildFromDirectory(dirname(__DIR__).'/lib');
rename(
dirname(__DIR__).'/lib/index.php',
dirname(__DIR__).'/lib/random.php'
);
/**
* If we pass an (optional) path to a private key as a second argument, we will
* sign the Phar with OpenSSL.
*
* If you leave this out, it will produce an unsigned .phar!
*/
if ($argc > 1) {
if (!@is_readable($argv[1])) {
echo 'Could not read the private key file:', $argv[1], "\n";
exit(255);
}
$pkeyFile = file_get_contents($argv[1]);
$private = openssl_get_privatekey($pkeyFile);
if ($private !== false) {
$pkey = '';
openssl_pkey_export($private, $pkey);
$phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
/**
* Save the corresponding public key to the file
*/
if (!@is_readable($dist.'/random_compat.phar.pubkey')) {
$details = openssl_pkey_get_details($private);
file_put_contents(
$dist.'/random_compat.phar.pubkey',
$details['key']
);
}
} else {
echo 'An error occurred reading the private key from OpenSSL.', "\n";
exit(255);
}
}
@@ -1,9 +0,0 @@
<?php
require_once 'lib/byte_safe_strings.php';
require_once 'lib/cast_to_int.php';
require_once 'lib/error_polyfill.php';
require_once 'other/ide_stubs/libsodium.php';
require_once 'lib/random.php';
$int = random_int(0, 65536);
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0"?>
<psalm
autoloader="psalm-autoload.php"
stopOnFirstError="false"
useDocblockTypes="true"
>
<projectFiles>
<directory name="lib" />
</projectFiles>
<issueHandlers>
<RedundantConditionGivenDocblockType errorLevel="info" />
<UnresolvableInclude errorLevel="info" />
<DuplicateClass errorLevel="info" />
<InvalidOperand errorLevel="info" />
<UndefinedConstant errorLevel="info" />
<MissingReturnType errorLevel="info" />
<InvalidReturnType errorLevel="info" />
</issueHandlers>
</psalm>
-7
View File
@@ -1,7 +0,0 @@
phpseclib Lead Developer: TerraFrost (Jim Wigginton)
phpseclib Developers: monnerat (Patrick Monnerat)
bantu (Andreas Fischer)
petrich (Hans-Jürgen Petrich)
GrahamCampbell (Graham Campbell)
hc-jworman
-19
View File
@@ -1,19 +0,0 @@
# Backers
phpseclib ongoing development is made possible by [Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) and by contributions by users like you. Thank you.
## Backers
- Allan Simon
- [ChargeOver](https://chargeover.com/)
- Raghu Veer Dendukuri
- Zane Hooper
- [Setasign](https://www.setasign.com/)
- [Charles Severance](https://github.com/csev)
- [Rachel Fish](https://github.com/itsrachelfish)
- Tharyrok
- [cjhaas](https://github.com/cjhaas)
- [istiak-tridip](https://github.com/istiak-tridip)
- [Anna Filina](https://github.com/afilina)
- [blakemckeeby](https://github.com/blakemckeeby)
- [ssddanbrown](https://github.com/ssddanbrown)
-20
View File
@@ -1,20 +0,0 @@
Copyright (c) 2011-2019 TerraFrost and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-97
View File
@@ -1,97 +0,0 @@
# phpseclib - PHP Secure Communications Library
[![CI Status](https://github.com/phpseclib/phpseclib/actions/workflows/ci.yml/badge.svg?branch=3.0&event=push "CI Status")](https://github.com/phpseclib/phpseclib)
## Supporting phpseclib
- [Become a backer or sponsor on Patreon](https://www.patreon.com/phpseclib)
- [One-time donation via PayPal or crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487)
- [Subscribe to Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme)
## Introduction
MIT-licensed pure-PHP implementations of the following:
SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 / Ed449 / Curve25519 / Curve449, ECDSA / ECDH (with support for 66 curves), RSA (PKCS#1 v2.2 compliant), DSA / DH, DES / 3DES / RC4 / Rijndael / AES / Blowfish / Twofish / Salsa20 / ChaCha20, GCM / Poly1305
* [Browse Git](https://github.com/phpseclib/phpseclib)
## Documentation
* [Documentation / Manual](https://phpseclib.com/)
* [API Documentation](https://api.phpseclib.com/3.0/) (generated by Doctum)
## Branches
### master
* Development Branch
* Unstable API
* Do not use in production
### 3.0
* Long term support (LTS) release
* Major expansion of cryptographic primitives
* Minimum PHP version: 5.6.1
* PSR-4 autoloading with namespace rooted at `\phpseclib3`
* Install via Composer: `composer require phpseclib/phpseclib:~3.0`
### 2.0
* Long term support (LTS) release
* Modernized version of 1.0
* Minimum PHP version: 5.3.3
* PSR-4 autoloading with namespace rooted at `\phpseclib`
* Install via Composer: `composer require phpseclib/phpseclib:~2.0`
### 1.0
* Long term support (LTS) release
* PHP4 compatible
* Composer compatible (PSR-0 autoloading)
* Install using Composer: `composer require phpseclib/phpseclib:~1.0`
* [Download 1.0.23 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.23.zip/download)
## Security contact information
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
## Support
Need Support?
* [Checkout Questions and Answers on Stack Overflow](http://stackoverflow.com/questions/tagged/phpseclib)
* [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new)
* [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use)
## Special Thanks
Special Thanks to our $50+ sponsors!:
- Allan Simon
- [ChargeOver](https://chargeover.com/)
## Contributing
1. Fork the Project
2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/))
3. Install Development Dependencies
```sh
composer install
```
4. Create a Feature Branch
5. Run continuous integration checks:
```sh
composer global require php:^8.1 squizlabs/php_codesniffer friendsofphp/php-cs-fixer vimeo/psalm
phpcs --standard=build/php_codesniffer.xml
php-cs-fixer fix --config=build/php-cs-fixer.php --diff --dry-run --using-cache=no
psalm --config=build/psalm.xml --no-cache --long-progress --report-show-info=false --output-format=text
vendor/bin/phpunit --verbose --configuration tests/phpunit.xml
```
6. Send us a Pull Request
-84
View File
@@ -1,84 +0,0 @@
{
"name": "phpseclib/phpseclib",
"type": "library",
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"keywords": [
"security",
"crypto",
"cryptography",
"encryption",
"signature",
"signing",
"rsa",
"aes",
"blowfish",
"twofish",
"ssh",
"sftp",
"x509",
"x.509",
"asn1",
"asn.1",
"BigInteger"
],
"homepage": "http://phpseclib.sourceforge.net",
"license": "MIT",
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"require": {
"php": ">=5.6.1",
"paragonie/constant_time_encoding": "^1|^2|^3",
"paragonie/random_compat": "^1.4|^2.0|^9.99.99"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"suggest": {
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-dom": "Install the DOM extension to load XML formatted public keys."
},
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib3\\": "phpseclib/"
}
},
"autoload-dev": {
"psr-4": {
"phpseclib3\\Tests\\": "tests/"
}
},
"config": {
"sort-packages": true
}
}
@@ -1,505 +0,0 @@
<?php
/**
* Common String Functions
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Common\Functions;
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\ConstantTime\Hex;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\Common\FiniteField;
/**
* Common String Functions
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Strings
{
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
*/
public static function shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* String Pop
*
* Inspired by array_pop
*
* @param string $string
* @param int $index
* @return string
*/
public static function pop(&$string, $index = 1)
{
$substr = substr($string, -$index);
$string = substr($string, 0, -$index);
return $substr;
}
/**
* Parse SSH2-style string
*
* Returns either an array or a boolean if $data is malformed.
*
* Valid characters for $format are as follows:
*
* C = byte
* b = boolean (true/false)
* N = uint32
* Q = uint64
* s = string
* i = mpint
* L = name-list
*
* uint64 is not supported.
*
* @param string $format
* @param string $data
* @return mixed
*/
public static function unpackSSH2($format, &$data)
{
$format = self::formatPack($format);
$result = [];
for ($i = 0; $i < strlen($format); $i++) {
switch ($format[$i]) {
case 'C':
case 'b':
if (!strlen($data)) {
throw new \LengthException('At least one byte needs to be present for successful C / b decodes');
}
break;
case 'N':
case 'i':
case 's':
case 'L':
if (strlen($data) < 4) {
throw new \LengthException('At least four byte needs to be present for successful N / i / s / L decodes');
}
break;
case 'Q':
if (strlen($data) < 8) {
throw new \LengthException('At least eight byte needs to be present for successful N / i / s / L decodes');
}
break;
default:
throw new \InvalidArgumentException('$format contains an invalid character');
}
switch ($format[$i]) {
case 'C':
$result[] = ord(self::shift($data));
continue 2;
case 'b':
$result[] = ord(self::shift($data)) != 0;
continue 2;
case 'N':
list(, $temp) = unpack('N', self::shift($data, 4));
$result[] = $temp;
continue 2;
case 'Q':
// pack() added support for Q in PHP 5.6.3 and PHP 5.6 is phpseclib 3's minimum version
// so in theory we could support this BUT, "64-bit format codes are not available for
// 32-bit versions" and phpseclib works on 32-bit installs. on 32-bit installs
// 64-bit floats can be used to get larger numbers then 32-bit signed ints would allow
// for. sure, you're not gonna get the full precision of 64-bit numbers but just because
// you need > 32-bit precision doesn't mean you need the full 64-bit precision
extract(unpack('Nupper/Nlower', self::shift($data, 8)));
$temp = $upper ? 4294967296 * $upper : 0;
$temp += $lower < 0 ? ($lower & 0x7FFFFFFFF) + 0x80000000 : $lower;
// $temp = hexdec(bin2hex(self::shift($data, 8)));
$result[] = $temp;
continue 2;
}
list(, $length) = unpack('N', self::shift($data, 4));
if (strlen($data) < $length) {
throw new \LengthException("$length bytes needed; " . strlen($data) . ' bytes available');
}
$temp = self::shift($data, $length);
switch ($format[$i]) {
case 'i':
$result[] = new BigInteger($temp, -256);
break;
case 's':
$result[] = $temp;
break;
case 'L':
$result[] = explode(',', $temp);
}
}
return $result;
}
/**
* Create SSH2-style string
*
* @param string $format
* @param string|int|float|array|bool ...$elements
* @return string
*/
public static function packSSH2($format, ...$elements)
{
$format = self::formatPack($format);
if (strlen($format) != count($elements)) {
throw new \InvalidArgumentException('There must be as many arguments as there are characters in the $format string');
}
$result = '';
for ($i = 0; $i < strlen($format); $i++) {
$element = $elements[$i];
switch ($format[$i]) {
case 'C':
if (!is_int($element)) {
throw new \InvalidArgumentException('Bytes must be represented as an integer between 0 and 255, inclusive.');
}
$result .= pack('C', $element);
break;
case 'b':
if (!is_bool($element)) {
throw new \InvalidArgumentException('A boolean parameter was expected.');
}
$result .= $element ? "\1" : "\0";
break;
case 'Q':
if (!is_int($element) && !is_float($element)) {
throw new \InvalidArgumentException('An integer was expected.');
}
// 4294967296 == 1 << 32
$result .= pack('NN', $element / 4294967296, $element);
break;
case 'N':
if (is_float($element)) {
$element = (int) $element;
}
if (!is_int($element)) {
throw new \InvalidArgumentException('An integer was expected.');
}
$result .= pack('N', $element);
break;
case 's':
if (!self::is_stringable($element)) {
throw new \InvalidArgumentException('A string was expected.');
}
$result .= pack('Na*', strlen($element), $element);
break;
case 'i':
if (!$element instanceof BigInteger && !$element instanceof FiniteField\Integer) {
throw new \InvalidArgumentException('A phpseclib3\Math\BigInteger or phpseclib3\Math\Common\FiniteField\Integer object was expected.');
}
$element = $element->toBytes(true);
$result .= pack('Na*', strlen($element), $element);
break;
case 'L':
if (!is_array($element)) {
throw new \InvalidArgumentException('An array was expected.');
}
$element = implode(',', $element);
$result .= pack('Na*', strlen($element), $element);
break;
default:
throw new \InvalidArgumentException('$format contains an invalid character');
}
}
return $result;
}
/**
* Expand a pack string
*
* Converts C5 to CCCCC, for example.
*
* @param string $format
* @return string
*/
private static function formatPack($format)
{
$parts = preg_split('#(\d+)#', $format, -1, PREG_SPLIT_DELIM_CAPTURE);
$format = '';
for ($i = 1; $i < count($parts); $i += 2) {
$format .= substr($parts[$i - 1], 0, -1) . str_repeat(substr($parts[$i - 1], -1), $parts[$i]);
}
$format .= $parts[$i - 1];
return $format;
}
/**
* Convert binary data into bits
*
* bin2hex / hex2bin refer to base-256 encoded data as binary, whilst
* decbin / bindec refer to base-2 encoded data as binary. For the purposes
* of this function, bin refers to base-256 encoded data whilst bits refers
* to base-2 encoded data
*
* @param string $x
* @return string
*/
public static function bits2bin($x)
{
/*
// the pure-PHP approach is faster than the GMP approach
if (function_exists('gmp_export')) {
return strlen($x) ? gmp_export(gmp_init($x, 2)) : gmp_init(0);
}
*/
if (preg_match('#[^01]#', $x)) {
throw new \RuntimeException('The only valid characters are 0 and 1');
}
if (!defined('PHP_INT_MIN')) {
define('PHP_INT_MIN', ~PHP_INT_MAX);
}
$length = strlen($x);
if (!$length) {
return '';
}
$block_size = PHP_INT_SIZE << 3;
$pad = $block_size - ($length % $block_size);
if ($pad != $block_size) {
$x = str_repeat('0', $pad) . $x;
}
$parts = str_split($x, $block_size);
$str = '';
foreach ($parts as $part) {
$xor = $part[0] == '1' ? PHP_INT_MIN : 0;
$part[0] = '0';
$str .= pack(
PHP_INT_SIZE == 4 ? 'N' : 'J',
$xor ^ eval('return 0b' . $part . ';')
);
}
return ltrim($str, "\0");
}
/**
* Convert bits to binary data
*
* @param string $x
* @return string
*/
public static function bin2bits($x, $trim = true)
{
/*
// the pure-PHP approach is slower than the GMP approach BUT
// i want to the pure-PHP version to be easily unit tested as well
if (function_exists('gmp_import')) {
return gmp_strval(gmp_import($x), 2);
}
*/
$len = strlen($x);
$mod = $len % PHP_INT_SIZE;
if ($mod) {
$x = str_pad($x, $len + PHP_INT_SIZE - $mod, "\0", STR_PAD_LEFT);
}
$bits = '';
if (PHP_INT_SIZE == 4) {
$digits = unpack('N*', $x);
foreach ($digits as $digit) {
$bits .= sprintf('%032b', $digit);
}
} else {
$digits = unpack('J*', $x);
foreach ($digits as $digit) {
$bits .= sprintf('%064b', $digit);
}
}
return $trim ? ltrim($bits, '0') : $bits;
}
/**
* Switch Endianness Bit Order
*
* @param string $x
* @return string
*/
public static function switchEndianness($x)
{
$r = '';
for ($i = strlen($x) - 1; $i >= 0; $i--) {
$b = ord($x[$i]);
if (PHP_INT_SIZE === 8) {
// 3 operations
// from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
$r .= chr((($b * 0x0202020202) & 0x010884422010) % 1023);
} else {
// 7 operations
// from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
$p1 = ($b * 0x0802) & 0x22110;
$p2 = ($b * 0x8020) & 0x88440;
$r .= chr(
(($p1 | $p2) * 0x10101) >> 16
);
}
}
return $r;
}
/**
* Increment the current string
*
* @param string $var
* @return string
*/
public static function increment_str(&$var)
{
if (function_exists('sodium_increment')) {
$var = strrev($var);
sodium_increment($var);
$var = strrev($var);
return $var;
}
for ($i = 4; $i <= strlen($var); $i += 4) {
$temp = substr($var, -$i, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4);
break;
case "\x7F\xFF\xFF\xFF":
$var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4);
return $var;
default:
$temp = unpack('Nnum', $temp);
$var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4);
return $var;
}
}
$remainder = strlen($var) % 4;
if ($remainder == 0) {
return $var;
}
$temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT));
$temp = substr(pack('N', $temp['num'] + 1), -$remainder);
$var = substr_replace($var, $temp, 0, $remainder);
return $var;
}
/**
* Find whether the type of a variable is string (or could be converted to one)
*
* @param mixed $var
* @return bool
* @psalm-assert-if-true string|\Stringable $var
*/
public static function is_stringable($var)
{
return is_string($var) || (is_object($var) && method_exists($var, '__toString'));
}
/**
* Constant Time Base64-decoding
*
* ParagoneIE\ConstantTime doesn't use libsodium if it's available so we'll do so
* ourselves. see https://github.com/paragonie/constant_time_encoding/issues/39
*
* @param string $data
* @return string
*/
public static function base64_decode($data)
{
return function_exists('sodium_base642bin') ?
sodium_base642bin($data, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, '=') :
Base64::decode($data);
}
/**
* Constant Time Base64-decoding (URL safe)
*
* @param string $data
* @return string
*/
public static function base64url_decode($data)
{
// return self::base64_decode(str_replace(['-', '_'], ['+', '/'], $data));
return function_exists('sodium_base642bin') ?
sodium_base642bin($data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, '=') :
Base64UrlSafe::decode($data);
}
/**
* Constant Time Base64-encoding
*
* @param string $data
* @return string
*/
public static function base64_encode($data)
{
return function_exists('sodium_bin2base64') ?
sodium_bin2base64($data, SODIUM_BASE64_VARIANT_ORIGINAL) :
Base64::encode($data);
}
/**
* Constant Time Base64-encoding (URL safe)
*
* @param string $data
* @return string
*/
public static function base64url_encode($data)
{
// return str_replace(['+', '/'], ['-', '_'], self::base64_encode($data));
return function_exists('sodium_bin2base64') ?
sodium_bin2base64($data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING) :
Base64UrlSafe::encode($data);
}
/**
* Constant Time Hex Decoder
*
* @param string $data
* @return string
*/
public static function hex2bin($data)
{
return function_exists('sodium_hex2bin') ?
sodium_hex2bin($data) :
Hex::decode($data);
}
/**
* Constant Time Hex Encoder
*
* @param string $data
* @return string
*/
public static function bin2hex($data)
{
return function_exists('sodium_bin2hex') ?
sodium_bin2hex($data) :
Hex::encode($data);
}
}
@@ -1,116 +0,0 @@
<?php
/**
* Pure-PHP implementation of AES.
*
* Uses mcrypt, if available/possible, and an internal implementation, otherwise.
*
* PHP version 5
*
* NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually
* just a wrapper to Rijndael.php you may consider using Rijndael.php instead of
* to save one include_once().
*
* If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
* {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
* it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()}
* is called, again, at which point, it'll be recalculated.
*
* Since \phpseclib3\Crypt\AES extends \phpseclib3\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't
* make a whole lot of sense. {@link self::setBlockLength() setBlockLength()}, for instance. Calling that function,
* however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $aes = new \phpseclib3\Crypt\AES('ctr');
*
* $aes->setKey('abcdefghijklmnop');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $aes->decrypt($aes->encrypt($plaintext));
* ?>
* </code>
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2008 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
/**
* Pure-PHP implementation of AES.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class AES extends Rijndael
{
/**
* Dummy function
*
* Since \phpseclib3\Crypt\AES extends \phpseclib3\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything.
*
* @see \phpseclib3\Crypt\Rijndael::setBlockLength()
* @param int $length
* @throws \BadMethodCallException anytime it's called
*/
public function setBlockLength($length)
{
throw new \BadMethodCallException('The block length cannot be set for AES.');
}
/**
* Sets the key length
*
* Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length
*
* @see \phpseclib3\Crypt\Rijndael:setKeyLength()
* @param int $length
* @throws \LengthException if the key length isn't supported
*/
public function setKeyLength($length)
{
switch ($length) {
case 128:
case 192:
case 256:
break;
default:
throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported');
}
parent::setKeyLength($length);
}
/**
* Sets the key.
*
* Rijndael supports five different key lengths, AES only supports three.
*
* @see \phpseclib3\Crypt\Rijndael:setKey()
* @see setKeyLength()
* @param string $key
* @throws \LengthException if the key length isn't supported
*/
public function setKey($key)
{
switch (strlen($key)) {
case 16:
case 24:
case 32:
break;
default:
throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
}
parent::setKey($key);
}
}
@@ -1,835 +0,0 @@
<?php
/**
* Pure-PHP implementation of Blowfish.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP version 5
*
* Useful resources are as follows:
*
* - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia description of Blowfish}
*
* # An overview of bcrypt vs Blowfish
*
* OpenSSH private keys use a customized version of bcrypt. Specifically, instead of
* encrypting OrpheanBeholderScryDoubt 64 times OpenSSH's bcrypt variant encrypts
* OxychromaticBlowfishSwatDynamite 64 times. so we can't use crypt().
*
* bcrypt is basically Blowfish but instead of performing the key expansion once it performs
* the expansion 129 times for each round, with the first key expansion interleaving the salt
* and password. This renders OpenSSL unusable and forces us to use a pure-PHP implementation
* of blowfish.
*
* # phpseclib's four different _encryptBlock() implementations
*
* When using Blowfish as an encryption algorithm, _encryptBlock() is called 9 + 512 +
* (the number of blocks in the plaintext) times.
*
* Each of the first 9 calls to _encryptBlock() modify the P-array. Each of the next 512
* calls modify the S-boxes. The remaining _encryptBlock() calls operate on the plaintext to
* produce the ciphertext. In the pure-PHP implementation of Blowfish these remaining
* _encryptBlock() calls are highly optimized through the use of eval(). Among other things,
* P-array lookups are eliminated by hard-coding the key-dependent P-array values, and thus we
* have explained 2 of the 4 different _encryptBlock() implementations.
*
* With bcrypt things are a bit different. _encryptBlock() is called 1,079,296 times,
* assuming 16 rounds (which is what OpenSSH's bcrypt defaults to). The eval()-optimized
* _encryptBlock() isn't as beneficial because the P-array values are not constant. Well, they
* are constant, but only for, at most, 777 _encryptBlock() calls, which is equivalent to ~6KB
* of data. The average length of back to back _encryptBlock() calls with a fixed P-array is
* 514.12, which is ~4KB of data. Creating an eval()-optimized _encryptBlock() has an upfront
* cost, which is CPU dependent and is probably not going to be worth it for just ~4KB of
* data. Conseqeuently, bcrypt does not benefit from the eval()-optimized _encryptBlock().
*
* The regular _encryptBlock() does unpack() and pack() on every call, as well, and that can
* begin to add up after one million function calls.
*
* In theory, one might think that it might be beneficial to rewrite all block ciphers so
* that, instead of passing strings to _encryptBlock(), you convert the string to an array of
* integers and then pass successive subarrays of that array to _encryptBlock. This, however,
* kills PHP's memory use. Like let's say you have a 1MB long string. After doing
* $in = str_repeat('a', 1024 * 1024); PHP's memory utilization jumps up by ~1MB. After doing
* $blocks = str_split($in, 4); it jumps up by an additional ~16MB. After
* $blocks = array_map(fn($x) => unpack('N*', $x), $blocks); it jumps up by an additional
* ~90MB, yielding a 106x increase in memory usage. Consequently, it bcrypt calls a different
* _encryptBlock() then the regular Blowfish does. That said, the Blowfish _encryptBlock() is
* basically just a thin wrapper around the bcrypt _encryptBlock(), so there's that.
*
* This explains 3 of the 4 _encryptBlock() implementations. the last _encryptBlock()
* implementation can best be understood by doing Ctrl + F and searching for where
* self::$use_reg_intval is defined.
*
* # phpseclib's three different _setupKey() implementations
*
* Every bcrypt round is the equivalent of encrypting 512KB of data. Since OpenSSH uses 16
* rounds by default that's ~8MB of data that's essentially being encrypted whenever
* you use bcrypt. That's a lot of data, however, bcrypt operates within tighter constraints
* than regular Blowfish, so we can use that to our advantage. In particular, whereas Blowfish
* supports variable length keys, in bcrypt, the initial "key" is the sha512 hash of the
* password. sha512 hashes are 512 bits or 64 bytes long and thus the bcrypt keys are of a
* fixed length whereas Blowfish keys are not of a fixed length.
*
* bcrypt actually has two different key expansion steps. The first one (expandstate) is
* constantly XOR'ing every _encryptBlock() parameter against the salt prior _encryptBlock()'s
* being called. The second one (expand0state) is more similar to Blowfish's _setupKey()
* but it can still use the fixed length key optimization discussed above and can do away with
* the pack() / unpack() calls.
*
* I suppose _setupKey() could be made to be a thin wrapper around expandstate() but idk it's
* just a lot of work for very marginal benefits as _setupKey() is only called once for
* regular Blowfish vs the 128 times it's called --per round-- with bcrypt.
*
* # blowfish + bcrypt in the same class
*
* Altho there's a lot of Blowfish code that bcrypt doesn't re-use, bcrypt does re-use the
* initial S-boxes, the initial P-array and the int-only _encryptBlock() implementation.
*
* # Credit
*
* phpseclib's bcrypt implementation is based losely off of OpenSSH's implementation:
*
* https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bcrypt_pbkdf.c
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $blowfish = new \phpseclib3\Crypt\Blowfish('ctr');
*
* $blowfish->setKey('12345678901234567890123456789012');
*
* $plaintext = str_repeat('a', 1024);
*
* echo $blowfish->decrypt($blowfish->encrypt($plaintext));
* ?>
* </code>
*
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Crypt\Common\BlockCipher;
/**
* Pure-PHP implementation of Blowfish.
*
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
*/
class Blowfish extends BlockCipher
{
/**
* Block Length of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::block_size
* @var int
*/
protected $block_size = 8;
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @var string
*/
protected $cipher_name_mcrypt = 'blowfish';
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len
* @var int
*/
protected $cfb_init_len = 500;
/**
* The fixed subkeys boxes
*
* S-Box
*
* @var array
*/
private static $sbox = [
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
];
/**
* P-Array consists of 18 32-bit subkeys
*
* @var array
*/
private static $parray = [
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b
];
/**
* The BCTX-working Array
*
* Holds the expanded key [p] and the key-depended s-boxes [sb]
*
* @var array
*/
private $bctx;
/**
* Holds the last used key
*
* @var array
*/
private $kl;
/**
* The Key Length (in bytes)
* {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
* because the encryption / decryption / key schedule creation requires this number and not $key_length. We could
* derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.}
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setKeyLength()
* @var int
*/
protected $key_length = 16;
/**
* Default Constructor.
*
* @param string $mode
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/
public function __construct($mode)
{
parent::__construct($mode);
if ($this->mode == self::MODE_STREAM) {
throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode');
}
}
/**
* Sets the key length.
*
* Key lengths can be between 32 and 448 bits.
*
* @param int $length
*/
public function setKeyLength($length)
{
if ($length < 32 || $length > 448) {
throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported');
}
$this->key_length = $length >> 3;
parent::setKeyLength($length);
}
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
* @param int $engine
* @return bool
*/
protected function isValidEngineHelper($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
if ($this->key_length < 16) {
return false;
}
// quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
// "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
// in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
return false;
}
$this->cipher_name_openssl_ecb = 'bf-ecb';
$this->cipher_name_openssl = 'bf-' . $this->openssl_translate_mode();
}
return parent::isValidEngineHelper($engine);
}
/**
* Setup the key (expansion)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey()
*/
protected function setupKey()
{
if (isset($this->kl['key']) && $this->key === $this->kl['key']) {
// already expanded
return;
}
$this->kl = ['key' => $this->key];
/* key-expanding p[] and S-Box building sb[] */
$this->bctx = [
'p' => [],
'sb' => self::$sbox
];
// unpack binary string in unsigned chars
$key = array_values(unpack('C*', $this->key));
$keyl = count($key);
// with bcrypt $keyl will always be 16 (because the key is the sha512 of the key you provide)
for ($j = 0, $i = 0; $i < 18; ++$i) {
// xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ...
for ($data = 0, $k = 0; $k < 4; ++$k) {
$data = ($data << 8) | $key[$j];
if (++$j >= $keyl) {
$j = 0;
}
}
$this->bctx['p'][] = self::$parray[$i] ^ intval($data);
}
// encrypt the zero-string, replace P1 and P2 with the encrypted data,
// encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys
$data = "\0\0\0\0\0\0\0\0";
for ($i = 0; $i < 18; $i += 2) {
list($l, $r) = array_values(unpack('N*', $data = $this->encryptBlock($data)));
$this->bctx['p'][$i ] = $l;
$this->bctx['p'][$i + 1] = $r;
}
for ($i = 0; $i < 0x400; $i += 0x100) {
for ($j = 0; $j < 256; $j += 2) {
list($l, $r) = array_values(unpack('N*', $data = $this->encryptBlock($data)));
$this->bctx['sb'][$i | $j] = $l;
$this->bctx['sb'][$i | ($j + 1)] = $r;
}
}
}
/**
* Initialize Static Variables
*/
protected static function initialize_static_variables()
{
if (is_float(self::$sbox[0x200])) {
self::$sbox = array_map('intval', self::$sbox);
self::$parray = array_map('intval', self::$parray);
}
parent::initialize_static_variables();
}
/**
* bcrypt
*
* @param string $sha2pass
* @param string $sha2salt
* @access private
* @return string
*/
private static function bcrypt_hash($sha2pass, $sha2salt)
{
$p = self::$parray;
$sbox = self::$sbox;
$cdata = array_values(unpack('N*', 'OxychromaticBlowfishSwatDynamite'));
$sha2pass = array_values(unpack('N*', $sha2pass));
$sha2salt = array_values(unpack('N*', $sha2salt));
self::expandstate($sha2salt, $sha2pass, $sbox, $p);
for ($i = 0; $i < 64; $i++) {
self::expand0state($sha2salt, $sbox, $p);
self::expand0state($sha2pass, $sbox, $p);
}
for ($i = 0; $i < 64; $i++) {
for ($j = 0; $j < 8; $j += 2) { // count($cdata) == 8
list($cdata[$j], $cdata[$j + 1]) = self::encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox, $p);
}
}
return pack('V*', ...$cdata);
}
/**
* Performs OpenSSH-style bcrypt
*
* @param string $pass
* @param string $salt
* @param int $keylen
* @param int $rounds
* @access public
* @return string
*/
public static function bcrypt_pbkdf($pass, $salt, $keylen, $rounds)
{
self::initialize_static_variables();
if (PHP_INT_SIZE == 4) {
throw new \RuntimeException('bcrypt is far too slow to be practical on 32-bit versions of PHP');
}
$sha2pass = hash('sha512', $pass, true);
$results = [];
$count = 1;
while (32 * count($results) < $keylen) {
$countsalt = $salt . pack('N', $count++);
$sha2salt = hash('sha512', $countsalt, true);
$out = $tmpout = self::bcrypt_hash($sha2pass, $sha2salt);
for ($i = 1; $i < $rounds; $i++) {
$sha2salt = hash('sha512', $tmpout, true);
$tmpout = self::bcrypt_hash($sha2pass, $sha2salt);
$out ^= $tmpout;
}
$results[] = $out;
}
$output = '';
for ($i = 0; $i < 32; $i++) {
foreach ($results as $result) {
$output .= $result[$i];
}
}
return substr($output, 0, $keylen);
}
/**
* Key expansion without salt
*
* @access private
* @param int[] $key
* @param int[] $sbox
* @param int[] $p
* @see self::_bcrypt_hash()
*/
private static function expand0state(array $key, array &$sbox, array &$p)
{
// expand0state is basically the same thing as this:
//return self::expandstate(array_fill(0, 16, 0), $key);
// but this separate function eliminates a bunch of XORs and array lookups
$p = [
$p[0] ^ $key[0],
$p[1] ^ $key[1],
$p[2] ^ $key[2],
$p[3] ^ $key[3],
$p[4] ^ $key[4],
$p[5] ^ $key[5],
$p[6] ^ $key[6],
$p[7] ^ $key[7],
$p[8] ^ $key[8],
$p[9] ^ $key[9],
$p[10] ^ $key[10],
$p[11] ^ $key[11],
$p[12] ^ $key[12],
$p[13] ^ $key[13],
$p[14] ^ $key[14],
$p[15] ^ $key[15],
$p[16] ^ $key[0],
$p[17] ^ $key[1]
];
// @codingStandardsIgnoreStart
list( $p[0], $p[1]) = self::encryptBlockHelperFast( 0, 0, $sbox, $p);
list( $p[2], $p[3]) = self::encryptBlockHelperFast($p[ 0], $p[ 1], $sbox, $p);
list( $p[4], $p[5]) = self::encryptBlockHelperFast($p[ 2], $p[ 3], $sbox, $p);
list( $p[6], $p[7]) = self::encryptBlockHelperFast($p[ 4], $p[ 5], $sbox, $p);
list( $p[8], $p[9]) = self::encryptBlockHelperFast($p[ 6], $p[ 7], $sbox, $p);
list($p[10], $p[11]) = self::encryptBlockHelperFast($p[ 8], $p[ 9], $sbox, $p);
list($p[12], $p[13]) = self::encryptBlockHelperFast($p[10], $p[11], $sbox, $p);
list($p[14], $p[15]) = self::encryptBlockHelperFast($p[12], $p[13], $sbox, $p);
list($p[16], $p[17]) = self::encryptBlockHelperFast($p[14], $p[15], $sbox, $p);
// @codingStandardsIgnoreEnd
list($sbox[0], $sbox[1]) = self::encryptBlockHelperFast($p[16], $p[17], $sbox, $p);
for ($i = 2; $i < 1024; $i += 2) {
list($sbox[$i], $sbox[$i + 1]) = self::encryptBlockHelperFast($sbox[$i - 2], $sbox[$i - 1], $sbox, $p);
}
}
/**
* Key expansion with salt
*
* @access private
* @param int[] $data
* @param int[] $key
* @param int[] $sbox
* @param int[] $p
* @see self::_bcrypt_hash()
*/
private static function expandstate(array $data, array $key, array &$sbox, array &$p)
{
$p = [
$p[0] ^ $key[0],
$p[1] ^ $key[1],
$p[2] ^ $key[2],
$p[3] ^ $key[3],
$p[4] ^ $key[4],
$p[5] ^ $key[5],
$p[6] ^ $key[6],
$p[7] ^ $key[7],
$p[8] ^ $key[8],
$p[9] ^ $key[9],
$p[10] ^ $key[10],
$p[11] ^ $key[11],
$p[12] ^ $key[12],
$p[13] ^ $key[13],
$p[14] ^ $key[14],
$p[15] ^ $key[15],
$p[16] ^ $key[0],
$p[17] ^ $key[1]
];
// @codingStandardsIgnoreStart
list( $p[0], $p[1]) = self::encryptBlockHelperFast($data[ 0] , $data[ 1] , $sbox, $p);
list( $p[2], $p[3]) = self::encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox, $p);
list( $p[4], $p[5]) = self::encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox, $p);
list( $p[6], $p[7]) = self::encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox, $p);
list( $p[8], $p[9]) = self::encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox, $p);
list($p[10], $p[11]) = self::encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox, $p);
list($p[12], $p[13]) = self::encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox, $p);
list($p[14], $p[15]) = self::encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox, $p);
list($p[16], $p[17]) = self::encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox, $p);
// @codingStandardsIgnoreEnd
list($sbox[0], $sbox[1]) = self::encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox, $p);
for ($i = 2, $j = 4; $i < 1024; $i += 2, $j = ($j + 2) % 16) { // instead of 16 maybe count($data) would be better?
list($sbox[$i], $sbox[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox[$i - 2], $data[$j + 1] ^ $sbox[$i - 1], $sbox, $p);
}
}
/**
* Encrypts a block
*
* @param string $in
* @return string
*/
protected function encryptBlock($in)
{
$p = $this->bctx['p'];
// extract($this->bctx['sb'], EXTR_PREFIX_ALL, 'sb'); // slower
$sb = $this->bctx['sb'];
$in = unpack('N*', $in);
$l = $in[1];
$r = $in[2];
list($r, $l) = PHP_INT_SIZE == 4 ?
self::encryptBlockHelperSlow($l, $r, $sb, $p) :
self::encryptBlockHelperFast($l, $r, $sb, $p);
return pack("N*", $r, $l);
}
/**
* Fast helper function for block encryption
*
* @access private
* @param int $x0
* @param int $x1
* @param int[] $sbox
* @param int[] $p
* @return int[]
*/
private static function encryptBlockHelperFast($x0, $x1, array $sbox, array $p)
{
$x0 ^= $p[0];
$x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[1];
$x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[2];
$x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[3];
$x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[4];
$x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[5];
$x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[6];
$x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[7];
$x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[8];
$x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[9];
$x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[10];
$x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[11];
$x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[12];
$x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[13];
$x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[14];
$x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[15];
$x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[16];
return [$x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF];
}
/**
* Slow helper function for block encryption
*
* @access private
* @param int $x0
* @param int $x1
* @param int[] $sbox
* @param int[] $p
* @return int[]
*/
private static function encryptBlockHelperSlow($x0, $x1, array $sbox, array $p)
{
// -16777216 == intval(0xFF000000) on 32-bit PHP installs
$x0 ^= $p[0];
$x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[1];
$x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[2];
$x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[3];
$x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[4];
$x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[5];
$x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[6];
$x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[7];
$x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[8];
$x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[9];
$x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[10];
$x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[11];
$x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[12];
$x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[13];
$x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[14];
$x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[15];
$x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[16];
return [$x1 ^ $p[17], $x0];
}
/**
* Decrypts a block
*
* @param string $in
* @return string
*/
protected function decryptBlock($in)
{
$p = $this->bctx['p'];
$sb = $this->bctx['sb'];
$in = unpack('N*', $in);
$l = $in[1];
$r = $in[2];
for ($i = 17; $i > 2; $i -= 2) {
$l ^= $p[$i];
$r ^= self::safe_intval((self::safe_intval($sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]) ^
$sb[0x200 + ($l >> 8 & 0xff)]) +
$sb[0x300 + ($l & 0xff)]);
$r ^= $p[$i - 1];
$l ^= self::safe_intval((self::safe_intval($sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]) ^
$sb[0x200 + ($r >> 8 & 0xff)]) +
$sb[0x300 + ($r & 0xff)]);
}
return pack('N*', $r ^ $p[0], $l ^ $p[1]);
}
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::_setupInlineCrypt()
*/
protected function setupInlineCrypt()
{
$p = $this->bctx['p'];
$init_crypt = '
static $sb;
if (!$sb) {
$sb = $this->bctx["sb"];
}
';
$safeint = self::safe_intval_inline();
// Generating encrypt code:
$encrypt_block = '
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
';
for ($i = 0; $i < 16; $i += 2) {
$encrypt_block .= '
$l^= ' . $p[$i] . ';
$r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]') . ' ^
$sb[0x200 + ($l >> 8 & 0xff)]) +
$sb[0x300 + ($l & 0xff)]') . ';
$r^= ' . $p[$i + 1] . ';
$l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]') . ' ^
$sb[0x200 + ($r >> 8 & 0xff)]) +
$sb[0x300 + ($r & 0xff)]') . ';
';
}
$encrypt_block .= '
$in = pack("N*",
$r ^ ' . $p[17] . ',
$l ^ ' . $p[16] . '
);
';
// Generating decrypt code:
$decrypt_block = '
$in = unpack("N*", $in);
$l = $in[1];
$r = $in[2];
';
for ($i = 17; $i > 2; $i -= 2) {
$decrypt_block .= '
$l^= ' . $p[$i] . ';
$r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]') . ' ^
$sb[0x200 + ($l >> 8 & 0xff)]) +
$sb[0x300 + ($l & 0xff)]') . ';
$r^= ' . $p[$i - 1] . ';
$l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]') . ' ^
$sb[0x200 + ($r >> 8 & 0xff)]) +
$sb[0x300 + ($r & 0xff)]') . ';
';
}
$decrypt_block .= '
$in = pack("N*",
$r ^ ' . $p[0] . ',
$l ^ ' . $p[1] . '
);
';
$this->inline_crypt = $this->createInlineCryptFunction(
[
'init_crypt' => $init_crypt,
'init_encrypt' => '',
'init_decrypt' => '',
'encrypt_block' => $encrypt_block,
'decrypt_block' => $decrypt_block
]
);
}
}
@@ -1,799 +0,0 @@
<?php
/**
* Pure-PHP implementation of ChaCha20.
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2019 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Crypt;
use phpseclib3\Exception\BadDecryptionException;
use phpseclib3\Exception\InsufficientSetupException;
/**
* Pure-PHP implementation of ChaCha20.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class ChaCha20 extends Salsa20
{
/**
* The OpenSSL specific name of the cipher
*
* @var string
*/
protected $cipher_name_openssl = 'chacha20';
/**
* Test for engine validity
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @param int $engine
* @return bool
*/
protected function isValidEngineHelper($engine)
{
switch ($engine) {
case self::ENGINE_LIBSODIUM:
// PHP 7.2.0 (30 Nov 2017) added support for libsodium
// we could probably make it so that if $this->counter == 0 then the first block would be done with either OpenSSL
// or PHP and then subsequent blocks would then be done with libsodium but idk - it's not a high priority atm
// we could also make it so that if $this->counter == 0 and $this->continuousBuffer then do the first string
// with libsodium and subsequent strings with openssl or pure-PHP but again not a high priority
return function_exists('sodium_crypto_aead_chacha20poly1305_ietf_encrypt') &&
$this->key_length == 32 &&
(($this->usePoly1305 && !isset($this->poly1305Key) && $this->counter == 0) || $this->counter == 1) &&
!$this->continuousBuffer;
case self::ENGINE_OPENSSL:
// OpenSSL 1.1.0 (released 25 Aug 2016) added support for chacha20.
// PHP didn't support OpenSSL 1.1.0 until 7.0.19 (11 May 2017)
// if you attempt to provide openssl with a 128 bit key (as opposed to a 256 bit key) openssl will null
// pad the key to 256 bits and still use the expansion constant for 256-bit keys. the fact that
// openssl treats the IV as both the counter and nonce, however, let's us use openssl in continuous mode
// whereas libsodium does not
if ($this->key_length != 32) {
return false;
}
}
return parent::isValidEngineHelper($engine);
}
/**
* Encrypts a message.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see self::crypt()
* @param string $plaintext
* @return string $ciphertext
*/
public function encrypt($plaintext)
{
$this->setup();
if ($this->engine == self::ENGINE_LIBSODIUM) {
return $this->encrypt_with_libsodium($plaintext);
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see self::crypt()
* @param string $ciphertext
* @return string $plaintext
*/
public function decrypt($ciphertext)
{
$this->setup();
if ($this->engine == self::ENGINE_LIBSODIUM) {
return $this->decrypt_with_libsodium($ciphertext);
}
return parent::decrypt($ciphertext);
}
/**
* Encrypts a message with libsodium
*
* @see self::encrypt()
* @param string $plaintext
* @return string $text
*/
private function encrypt_with_libsodium($plaintext)
{
$params = [$plaintext, $this->aad, $this->nonce, $this->key];
$ciphertext = strlen($this->nonce) == 8 ?
sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
if (!$this->usePoly1305) {
return substr($ciphertext, 0, strlen($plaintext));
}
$newciphertext = substr($ciphertext, 0, strlen($plaintext));
$this->newtag = $this->usingGeneratedPoly1305Key && strlen($this->nonce) == 12 ?
substr($ciphertext, strlen($plaintext)) :
$this->poly1305($newciphertext);
return $newciphertext;
}
/**
* Decrypts a message with libsodium
*
* @see self::decrypt()
* @param string $ciphertext
* @return string $text
*/
private function decrypt_with_libsodium($ciphertext)
{
$params = [$ciphertext, $this->aad, $this->nonce, $this->key];
if (isset($this->poly1305Key)) {
if ($this->oldtag === false) {
throw new InsufficientSetupException('Authentication Tag has not been set');
}
if ($this->usingGeneratedPoly1305Key && strlen($this->nonce) == 12) {
$plaintext = sodium_crypto_aead_chacha20poly1305_ietf_decrypt(...$params);
$this->oldtag = false;
if ($plaintext === false) {
throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
}
return $plaintext;
}
$newtag = $this->poly1305($ciphertext);
if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) {
$this->oldtag = false;
throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
}
$this->oldtag = false;
}
$plaintext = strlen($this->nonce) == 8 ?
sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
return substr($plaintext, 0, strlen($ciphertext));
}
/**
* Sets the nonce.
*
* @param string $nonce
*/
public function setNonce($nonce)
{
if (!is_string($nonce)) {
throw new \UnexpectedValueException('The nonce should be a string');
}
/*
from https://tools.ietf.org/html/rfc7539#page-7
"Note also that the original ChaCha had a 64-bit nonce and 64-bit
block count. We have modified this here to be more consistent with
recommendations in Section 3.2 of [RFC5116]."
*/
switch (strlen($nonce)) {
case 8: // 64 bits
case 12: // 96 bits
break;
default:
throw new \LengthException('Nonce of size ' . strlen($nonce) . ' not supported by this algorithm. Only 64-bit nonces or 96-bit nonces are supported');
}
$this->nonce = $nonce;
$this->changed = true;
$this->setEngine();
}
/**
* Setup the self::ENGINE_INTERNAL $engine
*
* (re)init, if necessary, the internal cipher $engine
*
* _setup() will be called each time if $changed === true
* typically this happens when using one or more of following public methods:
*
* - setKey()
*
* - setNonce()
*
* - First run of encrypt() / decrypt() with no init-settings
*
* @see self::setKey()
* @see self::setNonce()
* @see self::disableContinuousBuffer()
*/
protected function setup()
{
if (!$this->changed) {
return;
}
$this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter];
$this->changed = $this->nonIVChanged = false;
if ($this->nonce === false) {
throw new InsufficientSetupException('No nonce has been defined');
}
if ($this->key === false) {
throw new InsufficientSetupException('No key has been defined');
}
if ($this->usePoly1305 && !isset($this->poly1305Key)) {
$this->usingGeneratedPoly1305Key = true;
if ($this->engine == self::ENGINE_LIBSODIUM) {
return;
}
$this->createPoly1305Key();
}
$key = $this->key;
if (strlen($key) == 16) {
$constant = 'expand 16-byte k';
$key .= $key;
} else {
$constant = 'expand 32-byte k';
}
$this->p1 = $constant . $key;
$this->p2 = $this->nonce;
if (strlen($this->nonce) == 8) {
$this->p2 = "\0\0\0\0" . $this->p2;
}
}
/**
* The quarterround function
*
* @param int $a
* @param int $b
* @param int $c
* @param int $d
*/
protected static function quarterRound(&$a, &$b, &$c, &$d)
{
// in https://datatracker.ietf.org/doc/html/rfc7539#section-2.1 the addition,
// xor'ing and rotation are all on the same line so i'm keeping it on the same
// line here as well
// @codingStandardsIgnoreStart
$a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 16);
$c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 12);
$a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 8);
$c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 7);
// @codingStandardsIgnoreEnd
}
/**
* The doubleround function
*
* @param int $x0 (by reference)
* @param int $x1 (by reference)
* @param int $x2 (by reference)
* @param int $x3 (by reference)
* @param int $x4 (by reference)
* @param int $x5 (by reference)
* @param int $x6 (by reference)
* @param int $x7 (by reference)
* @param int $x8 (by reference)
* @param int $x9 (by reference)
* @param int $x10 (by reference)
* @param int $x11 (by reference)
* @param int $x12 (by reference)
* @param int $x13 (by reference)
* @param int $x14 (by reference)
* @param int $x15 (by reference)
*/
protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15)
{
// columnRound
static::quarterRound($x0, $x4, $x8, $x12);
static::quarterRound($x1, $x5, $x9, $x13);
static::quarterRound($x2, $x6, $x10, $x14);
static::quarterRound($x3, $x7, $x11, $x15);
// rowRound
static::quarterRound($x0, $x5, $x10, $x15);
static::quarterRound($x1, $x6, $x11, $x12);
static::quarterRound($x2, $x7, $x8, $x13);
static::quarterRound($x3, $x4, $x9, $x14);
}
/**
* The Salsa20 hash function function
*
* On my laptop this loop unrolled / function dereferenced version of parent::salsa20 encrypts 1mb of text in
* 0.65s vs the 0.85s that it takes with the parent method.
*
* If we were free to assume that the host OS would always be 64-bits then the if condition in leftRotate could
* be eliminated and we could knock this done to 0.60s.
*
* For comparison purposes, RC4 takes 0.16s and AES in CTR mode with the Eval engine takes 0.48s.
* AES in CTR mode with the PHP engine takes 1.19s. Salsa20 / ChaCha20 do not benefit as much from the Eval
* approach due to the fact that there are a lot less variables to de-reference, fewer loops to unroll, etc
*
* @param string $x
*/
protected static function salsa20($x)
{
list(, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15) = unpack('V*', $x);
$z0 = $x0;
$z1 = $x1;
$z2 = $x2;
$z3 = $x3;
$z4 = $x4;
$z5 = $x5;
$z6 = $x6;
$z7 = $x7;
$z8 = $x8;
$z9 = $x9;
$z10 = $x10;
$z11 = $x11;
$z12 = $x12;
$z13 = $x13;
$z14 = $x14;
$z15 = $x15;
// @codingStandardsIgnoreStart
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
// @codingStandardsIgnoreEnd
$x0 += $z0;
$x1 += $z1;
$x2 += $z2;
$x3 += $z3;
$x4 += $z4;
$x5 += $z5;
$x6 += $z6;
$x7 += $z7;
$x8 += $z8;
$x9 += $z9;
$x10 += $z10;
$x11 += $z11;
$x12 += $z12;
$x13 += $z13;
$x14 += $z14;
$x15 += $z15;
return pack('V*', $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15);
}
}

Some files were not shown because too many files have changed in this diff Show More