- Fix RedisCache driver to handle MGET failures gracefully with fallback - Add comprehensive discovery context comparison debug tools - Identify root cause: WEB context discovery missing 166 items vs CLI - WEB context missing RequestFactory class entirely (52 vs 69 commands) - Improved exception handling with detailed binding diagnostics
261 lines
6.4 KiB
PHP
261 lines
6.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Cryptography;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
/**
|
|
* Digital Signature Result Value Object
|
|
*
|
|
* Represents the result of a digital signature operation including the signature
|
|
* and all metadata needed for verification.
|
|
*/
|
|
final readonly class DigitalSignatureResult
|
|
{
|
|
public function __construct(
|
|
private string $signature,
|
|
private string $algorithm,
|
|
private string $hashAlgorithm,
|
|
private int $keySize,
|
|
private ?string $curve = null
|
|
) {
|
|
if (empty($signature)) {
|
|
throw new InvalidArgumentException('Signature cannot be empty');
|
|
}
|
|
|
|
if (empty($algorithm)) {
|
|
throw new InvalidArgumentException('Algorithm cannot be empty');
|
|
}
|
|
|
|
if (empty($hashAlgorithm)) {
|
|
throw new InvalidArgumentException('Hash algorithm cannot be empty');
|
|
}
|
|
|
|
$supportedAlgorithms = ['rsa', 'ecdsa'];
|
|
if (! in_array($algorithm, $supportedAlgorithms, true)) {
|
|
throw new InvalidArgumentException('Unsupported algorithm');
|
|
}
|
|
|
|
$supportedHashAlgorithms = ['sha256', 'sha384', 'sha512', 'sha224'];
|
|
if (! in_array($hashAlgorithm, $supportedHashAlgorithms, true)) {
|
|
throw new InvalidArgumentException('Unsupported hash algorithm');
|
|
}
|
|
|
|
if ($keySize < 256) {
|
|
throw new InvalidArgumentException('Key size must be at least 256 bits');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the signature bytes
|
|
*/
|
|
public function getSignature(): string
|
|
{
|
|
return $this->signature;
|
|
}
|
|
|
|
/**
|
|
* Get the signature algorithm
|
|
*/
|
|
public function getAlgorithm(): string
|
|
{
|
|
return $this->algorithm;
|
|
}
|
|
|
|
/**
|
|
* Get the hash algorithm used
|
|
*/
|
|
public function getHashAlgorithm(): string
|
|
{
|
|
return $this->hashAlgorithm;
|
|
}
|
|
|
|
/**
|
|
* Get the key size in bits
|
|
*/
|
|
public function getKeySize(): int
|
|
{
|
|
return $this->keySize;
|
|
}
|
|
|
|
/**
|
|
* Get the curve name (ECDSA only)
|
|
*/
|
|
public function getCurve(): ?string
|
|
{
|
|
return $this->curve;
|
|
}
|
|
|
|
/**
|
|
* Get signature as Base64 string
|
|
*/
|
|
public function getSignatureBase64(): string
|
|
{
|
|
return base64_encode($this->signature);
|
|
}
|
|
|
|
/**
|
|
* Get signature as hexadecimal string
|
|
*/
|
|
public function getSignatureHex(): string
|
|
{
|
|
return bin2hex($this->signature);
|
|
}
|
|
|
|
/**
|
|
* Check if this is an RSA signature
|
|
*/
|
|
public function isRsa(): bool
|
|
{
|
|
return $this->algorithm === 'rsa';
|
|
}
|
|
|
|
/**
|
|
* Check if this is an ECDSA signature
|
|
*/
|
|
public function isEcdsa(): bool
|
|
{
|
|
return $this->algorithm === 'ecdsa';
|
|
}
|
|
|
|
/**
|
|
* Get signature length in bytes
|
|
*/
|
|
public function getSignatureLength(): int
|
|
{
|
|
return strlen($this->signature);
|
|
}
|
|
|
|
/**
|
|
* Export to array (for serialization)
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
$data = [
|
|
'signature' => $this->getSignatureBase64(),
|
|
'algorithm' => $this->algorithm,
|
|
'hash_algorithm' => $this->hashAlgorithm,
|
|
'key_size' => $this->keySize,
|
|
];
|
|
|
|
if ($this->curve !== null) {
|
|
$data['curve'] = $this->curve;
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Create from array (for deserialization)
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
public static function fromArray(array $data): self
|
|
{
|
|
$requiredFields = ['signature', 'algorithm', 'hash_algorithm', 'key_size'];
|
|
|
|
foreach ($requiredFields as $field) {
|
|
if (! isset($data[$field])) {
|
|
throw new InvalidArgumentException("Missing required field: {$field}");
|
|
}
|
|
}
|
|
|
|
$signature = base64_decode($data['signature'], true);
|
|
if ($signature === false) {
|
|
throw new InvalidArgumentException('Invalid Base64 signature');
|
|
}
|
|
|
|
return new self(
|
|
signature: $signature,
|
|
algorithm: $data['algorithm'],
|
|
hashAlgorithm: $data['hash_algorithm'],
|
|
keySize: (int)$data['key_size'],
|
|
curve: $data['curve'] ?? null
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create from Base64 signature
|
|
*/
|
|
public static function fromBase64(
|
|
string $base64Signature,
|
|
string $algorithm,
|
|
string $hashAlgorithm,
|
|
int $keySize,
|
|
?string $curve = null
|
|
): self {
|
|
$signature = base64_decode($base64Signature, true);
|
|
if ($signature === false) {
|
|
throw new InvalidArgumentException('Invalid Base64 signature');
|
|
}
|
|
|
|
return new self(
|
|
signature: $signature,
|
|
algorithm: $algorithm,
|
|
hashAlgorithm: $hashAlgorithm,
|
|
keySize: $keySize,
|
|
curve: $curve
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create from hexadecimal signature
|
|
*/
|
|
public static function fromHex(
|
|
string $hexSignature,
|
|
string $algorithm,
|
|
string $hashAlgorithm,
|
|
int $keySize,
|
|
?string $curve = null
|
|
): self {
|
|
$signature = hex2bin($hexSignature);
|
|
if ($signature === false) {
|
|
throw new InvalidArgumentException('Invalid hexadecimal signature');
|
|
}
|
|
|
|
return new self(
|
|
signature: $signature,
|
|
algorithm: $algorithm,
|
|
hashAlgorithm: $hashAlgorithm,
|
|
keySize: $keySize,
|
|
curve: $curve
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get signature description
|
|
*/
|
|
public function getDescription(): string
|
|
{
|
|
$description = strtoupper($this->algorithm);
|
|
|
|
if ($this->algorithm === 'rsa') {
|
|
$description .= " {$this->keySize}-bit";
|
|
} elseif ($this->algorithm === 'ecdsa') {
|
|
$description .= " {$this->curve}";
|
|
}
|
|
|
|
$description .= " with " . strtoupper($this->hashAlgorithm);
|
|
|
|
return $description;
|
|
}
|
|
|
|
/**
|
|
* Get summary information (safe for logging)
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function getSummary(): array
|
|
{
|
|
return [
|
|
'algorithm' => $this->algorithm,
|
|
'hash_algorithm' => $this->hashAlgorithm,
|
|
'key_size' => $this->keySize,
|
|
'curve' => $this->curve,
|
|
'signature_length' => $this->getSignatureLength(),
|
|
'description' => $this->getDescription(),
|
|
];
|
|
}
|
|
}
|