feat: add S3-compatible storage provider (MinIO, Ceph, R2, etc.)
Adds a new 'S3-Compatible Storage' provider that works with any
S3-API-compatible object storage service, including MinIO, Ceph,
Cloudflare R2, Backblaze B2, and others.
Changes:
- New provider class: classes/providers/storage/s3-compatible-provider.php
- Provider key: s3compatible
- Reads user-configured endpoint URL from settings
- Uses path-style URL access (required by most S3-compatible services)
- Supports credentials via AS3CF_S3COMPAT_ACCESS_KEY_ID /
AS3CF_S3COMPAT_SECRET_ACCESS_KEY wp-config.php constants
- Disables AWS-specific features (Block Public Access, Object Ownership)
- New provider SVG icons (s3compatible.svg, -link.svg, -round.svg)
- Registered provider in main plugin class with endpoint setting support
- Updated StorageProviderSubPage to show endpoint URL input for S3-compatible
- Built pro settings bundle with rollup (Svelte 4.2.19)
- Added package.json and updated rollup.config.mjs for pro-only builds
This commit is contained in:
161
vendor/Aws3/Aws/Crypto/Polyfill/AesGcm.php
vendored
Normal file
161
vendor/Aws3/Aws/Crypto/Polyfill/AesGcm.php
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws\Crypto\Polyfill;
|
||||
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Exception\CryptoPolyfillException;
|
||||
use InvalidArgumentException;
|
||||
use RangeException;
|
||||
/**
|
||||
* Class AesGcm
|
||||
*
|
||||
* This provides a polyfill for AES-GCM encryption/decryption, with caveats:
|
||||
*
|
||||
* 1. Only 96-bit nonces are supported.
|
||||
* 2. Only 128-bit authentication tags are supported. (i.e. non-truncated)
|
||||
*
|
||||
* Supports AES key sizes of 128-bit, 192-bit, and 256-bit.
|
||||
*/
|
||||
class AesGcm
|
||||
{
|
||||
use NeedsTrait;
|
||||
/** @var Key $aesKey */
|
||||
private $aesKey;
|
||||
/** @var int $keySize */
|
||||
private $keySize;
|
||||
/** @var int $blockSize */
|
||||
protected $blockSize = 8192;
|
||||
/**
|
||||
* AesGcm constructor.
|
||||
*
|
||||
* @param Key $aesKey
|
||||
* @param int $keySize
|
||||
* @param int $blockSize
|
||||
*
|
||||
* @throws CryptoPolyfillException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws RangeException
|
||||
*/
|
||||
public function __construct(Key $aesKey, $keySize = 256, $blockSize = 8192)
|
||||
{
|
||||
/* Preconditions: */
|
||||
self::needs(\in_array($keySize, [128, 192, 256], \true), "Key size must be 128, 192, or 256 bits; {$keySize} given", InvalidArgumentException::class);
|
||||
self::needs(\is_int($blockSize) && $blockSize > 0 && $blockSize <= \PHP_INT_MAX, 'Block size must be a positive integer.', RangeException::class);
|
||||
self::needs($aesKey->length() << 3 === $keySize, 'Incorrect key size; expected ' . $keySize . ' bits, got ' . ($aesKey->length() << 3) . ' bits.');
|
||||
$this->aesKey = $aesKey;
|
||||
$this->keySize = $keySize;
|
||||
}
|
||||
/**
|
||||
* Encryption interface for AES-GCM
|
||||
*
|
||||
* @param string $plaintext Message to be encrypted
|
||||
* @param string $nonce Number to be used ONCE
|
||||
* @param Key $key AES Key
|
||||
* @param string $aad Additional authenticated data
|
||||
* @param string &$tag Reference to variable to hold tag
|
||||
* @param int $keySize Key size (bits)
|
||||
* @param int $blockSize Block size (bytes) -- How much memory to buffer
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function encrypt($plaintext, $nonce, Key $key, $aad, &$tag, $keySize = 256, $blockSize = 8192)
|
||||
{
|
||||
self::needs(self::strlen($nonce) === 12, 'Nonce must be exactly 12 bytes', InvalidArgumentException::class);
|
||||
$encryptor = new AesGcm($key, $keySize, $blockSize);
|
||||
list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad);
|
||||
$ciphertext = \openssl_encrypt($plaintext, "aes-{$encryptor->keySize}-ctr", $key->get(), \OPENSSL_NO_PADDING | \OPENSSL_RAW_DATA, $nonce . "\x00\x00\x00\x02");
|
||||
/* Calculate auth tag in a streaming fashion to minimize memory usage: */
|
||||
$ciphertextLength = self::strlen($ciphertext);
|
||||
for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) {
|
||||
$cBlock = new ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));
|
||||
$gmac->update($cBlock);
|
||||
}
|
||||
$tag = $gmac->finish($aadLength, $ciphertextLength)->toString();
|
||||
return $ciphertext;
|
||||
}
|
||||
/**
|
||||
* Decryption interface for AES-GCM
|
||||
*
|
||||
* @param string $ciphertext Ciphertext to decrypt
|
||||
* @param string $nonce Number to be used ONCE
|
||||
* @param Key $key AES key
|
||||
* @param string $aad Additional authenticated data
|
||||
* @param string $tag Authentication tag
|
||||
* @param int $keySize Key size (bits)
|
||||
* @param int $blockSize Block size (bytes) -- How much memory to buffer
|
||||
* @return string Plaintext
|
||||
*
|
||||
* @throws CryptoPolyfillException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function decrypt($ciphertext, $nonce, Key $key, $aad, &$tag, $keySize = 256, $blockSize = 8192)
|
||||
{
|
||||
/* Precondition: */
|
||||
self::needs(self::strlen($nonce) === 12, 'Nonce must be exactly 12 bytes', InvalidArgumentException::class);
|
||||
$encryptor = new AesGcm($key, $keySize, $blockSize);
|
||||
list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad);
|
||||
/* Calculate auth tag in a streaming fashion to minimize memory usage: */
|
||||
$ciphertextLength = self::strlen($ciphertext);
|
||||
for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) {
|
||||
$cBlock = new ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));
|
||||
$gmac->update($cBlock);
|
||||
}
|
||||
/* Validate auth tag in constant-time: */
|
||||
$calc = $gmac->finish($aadLength, $ciphertextLength);
|
||||
$expected = new ByteArray($tag);
|
||||
self::needs($calc->equals($expected), 'Invalid authentication tag');
|
||||
/* Return plaintext if auth tag check succeeded: */
|
||||
return \openssl_decrypt($ciphertext, "aes-{$encryptor->keySize}-ctr", $key->get(), \OPENSSL_NO_PADDING | \OPENSSL_RAW_DATA, $nonce . "\x00\x00\x00\x02");
|
||||
}
|
||||
/**
|
||||
* Initialize a Gmac object with the nonce and this object's key.
|
||||
*
|
||||
* @param string $nonce Must be exactly 12 bytes long.
|
||||
* @param string|null $aad
|
||||
* @return array
|
||||
*/
|
||||
protected function gmacInit($nonce, $aad = null)
|
||||
{
|
||||
$gmac = new Gmac($this->aesKey, $nonce . "\x00\x00\x00\x01", $this->keySize);
|
||||
$aadBlock = new ByteArray($aad);
|
||||
$aadLength = $aadBlock->count();
|
||||
$gmac->update($aadBlock);
|
||||
$gmac->flush();
|
||||
return [$aadLength, $gmac];
|
||||
}
|
||||
/**
|
||||
* Calculate the length of a string.
|
||||
*
|
||||
* Uses the appropriate PHP function without being brittle to
|
||||
* mbstring.func_overload.
|
||||
*
|
||||
* @param string $string
|
||||
* @return int
|
||||
*/
|
||||
protected static function strlen($string)
|
||||
{
|
||||
if (\is_callable('\\mb_strlen')) {
|
||||
return (int) \mb_strlen($string, '8bit');
|
||||
}
|
||||
return (int) \strlen($string);
|
||||
}
|
||||
/**
|
||||
* Return a substring of the provided string.
|
||||
*
|
||||
* Uses the appropriate PHP function without being brittle to
|
||||
* mbstring.func_overload.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $offset
|
||||
* @param int|null $length
|
||||
* @return string
|
||||
*/
|
||||
protected static function substr($string, $offset = 0, $length = null)
|
||||
{
|
||||
if (\is_callable('\\mb_substr')) {
|
||||
return \mb_substr($string, $offset, $length, '8bit');
|
||||
} elseif (!\is_null($length)) {
|
||||
return \substr($string, $offset, $length);
|
||||
}
|
||||
return \substr($string, $offset);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user