Signature Support
The CBOR library contains support forCreating
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'
}
}
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:
Label | Name | Argument | Description |
---|---|---|---|
1 | algorithm |
integer | Signature algorithm using COSE identifiers. |
3 | keyId |
"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.
|
4 | publicKey |
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. |
5 | certificatePath |
array | Optional 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.
|
6 | signatureValue |
byte string | Signature value. |
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'
}
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:
Name | Identifier | Compatibility |
---|---|---|
HS256 | 5 | COSE, JOSE |
HS384 | 6 | COSE, JOSE |
HS512 | 7 | COSE, JOSE |
Ed25519 | -8 | FIDO, PKIX [1] |
Ed448 | -9 | PKIX [2] |
ES256 | -7 | COSE, JOSE |
ES384 | -35 | COSE, JOSE |
ES512 | -36 | COSE, JOSE |
PS256 | -37 | COSE, JOSE |
PS384 | -38 | COSE, JOSE |
PS512 | -39 | COSE, JOSE |
RS256 | -257 | COSE, JOSE |
RS384 | -258 | COSE, JOSE |
RS512 | -259 | COSE, JOSE |
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);
}
}
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'
}
}
1: "Hello Signed CBOR World!",
2: [-4.5, true],
-1: {
1: 5,
6: h'56e814042baa44e5a0614f576ed95587abaa3af4183b16e0c5b8cff24680a532'
}
}
The resulting signed object expressed in hexadecimal:
a301781848656c6c6f205369676e65642043424f5220576f726c64210282f9c480f520a2010506582056e814042baa44e5a0614f576ed95587abaa3af4183b16e0c5b8cff24680a532