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:
173
vendor/Aws3/Aws/Crypto/Polyfill/Gmac.php
vendored
Normal file
173
vendor/Aws3/Aws/Crypto/Polyfill/Gmac.php
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws\Crypto\Polyfill;
|
||||
|
||||
/**
|
||||
* Class Gmac
|
||||
*/
|
||||
class Gmac
|
||||
{
|
||||
use NeedsTrait;
|
||||
const BLOCK_SIZE = 16;
|
||||
/** @var ByteArray $buf */
|
||||
protected $buf;
|
||||
/** @var int $bufLength */
|
||||
protected $bufLength = 0;
|
||||
/** @var ByteArray $h */
|
||||
protected $h;
|
||||
/** @var ByteArray $hf */
|
||||
protected $hf;
|
||||
/** @var Key $key */
|
||||
protected $key;
|
||||
/** @var ByteArray $x */
|
||||
protected $x;
|
||||
/**
|
||||
* Gmac constructor.
|
||||
*
|
||||
* @param Key $aesKey
|
||||
* @param string $nonce
|
||||
* @param int $keySize
|
||||
*/
|
||||
public function __construct(Key $aesKey, $nonce, $keySize = 256)
|
||||
{
|
||||
$this->buf = new ByteArray(16);
|
||||
$this->h = new ByteArray(\openssl_encrypt(\str_repeat("\x00", 16), "aes-{$keySize}-ecb", $aesKey->get(), \OPENSSL_RAW_DATA | \OPENSSL_NO_PADDING));
|
||||
$this->key = $aesKey;
|
||||
$this->x = new ByteArray(16);
|
||||
$this->hf = new ByteArray(\openssl_encrypt($nonce, "aes-{$keySize}-ecb", $aesKey->get(), \OPENSSL_RAW_DATA | \OPENSSL_NO_PADDING));
|
||||
}
|
||||
/**
|
||||
* Update the object with some data.
|
||||
*
|
||||
* This method mutates this Gmac object.
|
||||
*
|
||||
* @param ByteArray $blocks
|
||||
* @return self
|
||||
*/
|
||||
public function update(ByteArray $blocks)
|
||||
{
|
||||
if ($blocks->count() + $this->bufLength < self::BLOCK_SIZE) {
|
||||
// Write to internal buffer until we reach enough to write.
|
||||
$this->buf->set($blocks, $this->bufLength);
|
||||
$this->bufLength += $blocks->count();
|
||||
return $this;
|
||||
}
|
||||
// Process internal buffer first.
|
||||
if ($this->bufLength > 0) {
|
||||
// 0 <= state.buf_len < BLOCK_SIZE is an invariant
|
||||
$tmp = new ByteArray(self::BLOCK_SIZE);
|
||||
$tmp->set($this->buf->slice(0, $this->bufLength));
|
||||
$remainingBlockLength = self::BLOCK_SIZE - $this->bufLength;
|
||||
$tmp->set($blocks->slice(0, $remainingBlockLength), $this->bufLength);
|
||||
$blocks = $blocks->slice($remainingBlockLength);
|
||||
$this->bufLength = 0;
|
||||
$this->x = $this->blockMultiply($this->x->exclusiveOr($tmp), $this->h);
|
||||
}
|
||||
// Process full blocks.
|
||||
$numBlocks = $blocks->count() >> 4;
|
||||
for ($i = 0; $i < $numBlocks; ++$i) {
|
||||
$tmp = $blocks->slice($i << 4, self::BLOCK_SIZE);
|
||||
$this->x = $this->blockMultiply($this->x->exclusiveOr($tmp), $this->h);
|
||||
}
|
||||
$last = $numBlocks << 4;
|
||||
// Zero-fill buffer
|
||||
for ($i = 0; $i < 16; ++$i) {
|
||||
$this->buf[$i] = 0;
|
||||
}
|
||||
// Feed leftover into buffer.
|
||||
if ($last < $blocks->count()) {
|
||||
$tmp = $blocks->slice($last);
|
||||
$this->buf->set($tmp);
|
||||
$this->bufLength += $blocks->count() - $last;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Finish processing the authentication tag.
|
||||
*
|
||||
* This method mutates this Gmac object (effectively resetting it).
|
||||
*
|
||||
* @param int $aadLength
|
||||
* @param int $ciphertextLength
|
||||
* @return ByteArray
|
||||
*/
|
||||
public function finish($aadLength, $ciphertextLength)
|
||||
{
|
||||
$lengthBlock = new ByteArray(16);
|
||||
$state = $this->flush();
|
||||
// AES-GCM expects bit lengths, not byte lengths.
|
||||
$lengthBlock->set(ByteArray::enc32be($aadLength >> 29), 0);
|
||||
$lengthBlock->set(ByteArray::enc32be($aadLength << 3), 4);
|
||||
$lengthBlock->set(ByteArray::enc32be($ciphertextLength >> 29), 8);
|
||||
$lengthBlock->set(ByteArray::enc32be($ciphertextLength << 3), 12);
|
||||
$state->update($lengthBlock);
|
||||
$output = $state->x->exclusiveOr($state->hf);
|
||||
// Zeroize the internal values as a best-effort.
|
||||
$state->buf->zeroize();
|
||||
$state->x->zeroize();
|
||||
$state->h->zeroize();
|
||||
$state->hf->zeroize();
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* Get a specific bit from the provided array, at the given index.
|
||||
*
|
||||
* [01234567], 8+[01234567], 16+[01234567], ...
|
||||
*
|
||||
* @param ByteArray $x
|
||||
* @param int $i
|
||||
* @return int
|
||||
*/
|
||||
protected function bit(ByteArray $x, $i)
|
||||
{
|
||||
$byte = $i >> 3;
|
||||
return $x[$byte] >> (7 - $i & 7) & 1;
|
||||
}
|
||||
/**
|
||||
* Galois Field Multiplication
|
||||
*
|
||||
* This function is the critical path that must be constant-time in order to
|
||||
* avoid timing side-channels against AES-GCM.
|
||||
*
|
||||
* The contents of each are always calculated, regardless of the branching
|
||||
* condition, to prevent another kind of timing leak.
|
||||
*
|
||||
* @param ByteArray $x
|
||||
* @param ByteArray $y
|
||||
* @return ByteArray
|
||||
*/
|
||||
protected function blockMultiply(ByteArray $x, ByteArray $y)
|
||||
{
|
||||
static $fieldPolynomial = null;
|
||||
if (!$fieldPolynomial) {
|
||||
$fieldPolynomial = new ByteArray([0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
self::needs($x->count() === 16, 'Argument 1 must be a ByteArray of exactly 16 bytes');
|
||||
self::needs($y->count() === 16, 'Argument 2 must be a ByteArray of exactly 16 bytes');
|
||||
$v = clone $y;
|
||||
$z = new ByteArray(16);
|
||||
for ($i = 0; $i < 128; ++$i) {
|
||||
// if ($b) $z = $z->exclusiveOr($v);
|
||||
$b = $this->bit($x, $i);
|
||||
$z = ByteArray::select($b, $z->exclusiveOr($v), $z);
|
||||
// if ($b) $v = $v->exclusiveOr($fieldPolynomial);
|
||||
$b = $v[15] & 1;
|
||||
$v = $v->rshift();
|
||||
$v = ByteArray::select($b, $v->exclusiveOr($fieldPolynomial), $v);
|
||||
}
|
||||
return $z;
|
||||
}
|
||||
/**
|
||||
* Finish processing any leftover bytes in the internal buffer.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
if ($this->bufLength !== 0) {
|
||||
$this->x = $this->blockMultiply($this->x->exclusiveOr($this->buf), $this->h);
|
||||
$this->bufLength = 0;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user