<?php
namespace App\Support\Security;
use Aws\Kms\KmsClient; use RuntimeException;
final class AwsKmsEnvelopeEncryption implements EnvelopeEncryption {
 public function __construct(private KmsClient $kms,private string $keyId){}
 public function encrypt(string $plaintext, array $context=[]): array { $dataKey=$this->kms->generateDataKey(['KeyId'=>$this->keyId,'KeySpec'=>'AES_256','EncryptionContext'=>$context]); $plain=(string)$dataKey['Plaintext']; $nonce=random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $cipher=sodium_crypto_secretbox($plaintext,$nonce,$plain); sodium_memzero($plain); return ['ciphertext'=>base64_encode($nonce.$cipher),'encrypted_data_key'=>base64_encode((string)$dataKey['CiphertextBlob']),'kid'=>$this->keyId,'context'=>$context]; }
 public function decrypt(array $payload,array $context=[]): string { $dek=$this->kms->decrypt(['CiphertextBlob'=>base64_decode($payload['encrypted_data_key'],true),'EncryptionContext'=>$context,'KeyId'=>$payload['kid']??$this->keyId]); $plain=(string)$dek['Plaintext']; $raw=base64_decode($payload['ciphertext'],true); $nonce=substr($raw,0,SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $cipher=substr($raw,SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $value=sodium_crypto_secretbox_open($cipher,$nonce,$plain); sodium_memzero($plain); if($value===false) throw new RuntimeException('Unable to decrypt envelope.'); return $value; }
}
