web-auth/cose-lib
PHP 8.1+ COSE (RFC 9052/9053) library: sign, encrypt, and MAC with full tag support (Sign1/Sign, Encrypt0/Encrypt, Mac0/Mac). Supports ECDSA, EdDSA, RSA, and HMAC. Compatible with WebAuthn/FIDO2.
Install Dependencies:
composer require web-auth/cose-lib spomky-labs/cbor-php
web-auth/cose-lib provides COSE logic.spomky-labs/cbor-php handles CBOR encoding/decoding (required for COSE tags).First Use Case: Verify a COSE_Sign1 Signature
use CBOR\Decoder;
use CBOR\StringStream;
use CBOR\Tag\TagManager;
use Cose\Signature\CoseSign1Tag;
// Initialize CBOR decoder with COSE tag support
$decoder = Decoder::create(
TagManager::create()->add(CoseSign1Tag::class)
);
// Decode CBOR data
$coseSign1 = $decoder->decode(new StringStream($cborData));
// Verify signature (example with OpenSSL)
$isValid = openssl_verify(
(string) $coseSign1->getSigStructure(),
$coseSign1->getSignature()->getValue(),
$publicKey,
'sha256'
);
doc/Usage.md for API reference.use Cose\Signature\CoseSign1Tag;
use CBOR\MapObject;
// 1. Define headers (alg, kid, etc.)
$protectedHeader = MapObject::create([
[1, -7], // alg: ES256
[4, 'key-id'], // kid: Key identifier
]);
// 2. Sign payload (e.g., using `web-token/jwt-framework` or `paragonie/sodium`)
$signature = signData($payload, $privateKey);
// 3. Create COSE_Sign1
$coseSign1 = CoseSign1Tag::create(
$protectedHeader,
MapObject::create(), // unprotectedHeader
$payload,
$signature
);
// 4. Encode to CBOR
$cborData = (string) $coseSign1;
// Decode COSE_Sign1
$coseSign1 = $decoder->decode(new StringStream($cborData));
// Verify using OpenSSL (or Sodium, etc.)
$isValid = openssl_verify(
(string) $coseSign1->getSigStructure(),
$coseSign1->getSignature()->getValue(),
$publicKey,
'sha256'
);
use Cose\Encryption\CoseEncrypt0Tag;
// 1. Encrypt payload (e.g., using `paragonie/sodium`)
$ciphertext = encryptData($payload, $publicKey);
// 2. Create COSE_Encrypt0
$coseEncrypt0 = CoseEncrypt0Tag::create(
MapObject::create([ [1, -35] ]), // alg: ES384
MapObject::create([ [4, 'recipient-id'] ]), // kid
$ciphertext
);
$coseEncrypt0 = $decoder->decode(new StringStream($cborData));
$plaintext = decryptData(
$coseEncrypt0->getCiphertext(),
$privateKey
);
use Cose\Mac\CoseMac0Tag;
// 1. Compute MAC (e.g., using HMAC-SHA256)
$macTag = hash_hmac('sha256', $payload, $key, true);
// 2. Create COSE_Mac0
$coseMac0 = CoseMac0Tag::create(
MapObject::create([ [1, 5] ]), // alg: HS256
MapObject::create(),
$payload,
$macTag
);
$coseMac0 = $decoder->decode(new StringStream($cborData));
$computedMac = hash_hmac('sha256', $coseMac0->getPayload(), $key, true);
$isValid = hash_equals($computedMac, $coseMac0->getTag());
// app/Providers/CoseServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use CBOR\Decoder;
use CBOR\Tag\TagManager;
use Cose\Signature\CoseSign1Tag;
class CoseServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('cose.decoder', function () {
return Decoder::create(
TagManager::create()->add(CoseSign1Tag::class)
);
});
}
}
use Illuminate\Support\Facades\Cose;
public function verifySignature(Request $request)
{
$decoder = app('cose.decoder');
$coseSign1 = $decoder->decode(new StringStream($request->cbor_data));
return response()->json([
'valid' => openssl_verify(
(string) $coseSign1->getSigStructure(),
$coseSign1->getSignature()->getValue(),
$this->publicKey,
'sha256'
)
]);
}
config/cose.php):
'keys' => [
'private' => [
'es256' => '-----BEGIN PRIVATE KEY-----...',
],
'public' => [
'es256' => '-----BEGIN PUBLIC KEY-----...',
],
],
TagManager is Required: Always register COSE tags (e.g., CoseSign1Tag) with the TagManager before decoding:
$decoder = Decoder::create(
TagManager::create()->add(CoseSign1Tag::class)
);
Without this, COSE tags will be decoded as raw CBOR objects.
Protected Header Decoding: Use getProtectedHeaderAsMap() to decode the protected header (which may contain nested CBOR tags):
$protectedHeaderMap = $coseSign1->getProtectedHeaderAsMap();
-7 for ES256). Ensure your cryptographic library matches:
$protectedHeader = MapObject::create([ [1, -7] ]); // ES256
$derSignature = ECSignature::toAsn1($signatureBytes, 64);
$isValid = hash_equals($computedMac, $coseMac0->getTag());
Decoder once (e.g., as a Laravel service) and reuse it:
$decoder = Decoder::create(
TagManager::create()->add(CoseSign1Tag::class)
);
ByteStringObject directly where possible to avoid unnecessary conversions:
$payload = $coseSign1->getPayload(); // ByteStringObject
$signature = $coseSign1->getSignature()->getValue(); // string (if needed)
cbor.php's built-in tools to debug CBOR structures:
use CBOR\Debug\Debugger;
Debugger::debug($coseSign1);
How can I help you explore Laravel packages today?