Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cose Lib Laravel Package

web-auth/cose-lib

PHP 8.1+ COSE (RFC 9052/9053) library supporting Sign1/Sign, Encrypt0/Encrypt, Mac0/Mac with full tag support. Implements ECDSA, EdDSA, RSA and HMAC algorithms for signing, encryption and MAC; compatible with WebAuthn/FIDO2.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require web-auth/cose-lib spomky-labs/cbor-php
    

    Ensure PHP 8.1+ with ext-json and ext-openssl enabled.

  2. First Use Case: Verify a COSE_Sign1 signature (e.g., from a WebAuthn attestation or EU Digital COVID Certificate):

    use CBOR\Decoder;
    use CBOR\StringStream;
    use CBOR\Tag\TagManager;
    use Cose\Signature\CoseSign1Tag;
    
    $tagManager = TagManager::create()->add(CoseSign1Tag::class);
    $decoder = Decoder::create($tagManager);
    $coseSign1 = $decoder->decode(new StringStream($cborData));
    
    // Extract components for verification
    $payload = $coseSign1->getPayload();
    $signature = $coseSign1->getSignature();
    
  3. Where to Look First:

    • doc/Usage.md: Detailed API reference and examples for all COSE tags.
    • src/Cose/: Core classes (CoseSign1Tag, CoseEncrypt0Tag, etc.).
    • Test suite: Real-world examples (e.g., tests/Integration/ for COVID certificates).

Implementation Patterns

Workflows

1. Signing Data (COSE_Sign1)

// Generate key (e.g., ECDSA P-256)
$key = openssl_pkey_new(['digest_alg' => 'sha256']);

// Sign payload
openssl_sign($payload, $signature, $key, OPENSSL_ALGO_DSA);

// Create COSE_Sign1
$protectedHeader = MapObject::create([
    MapItem::create(UnsignedIntegerObject::create(1), NegativeIntegerObject::create(-7)) // alg: ES256
]);
$coseSign1 = CoseSign1Tag::create(
    $protectedHeader,
    MapObject::create(), // unprotected header
    ByteStringObject::create($payload),
    ByteStringObject::create($signature)
);

2. Verifying Signatures

// Decode COSE_Sign1
$coseSign1 = $decoder->decode(new StringStream($cborData));

// Reconstruct Sig_structure (RFC 9052 §3.1)
$sigStructure = Signature1::create(
    $coseSign1->getProtectedHeader(),
    $coseSign1->getPayload()
);

// Verify with OpenSSL
$isValid = openssl_verify(
    (string) $sigStructure,
    $coseSign1->getSignature()->getValue(),
    $publicKey,
    'sha256'
);

3. Encryption (COSE_Encrypt0)

// Encrypt payload (e.g., using libsodium)
$ciphertext = sodium_crypto_box($payload, $nonce, $publicKey);

// Create COSE_Encrypt0
$protectedHeader = MapObject::create([
    MapItem::create(UnsignedIntegerObject::create(1), UnsignedIntegerObject::create(25)) // alg: XChaCha20Poly1305
]);
$coseEncrypt0 = CoseEncrypt0Tag::create(
    $protectedHeader,
    MapObject::create([/* IV, kid */]),
    ByteStringObject::create($ciphertext)
);

4. Integration with Laravel

  • Service Provider:
    public function register()
    {
        $this->app->singleton(CoseDecoder::class, function () {
            return Decoder::create(
                TagManager::create()
                    ->add(CoseSign1Tag::class)
                    ->add(CoseEncrypt0Tag::class),
                OtherObjectManager::create()
            );
        });
    }
    
  • Middleware for COSE Validation:
    public function handle($request, Closure $next)
    {
        $cborData = $request->header('X-COSE-Signature');
        $decoder = app(CoseDecoder::class);
        $coseSign1 = $decoder->decode(new StringStream($cborData));
    
        if (!$this->verifySignature($coseSign1)) {
            abort(403, 'Invalid signature');
        }
        return $next($request);
    }
    

