1. Encryption Support

The CBOR library contains support for Encrypting and Decrypting arbitrary binary data using a specific scheme called "CBOR Encryption Format" (CEF). In similarity to CSF, CEF also depends on Deterministic Encoding which is used to create authentication data to content encryption algorithms like GCM.

The following is a CEF object (here expressed in CBOR diagnostic notation), using the algorithms A256GCM and ECDH-ES+A256KW for content- respectively key-wrapping:

{
  / Content encryption algorithm = A256GCM /
  1: 3,
  / Key encryption object /
  2: {
    / Key encryption algorithm = ECDH-ES+A256KW /
    1: -31,
    / Key Id /
    3: "example.com:x25519",
    / Ephemeral public key descriptor in COSE format /
    7: {
      / kty = OKP /
      1: 1,
      / crv = X25519 /
      -1: 4,
      / x /
      -2: h'c219e35a9c09bfcf1bd6c6dcd1e05ecb36cb6f465d9caeb101795e33fd7db112'
    },
    / CipherText (Encrypted key) /
    10: h'842916c5c81f8a815ec5ef2a472981b7300bc33fe748928c26e8c4dfff7a4747ecff9caea7040585'
  },
  / Tag /
  8: h'84ad6926aa92d0de56e4674abf863390',
  / Initialization Vector (IV) /
  9: h'c7cc6f77b9f984c15bc3cbeb',
  / Ciphertext (Encrypted Content) /
  10: h'625c2b2a41907547b2210624e1e818991c00790aed16'
}

The same object expressed as hex-encoded CBOR:

a5010302a401381e03726578616d706c652e636f6d3a78323535313907a301012004215820c219e35a9c09bfcf1bd6c6dcd1e05ecb36cb6f465d9caeb101795e33fd7db1120a5828842916c5c81f8a815ec5ef2a472981b7300bc33fe748928c26e8c4dfff7a4747ecff9caea7040585085084ad6926aa92d0de56e4674abf863390094cc7cc6f77b9f984c15bc3cbeb0a56625c2b2a41907547b2210624e1e818991c00790aed16

Decryption of this object using the private key (here in JWK format)

{
  "kid": "example.com:x25519",
  "kty": "OKP",
  "crv": "X25519",
  "x": "6ZoM7yBYlJYNmxwFl4UT3MtCoTv7ztUjpRuKEXrV8Aw",
  "d": "cxfl86EVmcqrR07mWENCf1F_5Ni5mt1ViGyERB6Q1vA"
}
should return the UTF-8 encoded string "Hello encrypted world!".

2. Encryption Container

The following figure shows the layout of CEF objects:

CEF object layout
The numbers in paranthesis denote the actual label (map key) value.

2.1. Content Encryption Map

The following table shows the function and type of each element of content encryption maps:
NameLabelArgumentDescription
customData0 "Any"Optional: data included (in clear) in the CEF container. Also see Crypto Options.
algorithm1 intContent encryption algorithm using COSE identifiers.
keyEncryption2 map Optional: Key encryption parameters.
keyId3 "Any" Optional: key identifier using any valid CBOR object. Note that keyId must not be used together with the keyEncryption option. A compliant keyId must uniquely identify a specific signature key.
tag8 bstr Authentication Tag.
iv9 bstrInitialization Vector (IV).
cipherText10 bstrEncrypted content.

2.2. Key Encryption Map

The following table shows the function and type of each element of key encryption maps:
NameLabelArgumentDescription
algorithm1 intKey encryption 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 key encryption key.
publicKey4 map Optional: public key in COSE format. Note that public key objects must not contain additional information like key identifiers or preferred 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 encryption certificate. Key encryption objects must not contain both certificatePath and publicKey elements.
ephemeralKey7 map Optional: public key in COSE format. Used by ECDH algorithms.
cipherText10 bstr Optional: Encrypted key. Used by key-wrapping algorithms.

3. Decryption Process

This section describes the steps needed to decrypt a CEF object (encryption is performed by reversing the process by generating and writing data).

Begin by defining the variables contentEncryptionAlgorithm, contentEncryptionKey, and additionalAuthenticationData.

3.1 Get Content Encryption Algorithm

Perform the following steps:
  1. Decode the Content Encryption Map.
  2. Set contentEncryptionAlgorithm = algorithm.

3.2 Recover Content Encryption Key

For key encryption schemes (= having a keyEncryption attribute), perform the following steps:
  1. Decode the Key Encryption Map.
  2. Determine the privateKey to use. Private keys are either implicit, or are identified through a keyId, publicKey, or certificatePath attribute.
  3. Recover the contentEncryptionKey by applying the algorithm, ephemeralKey, cipherText, and the previously located privateKey to an associated key decryption method, while using the contentEncryptionAlgorithm to get the expected length of the key.
For symmetric-key schemes only, the contentEncryptionKey is either implicit or is located through a keyId attribute.

3.3. Decrypt Content

