Signature Support

The CBOR library contains support for Creating and Validating signatures using a specific scheme called "CBOR Signature Format" (CSF).

Note that unlike COSE [RFC 9052], CSF leverages Deterministic Encoding, enabling constructs like the following:

{
  1: {
    1: "Space Shop",
    2: "435.00",
    3: "USD"
  },
  2: "spaceshop.com",
  3: "FR7630002111110020050014382",
  4: "https://banknet2.org",
  5: "05768401",
  6: "2024-08-22T09:34:08-05:00",
  7: {
    1: 38.8882,
    2: 77.0199
  },
  / Enveloped signature object /
  -1: {
    / Signature algorithm = ESP256 /
    1: -9,
    / Public key descriptor in COSE format /
    4: {
      / kty = EC /
      1: 2,
      / crv = P-256 /
      -1: 1,
      / x /
      -2: h'e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b5',
      / y /
      -3: h'9826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8'
    },
    / Signature value /
    6: h'fdfe515197a79f1ebf149ee8a107c7578a7ec90bb7452df972f11f6105479b05963f2fe1f5a79fa9515af9bd5eece61a90b346527fe34b59b18732361be55746'
  }
}

The same object expressed as hex-encoded CBOR:

a801a3016a53706163652053686f7002663433352e30300363555344026d737061636573686f702e636f6d03781b465237363330303032313131313130303230303530303134333832047468747470733a2f2f62616e6b6e6574322e6f726705683035373638343031067819323032342d30382d32325430393a33343a30382d30353a303007a201fb404371b089a0275202fb405341460aa64c3020a3012804a401022001215820e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b52258209826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8065840fdfe515197a79f1ebf149ee8a107c7578a7ec90bb7452df972f11f6105479b05963f2fe1f5a79fa9515af9bd5eece61a90b346527fe34b59b18732361be55746

The sample was signed using the following COSE private key (here in diagnostic notation):

{
  1: 2,
  -1: 1,
  -2: h'e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b5',
  -3: h'9826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8',
  -4: h'e97c4c15785c613e5037dc394c88366922ac6dc8fea63e019d990aed93ade01f'
}
Explanation: Labels 1-7 represent application data (which for compatibility with CSF must be supplied in a CBOR map), while the application-specific label -1 holds an associated enveloped signature.

Note that the application-specific labels (including the one holding the signature object), may be of any CBOR type. The only requirement is that they are unique.

Signatures must for compatibility with CSF, be provided as a CBOR map having a fixed set of labels according to the following table:
NameLabelArgumentDescription
customData0 "Any"Optional: data included in the CSF container. Also see Crypto Options.
algorithm1 integerSignature algorithm using COSE identifiers.
keyId3 "Any"Optional: key identifier using any valid CBOR object. Note that keyId must not be used together with publicKey or certificatePath. A compliant keyId must uniquely identify a specific signature key.
publicKey4 map Optional: public key in COSE format. Note that ECDSA public keys must be supplied with a value parameter for the y-coordinate as well. The public key object must not contain any additional information like key identifiers or preferred signature algorithms.
certificatePath5 arrayOptional: certificate path supplied as an array of byte string objects holding X.509 certificates in DER format, where the first object must be the signature certificate. Signature objects must not contain both certificatePath and publicKey elements.
signatureValue6 byte stringSignature value.

Signature Validation

The CBORValidator super classes perform core CSF validation according to the following set of rules. while leaving policy enforcements to the signature-using application.
  • CSF signatures are validated by running the signature validation algorithm (gathered from label 1), over the CBOR binary representation of the map object holding the application data including the enveloped signature object, with the signatureValue (label 6) and its associated argument as the sole exception (highlighted in the example). Note that the length of the signature object map must be updated to reflect the removal of the signatureValue attribute during validation.
  • Signature validation keys are either implicit or provided via keyId (label 3) attributes.
  • If publicKey (label 4) or certificatePath (label 5) attributes are supplied, they must be used as signature validation keys. Note though that such keys must also be verified to be known and trusted by the signature-using application.
  • Signature validation keys must be checked for compatibility with the algorithm (label 1) attribute.

Tagged and Custom Signature Data

CSF containers can be further enhanced through a couple of options described in Crypto Options.

Signature Algorithms

Currently supported COSE signature algorithms:
NameIdentifierCompatibility
HS2565
HS3846
HS5127
Ed25519-501
Ed448-511
ESP256-91
ESP384-481
ESP512-491
PS256-37
PS384-38
PS512-39
RS256-257
RS384-258
RS512-259

[1] Updated for compliance with https://datatracker.ietf.org/doc/draft-ietf-jose-fully-specified-algorithms/.

Test Vectors

An extensive set of test vectors is currently available at: https://github.com/cyberphone/openkeystore/tree/master/testdata/cbor-signatures .

Use CBORPrinter to list contents in a human-friendly way.

Using the Signature API

The following section outlines how the signature API is supposed to be used.

Sample program:

package cbor_api_demo;

import org.webpki.cbor.CBORArray;
import org.webpki.cbor.CBORBoolean;
import org.webpki.cbor.CBORDecoder;
import org.webpki.cbor.CBORFloat;
import org.webpki.cbor.CBORHmacSigner;
import org.webpki.cbor.CBORHmacValidator;
import org.webpki.cbor.CBORInt;
import org.webpki.cbor.CBORMap;
import org.webpki.cbor.CBORString;

import org.webpki.crypto.HmacAlgorithms;

import org.webpki.util.HexaDecimal;

public class SignatureDemo {
    
    static final byte[] HMAC_KEY = HexaDecimal.decode(
            "7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a");
    
    static final CBORInt GREETINGS_LABEL  = new CBORInt(1);
    static final CBORInt OTHER_DATA_LABEL = new CBORInt(2);
    static final CBORInt SIGNATURE_LABEL  = new CBORInt(-1);
    
    public static void main(String[] args) {
        // Create CBOR data to be signed.
        CBORMap dataToBeSigned = new CBORMap()
            .set(GREETINGS_LABEL, new CBORString("Hello Signed CBOR World!"))
            .set(OTHER_DATA_LABEL, new CBORArray()
                .add(new CBORFloat(-4.5))
                .add(new CBORBoolean(true)));
        
        // Sign data using CSF.
        byte[] signatureObject = new CBORHmacSigner(HMAC_KEY, HmacAlgorithms.HMAC_SHA256)
            .sign(SIGNATURE_LABEL, dataToBeSigned).encode();
        
        // Validate CSF object.
        CBORMap decodedCbor = new CBORHmacValidator(HMAC_KEY)
            .validate(SIGNATURE_LABEL, CBORDecoder.decode(signatureObject)).getMap();

        // Fetch a map item.
        System.out.println(decodedCbor.get(GREETINGS_LABEL).getString());
    }
}

The resulting signed object in diagnostic notation:

{
  1: "Hello Signed CBOR World!",
  2: [-4.5, true],
  -1: {
    1: 5,
    6: h'56e814042baa44e5a0614f576ed95587abaa3af4183b16e0c5b8cff24680a532'
  }
}

The resulting signed object expressed in hexadecimal:

a301781848656c6c6f205369676e65642043424f5220576f726c64210282f9c480f520a2010506582056e814042baa44e5a0614f576ed95587abaa3af4183b16e0c5b8cff24680a532