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!".

The CEF decryption process is as follows:

  • Read and save the content encryption parameters tag, iv, and ciphertext.
  • Remove the tag, iv, and ciphertext elements from the encryption object. Note that the top level map object must be updated as well to reflect the changed number of elements.
  • Serialize the remaining CBOR encryption object and set the result to Additional Authentication Data (AAD).
  • Recover the content encryption key using the elements in the key encryption object (argument of label 2).
  • Apply the content encryption algorithm (argument of label 1 in the main map) to the recovered content encryption key, the input parameters read in the first step, and AAD. The result is the decrypted content.
Symmetric key encryption CEF objects are processed as above, but since they do not have a key encryption component, content encryption keys must be known by recipients.

The following figure shows the layout of CEF objects:

CEF object layout
The numbers in paranthesis denote the actual label (map key) value. Properly designed CEF objects must adhere to the following rules:
  • publicKey and ephemeralKey elements must be compliant with COSE, but must not contain any other information than the public key itself and associated parameters.
  • ECDSA public keys must be supplied with a value parameter for the y-coordinate as well.
  • keyId, publicKey and certificatePath must not be combined.
  • If a keyId is used it must be supplied in the main map for symmetric encryption schemes, and in the sub map for schemes using key encryption. A compliant keyId must uniquely identify a specific encryption key.
  • The currently supported key encryption algorithms except for ECDH-ES require that the encrypted key is provided in the chipherText element in the sub map.
  • All ECDH-ES variants must include an ephemeralKey element.
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.

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.

Content Encryption Algorithms

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

Key Encryption Algorithms

Currently supported key encryption algorithms:
NameIdentifierCompatibility
ECDH-ES-25COSE, JOSE [1]
ECDH-ES+A128KW-29COSE, JOSE [1]
ECDH-ES+A192KW-30COSE, JOSE [1]
ECDH-ES+A256KW-31COSE, JOSE [1]
RSA-OAEP-40COSE, JOSE
RSA-OAEP-256-41COSE, JOSE
[1] Note that COSE and JOSE do no use the same Key Derivation Function (KDF).

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.

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