COSE_Sign¶
The COSE_Sign structure supports multiple signatures from different signers.
use CBOR\ByteStringObject;
use CBOR\ListObject;
use CBOR\MapObject;
use CBOR\Tag\CoseSignTag;
use Cose\Signature\CoseSignature;
$protectedHeader = MapObject::create();
$unprotectedHeader = MapObject::create();
$payload = ByteStringObject::create('Document to be signed');
// Create signature structures for each signer
$signatures = ListObject::create([
ListObject::create([
ByteStringObject::create(''), // signature protected header
MapObject::create([/* signer 1 unprotected header */]),
ByteStringObject::create($signature1Bytes)
]),
ListObject::create([
ByteStringObject::create(''),
MapObject::create([/* signer 2 unprotected header */]),
ByteStringObject::create($signature2Bytes)
]),
]);
$coseSign = CoseSignTag::createFromComponents(
$protectedHeader,
$unprotectedHeader,
$payload,
$signatures
);
// Reading them back as checked views rather than raw lists
foreach (CoseSignature::all($coseSign->getSignatures()) as $signer) {
$signerProtectedHeader = $signer->getProtectedHeader(); // ByteStringObject
$kid = $signer->getUnprotectedHeaderParameter(4); // ?CBORObject
$signatureValue = $signer->getSignature(); // ByteStringObject
}
Warning
Each signature of a COSE_Sign covers the Signature structure of
RFC 9052 §4.4, which carries the protected bucket of the
message and the one of the signer's own entry, not the Signature1 structure a COSE_Sign1 uses:
use Cose\Signature\Signature;
$toBeSigned = Signature::create(
$coseSign->getProtectedHeader(), // body_protected
$signer->getProtectedHeader(), // sign_protected
$coseSign->getPayload(),
);
$isValid = $algorithm->verify((string) $toBeSigned, $key, $signer->getSignature()->getValue());
RFC 9052 §4.1 writes the list as [+ COSE_Signature]: at least one entry, each a [bstr, map, bstr] array. The CBOR
layer only checks that the item is a list, so CoseSignature::all() is where that rule is applied: it rejects an
empty list and any entry of another shape.
examples/02-sign-multiple-signers.php signs with two signers and shows
why Signature carries sign_protected.
A signature added after a message is finalized (a notary's, a timestamping service's) is a countersignature, not another signer: it lives in the unprotected bucket and is computed over a different structure. See Countersignatures. A signature over the digest of a payload kept elsewhere is a hash envelope.