cardano-php / cip8-verifier
A PHP library to verify Cardano CIP-8 signed messages.
Requires
- php: >=8.0
- ext-sodium: *
- spomky-labs/cbor-php: ^3.1
Requires (Dev)
- pestphp/pest: ^3.8
- phpstan/phpstan: ^1.10
README
A robust, production-ready PHP library for verifying Cardano CIP-8 signed messages. This library provides a clean, type-safe API for validating cryptographic signatures from Cardano wallets.
Features
- โ Full CIP-8 Compliance - Complete implementation of Cardano's CIP-8 message verification standard
- ๐ Cryptographically Secure - Uses Ed25519 signature verification with proper validation
- ๐๏ธ Clean Architecture - Built following SOLID principles with dependency injection
- ๐ก๏ธ Type Safe - Full PHP 8.1+ type hints and readonly classes
- โก High Performance - Optimized CBOR parsing and efficient algorithms
- ๐งช Thoroughly Tested - Comprehensive test suite ensuring reliability
- ๐ Well Documented - Clear API documentation and usage examples
Requirements
- PHP 8.1 or higher
ext-sodium
extension (for cryptographic operations)spomky-labs/cbor-php
package (for CBOR encoding/decoding)
Installation
Install via Composer:
composer require cardano-php/cip8-verifier
Quick Start
Please refer to the frontend example code to see how to obtain the signatureCbor
, signatureKey
, challengeHex
, expectedSignerStakeAddress
and networkMode
values.
<?php require 'vendor/autoload.php'; use CardanoPhp\CIP8Verifier\CIP8Verifier; use CardanoPhp\CIP8Verifier\DTO\VerificationRequest; use CardanoPhp\CIP8Verifier\Exception\CIP8VerificationException; try { // Your verification data $request = new VerificationRequest( signatureCbor: "84582aa201276761646472657373581de07a9647d2048870a0726f78621863e03797dc17b946473a35ded45f75a166686173686564f4582431633364353630312d386563632d343264662d623162302d3061323934643061346564355840d40e65ebb258bd48d04092f485b845a6c0c9b1728e896c8364e51e1b6d67cd2c36dc17ad52409671a8ac8e2376e3bf138869621d03c28841a50cd68bc34fa108", signatureKey: "a4010103272006215820eb59d52fbd257d3f8f8f51dd59b2013092763fc9cbc109d32d837920be5e62be", challengeHex: "31633364353630312d386563632d343264662d623162302d306132393464306134656435", expectedSignerStakeAddress: "stake_test1upafv37jqjy8pgrjdauxyxrruqme0hqhh9ryww34mm297agc0f3vc", networkMode: 0 ); // Verify the signature $verifier = CIP8Verifier::create(); $result = $verifier->verify($request); // Check results if ($result->isValid) { echo "โ Signature is valid!\n"; } else { echo "โ Signature verification failed\n"; } echo "Stake address matches: " . ($result->stakeAddressMatches ? 'Yes' : 'No') . "\n"; echo "Challenge matches: " . ($result->challengeMatches ? 'Yes' : 'No') . "\n"; echo "Signature validates: " . ($result->signatureValidates ? 'Yes' : 'No') . "\n"; } catch (CIP8VerificationException $e) { echo "โ Verification error: " . $e->getMessage() . "\n"; } catch (Throwable $e) { echo "โ Unexpected error: " . $e->getMessage() . "\n"; }
API Reference
Main Classes
CIP8Verifier
The main orchestrator class that coordinates all verification operations.
// Create a new verifier instance $verifier = CIP8Verifier::create(); // Verify with type-safe DTO $result = $verifier->verify(VerificationRequest $request): VerificationResult
VerificationRequest
(readonly DTO)
Input data structure for verification requests.
new VerificationRequest( string $signatureCbor, // CBOR-encoded signature data string $signatureKey, // Public key in CBOR format string $challengeHex, // Challenge message to verify string $expectedSignerStakeAddress, // Expected stake address int $networkMode // Network mode (0=testnet, 1=mainnet) ) // Factory method from an array VerificationRequest::fromArray(array $data): VerificationRequest
VerificationResult
(readonly DTO)
Output data structure containing verification results.
readonly class VerificationResult { public bool $isValid; // Overall validation result public bool $stakeAddressMatches; // Stake address validation public bool $challengeMatches; // Challenge content validation public bool $signatureValidates; // Cryptographic signature validation } // Convert to array $result->toArray(): array // Factory method VerificationResult::createValid(bool $stakeAddressMatches, bool $challengeMatches, bool $signatureValidates): VerificationResult
Exception Hierarchy
The library provides specific exceptions for different error scenarios:
CardanoPhp\CIP8Verifier\Exception\CIP8VerificationException โโโ InvalidSignatureLengthException โโโ InvalidPublicKeyLengthException
Service Classes
The library follows a modular architecture with focused service classes:
PublicKeyExtractor
- Extracts public keys from signature keysStakeAddressGenerator
- Generates Cardano stake addressesBech32Encoder
- Handles bech32 address encodingCoseParser
- Parses COSE_Sign1 message structuresSignatureVerifier
- Verifies Ed25519 signatures
Usage Examples
Error Handling
use CardanoPhp\CIP8Verifier\CIP8Verifier; use CardanoPhp\CIP8Verifier\DTO\VerificationRequest; use CardanoPhp\CIP8Verifier\Exception\CIP8VerificationException; try { $verifier = CIP8Verifier::create(); $result = $verifier->verify($request); if (!$result->isValid) { // Handle validation failure (invalid signature, mismatched wallet, etc.) error_log("CIP8 verification failed"); } } catch (CIP8VerificationException $e) { // Handle specific CIP8 errors (invalid input format, parsing errors, etc.) error_log("CIP8 error: " . $e->getMessage()); } catch (Throwable $e) { // Handle unexpected errors error_log("Unexpected error: " . $e->getMessage()); }
Dependency Injection
use CardanoPhp\CIP8Verifier\CIP8Verifier; use CardanoPhp\CIP8Verifier\Service\*; // Manual dependency injection $bech32Encoder = new Bech32Encoder(); $stakeAddressGenerator = new StakeAddressGenerator($bech32Encoder); $publicKeyExtractor = new PublicKeyExtractor(); $coseParser = new CoseParser(); $signatureVerifier = new SignatureVerifier(); $verifier = new CIP8Verifier( $publicKeyExtractor, $stakeAddressGenerator, $coseParser, $signatureVerifier );
Architecture
The library follows clean architecture principles with clear separation of concerns:
graph TD A["CIP8Verifier<br/>(Main Orchestrator)"] --> B["VerificationRequest<br/>(Input DTO)"] A --> C["VerificationResult<br/>(Output DTO)"] A --> D["PublicKeyExtractor<br/>(Service)"] A --> E["StakeAddressGenerator<br/>(Service)"] A --> F["CoseParser<br/>(Service)"] A --> G["SignatureVerifier<br/>(Service)"] E --> H["Bech32Encoder<br/>(Service)"] D --> I["CborHelper<br/>(Utility)"] E --> J["Blake2bHasher<br/>(Utility)"] F --> I G --> I G --> J F --> K["CoseSign1<br/>(DTO)"] L["CIP8VerificationException<br/>(Base Exception)"] --> M["InvalidSignatureLengthException"] L --> N["InvalidPublicKeyLengthException"] G --> M G --> NLoading
Network Modes
The library supports both Cardano networks:
- Mainnet:
networkMode = 1
- Testnet:
networkMode = 0
Wallet Compatibility
This library supports signature verification from:
- โ Lite Wallets (Nami, Eternl, etc.) - Direct payload signing
- โ Hardware Wallet (Ledger) - Blake2b hashed payload signing
Security Considerations
- All cryptographic operations use the
sodium
extension for security - Signature validation follows Ed25519 standards
- Public key and signature length validation prevents malformed input attacks
- CBOR parsing is handled securely with proper error handling
Performance
The library is optimized for performance:
- Efficient CBOR parsing and encoding
- Minimal memory allocations
- Fast cryptographic operations using native sodium functions
- Clean service architecture allows for easy optimization
Contributing
Contributions are welcome! Please ensure your code follows:
- PSR-12 coding standards
- Comprehensive test coverage
- Proper type hints
- Clear documentation
Testing
# Run the test suite composer test # Run with coverage composer test:coverage # Run static analysis composer analyze
Changelog
See CHANGELOG.md for version history and changes.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- ๐ Documentation: Check this README and inline code documentation
- ๐ Bug Reports: Open an issue on GitHub
- ๐ก Feature Requests: Open an issue with the
enhancement
label - ๐ฌ Questions: Start a discussion on GitHub Discussions