Skip to content

COSE

COSE produces and checks COSE messages: signatures, message authentication codes, content encryption and key management, over the CBOR message types of spomky-labs/cbor-php, with the cryptographic structures and the header rules of RFC 9052 applied for you. It is used by WebAuthn and FIDO2 relying parties, by verifiers of the EU Digital COVID Certificate, and wherever a CBOR Web Token or a COSE message has to be produced or read.

composer require web-auth/cose-lib spomky-labs/cbor-php
use CBOR\ByteStringObject;
use CBOR\ListObject;
use CBOR\MapItem;
use CBOR\MapObject;
use CBOR\NegativeIntegerObject;
use CBOR\Tag\CoseSign1Tag;
use CBOR\UnsignedIntegerObject;
use Cose\Algorithm\Signature\ECDSA\ES256;
use Cose\Key\Ec2Key;
use Cose\Signature\Signature1;
use Cose\Structure\HeaderMapHelper;

$algorithm = ES256::create();
$key = Ec2Key::create($yourPrivateCoseKey);

// Encode the protected bucket once, so the bytes that are signed are the bytes the message carries.
$protectedHeader = HeaderMapHelper::encodeProtected(MapObject::create([
    MapItem::create(
        UnsignedIntegerObject::create(1),                   // alg
        NegativeIntegerObject::create(ES256::identifier())  // ES256 (-7)
    ),
]));
$payload = ByteStringObject::create('Message to sign');

// The signature covers the Sig_structure, never the payload on its own.
$toBeSigned = Signature1::create($protectedHeader, $payload);
$signature = ByteStringObject::create($algorithm->sign((string) $toBeSigned, $key));

$coseSign1 = CoseSign1Tag::create(ListObject::create([
    $protectedHeader,
    MapObject::create(),
    $payload,
    $signature,
]));

$encoded = (string) $coseSign1;

What it covers

Chapter What it covers
Messages, structures and headers The six COSE tags, the Sig_structure / MAC_structure / Enc_structure classes, reading headers the RFC 9052 way, detached content, external_aad
Signing and verifying COSE_Sign1 and COSE_Sign, what the application must check, the verification contract, verifying with a certificate, countersignatures
Message authentication codes COSE_Mac0 and COSE_Mac, and why the tag covers the MAC_structure
Encryption COSE_Encrypt0 and COSE_Encrypt, encryptFor(), the nonce
Key management direct, HKDF, AES Key Wrap and ECDH recipients, and the KDF context
Keys Key types and curves, parameter forms, AKP keys, alg / key_ops restrictions, validation, thumbprints
Algorithms Every identifier the library ships, with its RFC reference, ML-DSA included
Header parameters and profiles CBOR Web Tokens, the X.509 parameters, the hash envelope, receipts and timestamp tokens

Where to start

Two things to know before you build on it

A verification tells you the signature is good, not that the message was allowed to say what it says. alg and crit are checks RFC 9052 §3.1 requires of the caller, and a verifier that skips them accepts a message announcing an algorithm it never agreed to. See What the application must check.

A signature or a MAC never covers the payload alone. It covers a structure that binds the protected header and the message type, so a tag cannot be lifted from one message type into another. Handing a bare payload to hash() or sign() produces something no other implementation accepts. See Cryptographic structures.

Security recommendations collects every such decision in one place.

The message classes come from cbor-php

The six COSE message types are CBOR\Tag\CoseSign1Tag and its siblings, shipped by spomky-labs/cbor-php 3.4.0 or later; this library owns what RFC 9052 reads into them. The Cose\...Tag classes it used to ship are deprecated, see Upgrading.