/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */ /* * Copyright 2016 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * JSON Web Encryption (RFC 7516) * * A JSON Web Encryption (JWE) object contains (usually) two levels of * encryption. First, the plaintext is encrypted with a random symmetric key. * In José, we call this key the Content Encryption Key (CEK). Next, the CEK is * wrapped (encrypted) with one or more keys. These keys are standard JSON Web * Keys (JWK) and may be symmetric or asymmetric. * * Thus there is (usually) one CEK and one or more JWKs. However, there are * some exceptions to this rule. Two such examples are the algorithms: "dir" * and "ECDH-ES". In the first case, the JWK is a symmetric key and is used * directly as the CEK. In the second case, an ECDH key exchange is performed * and the result is used directly as the CEK. But in general, the maxim holds. * * This means that you can encrypt the data using one CEK and then encrypt the * CEK to multiple recipients. With this schema, multiple recipients can each * decrypt the data using their own key without having to send separate * ciphertext to each recipient. * * For maximum flexibility, José structures its API to take advantage of this * schema. For example, when encrypting to two recipients, the code could look * like this (error handling omitted): * * json_t *enc(void *plaintext, size_t len, json_t *jwk0, json_t *jwk1) { * json_auto_t *jwe = json_object(); * json_auto_t *cek = json_object(); * * // Wrap to the first recipient (CEK generated implicitly) * jose_jwe_enc_jwk(NULL, jwe, NULL, jwk0, cek); * * // Wrap to the second recipient * jose_jwe_enc_jwk(NULL, jwe, NULL, jwk1, cek); * * // Encrypt plaintext using the generated CEK * jose_jwe_enc_cek(NULL, jwe, cek, plaintext, len); * * return json_incref(jwe); * } * * However, because José intends to be easy to use, we also provide shortcuts. * For example, you could use a JWKSet which contains multiple JWKs: * * json_t *enc(void *plaintext, size_t len, json_t *jwkset) { * json_auto_t *jwe = json_object(); * * // Perform wrapping and encryption to all recipients * jose_jwe_enc(NULL, jwe, NULL, jwkset, plaintext, len); * * return json_incref(jwe); * } * * Here are two tips to remember. First, let José generate your CEK implicitly. * Second, always perform wrapping before encryption. Both of these tips are * important because some wrapping algorithms may impose constraints on the * generation of the CEK. * * To decrypt a JWE, we just reverse the process. First, we use a JWK to * unwrap the CEK. Then we use the CEK to decrypt the ciphertext. Here is how * that might look in code (again, error handling omitted): * * void *dec(json_t *jwe, json_t *jwk, size_t *len) { * json_auto_t *cek = NULL; * * // Unwrap the CEK using our JWK * cek = jose_jwe_dec_jwk(NULL, jwe, NULL, jwk); * * // Decrypt ciphertext using the CEK * return jose_jwe_dec_cek(NULL, jwe, cek, len); * } * * Or, again, in simplified form: * * void *dec(json_t *jwe, json_t *jwk, size_t *len) { * return jose_jwe_dec(NULL, jwe, NULL, jwk, len); * } * * If you need to forward a JWE to a new recipient, you can do this without * performing re-encryption. Just unwrap the CEK and then wrap the CEK again * using a new JWK. For example: * * void fwd(json_t *jwe, json_t *oldjwk, json_t *newjwk) { * json_auto_t *cek = NULL; * * // Unwrap the CEK using the old JWK * cek = jose_jwe_dec_jwk(NULL, jwe, NULL, oldjwk); * * // Wrap the CEK to the new JWK * jose_jwe_enc_jwk(NULL, jwe, NULL, newjwk, cek); * } * * In all the above examples, parameters like which encryption algorithms to * use were inferred from our keys. Where such an inferrence cannot be made, * sensible and secure defaults were chosen automatically. If you would like * more control over the process, simply set parameters in the appropriate * objects (more on this in the function documentation). For example, * to enable plaintext compression, you can specify the \p zip property * in the JWE Protected Header: * * json_t *enc(void *plaintext, size_t len, json_t *jwkset) { * json_auto_t *jwe = json_pack("{s:{s:s}}", "protected", "zip", "DEF"); * * // Perform compressed wrapping and encryption to all recipients * jose_jwe_enc(NULL, jwe, NULL, jwkset, plaintext, len); * * return json_incref(jwe); * } * * José currently supports the "alg", "enc" and "zip" header parameters, as * well as any algorithm-specific header parameters used by the specific * algorithms we implement. Other header parameters may be specified, but do * not effect the behavior of José's JWE implementation. * * \defgroup jose_jwe JWE * \see https://tools.ietf.org/html/rfc7516 * @{ */ #pragma once #include "cfg.h" #include "io.h" #include #include #include /** * Merges the JOSE headers of a JWE object and a JWE recpient object. * * \param jwe A JWE object. * \param rcp A JWE recipient object. * \return The newly allocated JOSE header. */ json_t * jose_jwe_hdr(const json_t *jwe, const json_t *rcp); /** * Wraps and encrypts plaintext. * * This function is a thin wrapper around the jose_jwe_enc_io() function * allowing the user to specify the plaintext in a single call. The ciphertext * output will be appended to the JWE as the "ciphertext" property. * * \see jose_jwe_enc_cek_io() * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param rcp The JWE recipient object(s) or NULL. * \param jwk The JWK(s) or JWKSet used for wrapping. * \param pt The plaintext. * \param ptl The length of the plaintext. * \return On success, true. Otherwise, false. */ bool jose_jwe_enc(jose_cfg_t *cfg, json_t *jwe, json_t *rcp, const json_t *jwk, const void *pt, size_t ptl); /** * Wraps and encrypts plaintext using streaming. * * This function is a thin wrapper around the jose_jwe_enc_jwk() and * jose_jwe_enc_cek_io() functions, removing the complexity of managing the CEK. * * \see jose_jwe_enc_jwk() * \see jose_jwe_enc_cek_io() * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param rcp The JWE recipient object(s) or NULL. * \param jwk The JWK(s) or JWKSet used for wrapping. * \param next The next IO object in the chain. * \return The new IO object or NULL on error. */ jose_io_t * jose_jwe_enc_io(jose_cfg_t *cfg, json_t *jwe, json_t *rcp, const json_t *jwk, jose_io_t *next); /** * Wraps a CEK with a JWK. * * The purpose of this function is to wrap (encrypt) or, in some cases, produce * the CEK (\p cek) from the provided JWK(s) (\p jwk). This function can be * used in two modes: single-JWK and multi-JWK. * * In single-JWK mode, the \p jwk parameter contains a JWK object and the * \p rcp parameter must contain either a JWE recipient object or NULL, in * which case a default empty JWE recipient object is created internally. * * Multi-JWK mode works exactly the same as single-JWK mode except that it * performs multiple wrappings in a single call. This mode is enabled by * passing either an array of JWK objects or a JWKSet as the \p jwk parameter. * In this mode, the \p rcp parameter contains one of the following values: * * 1. A JWE recipient object that will be used for all wrappings. In this case, * a copy will be made for each wrapping and \p rcp will not be modified in * any way. * * 2. An array of JWE recipient objects. Each object will be used with its * corresponding JWK from \p jwk. If the arrays in \p sig and \p jwk are a * different size, an error will occur. * * 3. NULL. This has the same effect as passing NULL for each separate JWK. * * In either mode, if the resulting JWE (\p jwe) would contain only a single * recipient, the JWE will be represented in Flattened JWE JSON Serialization * Syntax. Otherwise, it will be represented in General JWE JSON Serialization * Syntax. * * If the "alg" parameter is not specified in the JOSE Header, it will be * inferred from the JWK and the chosen algorithm will be added to the JWE * Per-Recipient Unprotected Header. Likewise, any missing, required, * algorithm-specific parameters will be either inferred or sensible, secure * defaults will be chosen and the results will be added to the JWE * Per-Recipient Unprotected Header. * * If the provided CEK (\p cek) does not contain key material, it will be * implicitly generated during the first call to jose_jwe_enc_jwk(). This * important feature enables the use of the "dir" and "ECDH-ES" algorithms. * In the case of the "dir" algorithm, the JWK is the CEK and thus the key * material is copied from \p jwk to \p cek. A similar situation arises with * the algorithm "ECDH-ES" where the result of a key exchange is used as the * CEK; thus, the CEK is produced during the wrapping process. This feature * implies that if multiple wrappings are created, only one of them may have * the algorithm "ECDH-ES" and it must be the first wrapping. Attempting to * use "ECDH-ES" with an existing CEK will result in an error. * * It is additionally possible to pass a password JSON string as key input * to the PBES2 family of algorithms anywhere a JWK can be used. Likewise, if * the "alg" JOSE Header parameter is not specified and a JSON string is used * in place of a JWK, the PBES2 family of algorithms will be inferred. * * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param rcp The JWE recipient object(s) or NULL. * \param jwk The JWK(s) or JWKSet used for wrapping. * \param cek The CEK to wrap (if empty, generated). * \return On success, true. Otherwise, false. */ bool jose_jwe_enc_jwk(jose_cfg_t *cfg, json_t *jwe, json_t *rcp, const json_t *jwk, json_t *cek); /** * Encrypts plaintext with the CEK. * * This function is a thin wrapper around the jose_jwe_enc_cek_io() function * allowing the user to specify the plaintext in a single call. The ciphertext * output will be appended to the JWE as the "ciphertext" property. * * \see jose_jwe_enc_cek_io() * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param cek The CEK object. * \param pt The plaintext. * \param ptl The length of the plaintext. * \return On success, true. Otherwise, false. */ bool jose_jwe_enc_cek(jose_cfg_t *cfg, json_t *jwe, const json_t *cek, const void *pt, size_t ptl); /** * Encrypts plaintext with the CEK using streaming. * * The plaintext is provided through the returned IO object. The plaintext * will be encrypted and written to the \p next IO object. This IO object * works on binary data, so you may need to use a URL-safe Base64 decoder on * input and a URL-safe Base64 encoder on output, depending on your situation. * * Compressed plaintext can be implicitly enabled by specifying the "zip" * parameter the JWE Protected Header. * * If the JWE Protected Header is a JSON object rather than an encoded string, * this function will encode the JWE Protected Header to its URL-safe Base64 * encoding as defined in RFC 7516. However, this function will never modify * a JWE Protected Header that is already encoded. * * If the "enc" parameter is not specified in the JWE Protected Header or the * JWE Shared Unprotected Header, it will be inferred from the CEK and stored * in either the JWE Protected Header or the JWE Shared Unprotected Header * (preferring the JWE Protected header if it can be modified). * * Please note that the "tag" property will only be added to the JWE when * \ref jose_io_t.done() returns. * * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param cek The CEK object. * \param next The next IO object in the chain. * \return The new IO object or NULL on error. */ jose_io_t * jose_jwe_enc_cek_io(jose_cfg_t *cfg, json_t *jwe, const json_t *cek, jose_io_t *next); /** * Unwraps and decrypts ciphertext. * * This function is a thin wrapper around the jose_jwe_dec_io() function * allowing the user to obtain the plaintext in a single call. The ciphertext * input will be obtained from the JWE "ciphertext" property. * * \see jose_jwe_dec_io() * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param rcp The JWE recipient object(s) or NULL. * \param jwk The JWK(s) or JWKSet used for wrapping. * \param ptl The length of the plaintext (output). * \return The newly-allocated plaintext. */ void * jose_jwe_dec(jose_cfg_t *cfg, const json_t *jwe, const json_t *rcp, const json_t *jwk, size_t *ptl); /** * Unwraps and decrypts ciphertext using streaming. * * This function is a thin wrapper around the jose_jwe_dec_jwk() and * jose_jwe_dec_cek_io() functions, removing the complexity of managing the CEK. * * \see jose_jwe_dec_jwk() * \see jose_jwe_dec_cek_io() * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param rcp The JWE recipient object(s) or NULL. * \param jwk The JWK(s) or JWKSet used for unwrapping. * \param next The next IO object in the chain. * \return The new IO object or NULL on error. */ jose_io_t * jose_jwe_dec_io(jose_cfg_t *cfg, const json_t *jwe, const json_t *rcp, const json_t *jwk, jose_io_t *next); /** * Unwraps a CEK with a JWK. * * The purpose of this function is to unwrap (decrypt) or, in some cases, * produce the CEK (\p cek) from the provided JWK(s) (\p jwk). This function * can be used in two modes: single-JWK and multi-JWK. * * In single-JWK mode, the \p jwk parameter contains a JWK object and the * \p rcp parameter must contain either a JWE recipient object you wish to * unwrap or NULL, in which case all JWE recipients will be tried. * * Multi-JWK mode works exactly the same as single-JWK mode except that it * attempts to unwrap with multiple JWKs in a single call. This mode is enabled * by passing either an array of JWK objects or a JWKSet as the \p jwk * parameter. In this mode, the \p rcp parameter contains one of the following * values: * * 1. A JWE recipient object that will be used for all attempted unwrappings. * * 2. An array of JWE recipient objects. Each object will be used with its * corresponding JWK from \p jwk. If the arrays in \p sig and \p jwk are a * different size, an error will occur. * * 3. NULL. This has the same effect as passing NULL for each separate JWK. * * In either mode, a CEK is returned for the first JWK that successfully * unwraps a CEK. Two exceptions to this rule are if the "dir" or "ECDH-ES" * algorithms are used. In this case, a CEK may be returned which will fail * during decryption since there is no way to completely validate the JWK with * these algorithms. Thus, we suggest placing the keys for these algorithms * last when unwrapping with multiple JWKs. * * If the "alg" parameter is not specified in the JOSE Header, it will be * inferred from the JWK. This includes using a JSON string in place of a JWK * for the PBES2 family of algorithms. * * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param rcp The JWE recipient object(s) or NULL. * \param jwk The JWK(s) or JWKSet used for wrapping. * \return On success, a newly-allocated CEK object. Otherwise, NULL. */ json_t * jose_jwe_dec_jwk(jose_cfg_t *cfg, const json_t *jwe, const json_t *rcp, const json_t *jwk); /** * Decrypts ciphertext with the CEK. * * This function is a thin wrapper around the jose_jwe_dec_cek_io() function * allowing the user to obtain the plaintext in a single call. The ciphertext * input will be obtained from the JWE "ciphertext" property. * * \see jose_jwe_dec_cek_io() * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param cek The CEK object. * \param ptl The length of the plaintext (output). * \return The newly-allocated plaintext. */ void * jose_jwe_dec_cek(jose_cfg_t *cfg, const json_t *jwe, const json_t *cek, size_t *ptl); /** * Decrypts ciphertext with the CEK using streaming. * * The ciphertext is provided through the returned IO object. The ciphertext * will be decrypted and written to the \p next IO object. This IO object * works on binary data, so you may need to use a URL-safe Base64 decoder on * input and a URL-safe Base64 encoder on output, depending on your situation. * * Please note that validation of the ciphertext integrity protection is delayed * until \ref jose_io_t.done() returns. This means it is incredibly important * to check this return value and it is also important to be careful with the * plaintext emitted until this check is performed. * * Compressed plaintext will be internally decompressed if the "zip" property * is appropriately defined. * * If the "enc" parameter is not specified in the JWE Protected Header or the * JWE Shared Unprotected Header, it will be inferred from the CEK. * * Please note that the "tag" property on the JWE will only be accessed when * \ref jose_io_t.done() is called. So you may define it at any time on the * JWE object before calling \ref jose_io_t.done(). * * \param cfg The configuration context (optional). * \param jwe The JWE object. * \param cek The CEK object. * \param next The next IO object in the chain. * \return The new IO object or NULL on error. */ jose_io_t * jose_jwe_dec_cek_io(jose_cfg_t *cfg, const json_t *jwe, const json_t *cek, jose_io_t *next); /** @} */