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.
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¶
- Signing or verifying a message — COSE_Sign1, then
What the application must check before you rely on a
verification that returned
true. - Reading a message someone handed you — Reading headers.
- Encrypting for several recipients — COSE_Encrypt and Key management.
- Verifying a CBOR Web Token — CBOR Web Tokens.
- Choosing an identifier — Algorithms.
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.