Finalize the decryption process by performing the following steps:

  1. Read and save the tag, iv, and ciphertext attributes from the Content Encryption Map.
  2. Remove the tag, iv, and ciphertext attributes from the Content Encryption Map. Note that the top level map object must be updated as well to reflect the changed number of elements.
  3. Serialize the remaining CBOR object (including the optional Key Encryption Map), and assign the result to the additionalAuthenticationData (AAD) variable.
  4. Apply the contentEncryptionAlgorithm (in decrypt mode) to the recovered contentEncryptionKey, the attributes read in the step #1, and the additionalAuthenticationData.
The result from step #4 is the decrypted content.

4. Algorithms

4.1. Key Derivation Function (KDF)

ECDH based key encryption schemes must use a Key Derivation Function (KDF) according to HKDF [RFC 5869], profiled as follows:
  • hmac: The HKDF implementation must use HMAC with SHA-256
  • salt: N/A. The default extract mode handling must be implemented.
  • info: This parameter must consist of the actual COSE key encryption algorithm, expressed as a 32-bit (4 byte) signed big-endian integer.

4.2. Content Encryption Algorithms

Currently supported content encryption algorithms:
NameIdentifierCompatibility
A128GCM1COSE, JOSE
A192GCM2COSE, JOSE
A256GCM3COSE, JOSE
A128CBC-HS256200JOSE
A192CBC-HS384201JOSE
A256CBC-HS512202JOSE

4.3. Key Encryption Algorithms

Currently supported key encryption algorithms:
NameIdentifierCompatibilityNotes
ECDH-ES-25COSE, JOSE1, 2
ECDH-ES+A128KW-29COSE, JOSE1, 2
ECDH-ES+A192KW-30COSE, JOSE1, 2
ECDH-ES+A256KW-31COSE, JOSE1, 2
RSA-OAEP-40COSE, JOSE3
RSA-OAEP-256-41COSE, JOSE3
Notes:
  1. CEF and COSE use different profiles for the Key Derivation Function (KDF).
  2. Supported key types: P-256, P-384, P-521, X25519, and X448.
  3. Supported key lengths: 2048, 3072, and 4096

5. Tagged and Custom Encryption Data

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

6. Test Vectors

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

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

7. Using the Encryption API

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

Sample program:

package cbor_api_demo;

import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;

import org.webpki.cbor.CBORAsymKeyDecrypter;
import org.webpki.cbor.CBORAsymKeyEncrypter;
import org.webpki.cbor.CBORKeyPair;
import org.webpki.cbor.CBORDecoder;

import org.webpki.crypto.ContentEncryptionAlgorithms;
import org.webpki.crypto.KeyEncryptionAlgorithms;

import org.webpki.util.HexaDecimal;
import org.webpki.util.UTF8;

public class EncryptionDemo {
    
    // Message encoded in UTF-8.
    static final byte[] SECRET_MESSAGE = UTF8.encode("A very secret message");
    
    // X25519 private key in COSE format.
    static final byte[] X25519_PRIVATE_KEY = HexaDecimal.decode(
            "a401012004215820e99a0cef205894960d9b1c05978513dccb" +
            "42a13bfbced523a51b8a117ad5f00c2358207317e5f3a11599" +
            "caab474ee65843427f517fe4d8b99add55886c84441e90d6f0");
    
    public static void main(String[] args) {
        // Get keys in Java format.
        KeyPair keyPair = CBORKeyPair.convert(CBORDecoder.decode(X25519_PRIVATE_KEY));
        PrivateKey receiverKey = keyPair.getPrivate();
        PublicKey senderKey = keyPair.getPublic();
        
        // Encrypt data using CEF.
        byte[] encryptionObject = new CBORAsymKeyEncrypter(senderKey,
                                                           KeyEncryptionAlgorithms.ECDH_ES,
                                                           ContentEncryptionAlgorithms.A256GCM)
                .encrypt(SECRET_MESSAGE).encode();
        
        // Decrypt data using CEF.
        byte[] decryptedData = new CBORAsymKeyDecrypter(receiverKey)
                .decrypt(CBORDecoder.decode(encryptionObject));
        
        // Assume that the data is a string encoded in UTF-8.
        String secretMessage = UTF8.decode(decryptedData);
        System.out.println(secretMessage);
    }
}

Sample key in diagnostic notation:

{
  1: 1,
  -1: 4,
  -2: h'e99a0cef205894960d9b1c05978513dccb42a13bfbced523a51b8a117ad5f00c',
  -4: h'7317e5f3a11599caab474ee65843427f517fe4d8b99add55886c84441e90d6f0'
}

The resulting encryption object in diagnostic notation:

{
  1: 3,
  2: {
    1: -25,
    7: {
      1: 1,
      -1: 4,
      -2: h'3e9c03b4e2ccb023272fe0f1a5a414645a7e5a0952a3da8199ba46812603ee1a'
    }
  },
  8: h'4ac80be51285309b93b8f4cc38f6b8ba',
  9: h'9fbd6e151bad2af177dd3382',
  10: h'115b48dcffcf88dce70b2173d6c368b2cfe802521c'
}

The resulting encryption object expressed in hexadecimal:

a5010302a201381807a3010120042158203e9c03b4e2ccb023272fe0f1a5a414645a7e5a0952a3da8199ba46812603ee1a08504ac80be51285309b93b8f4cc38f6b8ba094c9fbd6e151bad2af177dd33820a55115b48dcffcf88dce70b2173d6c368b2cfe802521c