Tips for Daily Use

  1. Reuse Decoders: Create a singleton CoseDecoder instance in Laravel’s service container to avoid reinitializing TagManager/OtherObjectManager on every request.

  2. Algorithm Mappings: Use a helper method to map algorithm IDs (e.g., -7ES256) to OpenSSL constants:

    function getOpenSslAlgorithm(int $coseAlg): string
    {
        return match ($coseAlg) {
            -7 => 'sha256',
            -35 => 'sha384',
            -36 => 'sha512',
            default => throw new \InvalidArgumentException("Unsupported algorithm: $coseAlg"),
        };
    }
    
  3. Error Handling: Wrap COSE operations in try-catch blocks to handle malformed CBOR or unsupported algorithms:

    try {
        $coseSign1 = $decoder->decode($stream);
    } catch (CborException $e) {
        Log::error("Invalid COSE data: {$e->getMessage()}");
        abort(400);
    }
    
  4. Key Management: Store public keys in Laravel’s cache or database with a key_id (from the kid header) for quick lookup:

    $kid = $coseSign1->getUnprotectedHeaderAsMap()->get(4)?->getValue(); // kid
    $publicKey = cache()->get("cose_public_key:$kid");
    

Gotchas and Tips

Pitfalls

  1. Protected Header Decoding:

    • The protectedHeader in COSE_Sign1 is a CBOR-encoded map, not a plain map. Use getProtectedHeaderAsMap() to decode it:
      $protectedHeaderMap = $coseSign1->getProtectedHeaderAsMap();
      $algorithm = $protectedHeaderMap->get(1)?->getValue(); // alg
      
    • Gotcha: If the protected header contains custom CBOR tags (e.g., for key identifiers), pass a custom Decoder to getProtectedHeaderAsMap():
      $customDecoder = Decoder::create(
          TagManager::create()->add(MyCustomTag::class)
      );
      $protectedHeaderMap = $coseSign1->getProtectedHeaderAsMap($customDecoder);
      
  2. Signature Verification:

    • The Sig_structure (RFC 9052 §3.1) must include the protected header and payload. Omitting either will fail verification.
    • For ECDSA, convert the COSE signature to DER format before passing to openssl_verify:
      $derSignature = ECSignature::toAsn1($coseSign1->getSignature()->getValue(), 64);
      
  3. Algorithm Compatibility:

    • RSA-PSS (PS256/384/512): Requires OpenSSL 1.1.1+. Use openssl_sign with OPENSSL_ALGO_RSA_PSS:
      openssl_sign($payload, $signature, $key, OPENSSL_ALGO_RSA_PSS, ['salt_length' => 'auto']);
      
    • EdDSA: Use sodium_crypto_sign_detached and convert the signature to COSE format:
      $signature = sodium_crypto_sign_detached($payload, $privateKey);
      
  4. CBOR Encoding/Decoding:

    • Gotcha: The payload in COSE_Sign1 is a ByteStringObject, but it may represent text or binary data. Always encode/decode strings explicitly:
      $payload = ByteStringObject::create($stringPayload); // For text
      // OR
      $payload = ByteStringObject::create($binaryData);   // For binary
      
  5. Unprotected Headers:

    • Unprotected headers are not integrity-protected. Use them for metadata (e.g., kid, exp), not sensitive data.

Debugging Tips

  1. Inspect CBOR Data: Use spomky-labs/cbor-php’s Encoder to pretty-print COSE objects:

    $encoder = Encoder::create();
    $prettyCbor = $encoder->encode($coseSign1, EncoderOptions::create()->withPrettyPrint());
    
  2. Validate COSE Structures: Check the tag field to ensure you’re handling the correct COSE type:

    if ($coseSign1->getTag() !== 18) {
        throw new \RuntimeException("Expected COSE_Sign1 (tag
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours