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 8152], 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: "2022-09-29T09:34:08-05:00",
  7: {
    1: 38.8882,
    2: 77.0199
  },
  / Enveloped signature object /
  -1: {
    / Signature algorithm = ES256 /
    1: -7,
    / 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'5f21112b9f5c1a93743409596421e673f88e59cf34610d53a7cd00f4017327c8f603ea7344a9286db26c52c2a661dff6e4fabc0f2384fe7d1de6c0a23a3f5c21'
  }
}

The same object expressed as hex-encoded CBOR:

a801a3016a53706163652053686f7002663433352e30300363555344026d737061636573686f702e636f6d03781b465237363330303032313131313130303230303530303134333832047468747470733a2f2f62616e6b6e6574322e6f726705683035373638343031067819323032322d30392d32395430393a33343a30382d30353a303007a201fb404371b089a0275202fb405341460aa64c3020a3012604a401022001215820e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b52258209826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d80658405f21112b9f5c1a93743409596421e673f88e59cf34610d53a7cd00f4017327c8f603ea7344a9286db26c52c2a661dff6e4fabc0f2384fe7d1de6c0a23a3f5c21
Explanation: Labels 1-7 represent application data (which 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:
LabelNameArgumentDescription
1algorithm integerSignature algorithm using COSE identifiers.
3keyId "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.
4publicKey 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.
5certificatePath 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.
6signatureValue byte stringSignature value.
CSF signatures are validated by running the signature validation algorithm over the CBOR binary representation of the map holding the application data including the enveloped signature object, with the highlighted signatureValue (label 6) and its associated argument as the sole exception. Note that the length of the signature object map must be updated (to reflect the removal of an element) during validation.

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'
}

Tagged and Custom Signature Data

The CSF container can be enhanced through a couple of options described in Crypto Options. Such options are included in the data to be signed.

Signature Algorithms

Currently supported signature algorithms:
NameIdentifierCompatibility
HS2565COSE, JOSE
HS3846COSE, JOSE
HS5127COSE, JOSE
Ed25519-8FIDO, PKIX [1]
Ed448-9PKIX [2]
ES256-7COSE, JOSE
ES384-35COSE, JOSE
ES512-36COSE, JOSE
PS256-37COSE, JOSE
PS384-38COSE, JOSE
PS512-39COSE, JOSE
RS256-257COSE, JOSE
RS384-258COSE, JOSE
RS512-259COSE, JOSE
[1] Unfortunately, different standards groups did not in due time realize that existing cryptographic APIs only rarely accept "EdDSA" as a valid signature algorithm. In current FIDO alliance specifications the COSE EdDSA algorithm identifier has therefore been redefined to rather mean EdDSA with an Ed25519 key which is also what CSF follows. PKIX' RFC 8410 makes the same distinction.

[2] CSF extends the scheme used for Ed25519 to Ed448 as well.

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.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.CBORObject;
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 HELLO_LABEL     = new CBORInt(1);
    static final CBORInt ARRAY_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(HELLO_LABEL, new CBORString("Hello Signed CBOR World!"))
            .set(ARRAY_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, CBORObject.decode(signatureObject)).getMap();

        // Fetch a map item.
        String greatings = decodedCbor.get(HELLO_LABEL).getString();
        System.out.println(greatings);
    }
}

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