From ea89fe02196176b31514c508453131a3aa5fbbb8 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Sat, 1 Sep 2018 19:42:30 +0000 Subject: [PATCH] Add header and tests for JOSE support. I need sleep. git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1839838 13f79535-47bb-0310-9956-ffa450edef68 --- include/apr_jose.h | 1139 +++++++++++++++++++++++++++++++++++ test/testjose.c | 1423 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2562 insertions(+) create mode 100644 include/apr_jose.h create mode 100644 test/testjose.c diff --git a/include/apr_jose.h b/include/apr_jose.h new file mode 100644 index 00000000000..7389d74ae5c --- /dev/null +++ b/include/apr_jose.h @@ -0,0 +1,1139 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @file apr_jose.h + * @brief APR-UTIL JSON Object Signing and Encryption Library + */ +#ifndef APR_JOSE_H +#define APR_JOSE_H + +/** + * @defgroup APR_Util_JOSE JSON Object Signing and Encryption + * @ingroup APR_Util + * @{ + * + * The JOSE (JSON Object Signing and Encryption) library allows the encoding + * and decoding of JWS (JSON Web Signature), JWE (JSON Web Encryption), JWK + * (JSON Web Key) and JWT (JSON Web Token) objects, encoded using compact + * encoding, JSON encoding, or flattened JSON encoding. + * + * The following RFCs are supported: + * + * - https://tools.ietf.org/html/rfc7515 - JSON Web Signature (JWS) + * - https://tools.ietf.org/html/rfc7516 - JSON Web Encryption (JWE) + * - https://tools.ietf.org/html/rfc7517 - JSON Web Key (JWK) + * - https://tools.ietf.org/html/rfc7519 - JSON Web Token (JWT) + * + * Encryption, decryption, signing and verification are implemented as + * callbacks to the caller's specification, and are not included. + * + * When decrypting or verifying, the caller MUST verify that the 'alg' + * algorithm parameter in the JOSE message matches the algorithm expected + * by the implementation. + * + * It is recommended that the apr_crypto library be used to implement the + * callbacks, however an alternatively crypto library of the caller's choice + * may be used instead. + */ +#include "apr.h" +#include "apr_pools.h" +#include "apu_errno.h" +#include "apr_strings.h" +#include "apr_buckets.h" +#include "apr_json.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @package Apache JOSE library + * + */ + +/** + * HMAC using SHA-256 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_HS256 "HS256" + +/** + * HMAC using SHA-384 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_HS384 "HS384" + +/** + * HMAC using SHA-512 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_HS512 "HS512" + +/** + * RSASSA-PKCS1-v1_5 using SHA-256 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_RS256 "RS256" + +/** + * RSASSA-PKCS1-v1_5 using SHA-384 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_RS384 "RS384" + +/** + * RSASSA-PKCS1-v1_5 using SHA-512 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_RS512 "RS512" + +/** + * ECDSA using P-256 and SHA-256 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_ES256 "ES256" + +/** + * ECDSA using P-384 and SHA-384 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_ES384 "ES384" + +/** + * ECDSA using P-512 and SHA-512 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_ES512 "ES512" + +/** + * RSASSA-PSS using SHA-256 and MGF1 with SHA-256 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_PS256 "PS256" + +/** + * RSASSA-PSS using SHA-384 and MGF1 with SHA-384 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_PS384 "PS384" + +/** + * RSASSA-PSS using SHA-512 and MGF1 with SHA-512 + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_PS512 "PS512" + +/** + * No digital signature or MAC performed + * + * https://tools.ietf.org/html/rfc7518#section-3.1 + */ +#define APR_JOSE_JWA_NONE "none" + +/** + * "kty" (Key Type) Parameter + * + * https://tools.ietf.org/html/rfc7517#section-4.1 + */ +#define APR_JOSE_JWK_KEY_TYPE "kty" + +/** + * "use" (Public Key Use) Parameter + * + * https://tools.ietf.org/html/rfc7517#section-4.2 + */ +#define APR_JOSE_JWK_PUBLIC_KEY_USE "use" + +/** + * "key_ops" (Key Operations) Parameter + * + * https://tools.ietf.org/html/rfc7517#section-4.3 + */ +#define APR_JOSE_JWK_KEY_OPERATIONS "key_ops" + +/** + * "keys" Parameter + * + * https://tools.ietf.org/html/rfc7517#section-5.1 + */ +#define APR_JOSE_JWK_KEYS "keys" + +/** + * "alg" (Algorithm) Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.1 + * https://tools.ietf.org/html/rfc7516#section-4.1.1 + * https://tools.ietf.org/html/rfc7517#section-4.4 + */ +#define APR_JOSE_JWKSE_ALGORITHM "alg" + +/** + * "enc" (Encryption Algorithm) Header Parameter + * + * https://tools.ietf.org/html/rfc7516#section-4.1.2 + */ +#define APR_JOSE_JWE_ENCRYPTION "enc" + +/** + * "zip" (Compression Algorithm) Header Parameter + * + * https://tools.ietf.org/html/rfc7516#section-4.1.3 + */ +#define APR_JOSE_JWE_COMPRESSION "zip" + +/** + * "jku" (JWK Set URL) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.2 + * https://tools.ietf.org/html/rfc7516#section-4.1.4 + */ +#define APR_JOSE_JWSE_JWK_SET_URL "jku" + +/** + * "jwk" (JSON Web Key) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.3 + * https://tools.ietf.org/html/rfc7516#section-4.1.5 + */ +#define APR_JOSE_JWSE_JWK "jwk" + +/** + * "kid" (Key ID) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.4 + * https://tools.ietf.org/html/rfc7516#section-4.1.6 + */ +#define APR_JOSE_JWKSE_KEYID "kid" + +/** + * "x5u" (X.509 URL) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.5 + * https://tools.ietf.org/html/rfc7516#section-4.1.7 + */ +#define APR_JOSE_JWKSE_X509_URL "x5u" + +/** + * "x5c" (X.509 Certificate Chain) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.6 + * https://tools.ietf.org/html/rfc7516#section-4.1.8 + */ +#define APR_JOSE_JWKSE_X509_CHAIN "x5c" + +/** + * "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.7 + * https://tools.ietf.org/html/rfc7516#section-4.1.9 + */ +#define APR_JOSE_JWKSE_X509_SHA1_THUMBPRINT "x5t" + +/** + *"x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Header + * Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.8 + * https://tools.ietf.org/html/rfc7516#section-4.1.10 + */ +#define APR_JOSE_JWKSE_X509_SHA256_THUMBPRINT "x5t#S256" + +/** + * "typ" (Type) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.9 + * https://tools.ietf.org/html/rfc7516#section-4.1.11 + */ +#define APR_JOSE_JWSE_TYPE "typ" + +/** + * "cty" (Content Type) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.10 + * https://tools.ietf.org/html/rfc7516#section-4.1.12 + */ +#define APR_JOSE_JWSE_CONTENT_TYPE "cty" + +/** + * "crit" (Critical) Header Parameter + * + * https://tools.ietf.org/html/rfc7515#section-4.1.11 + * https://tools.ietf.org/html/rfc7516#section-4.1.13 + */ +#define APR_JOSE_JWSE_CRITICAL "crit" + +/** + * "payload" Parameter + * + * https://tools.ietf.org/html/rfc7515#section-7.2.1 + */ +#define APR_JOSE_JWS_PAYLOAD "payload" + +/** + * "signatures" Parameter + * + * https://tools.ietf.org/html/rfc7515#section-7.2.1 + */ +#define APR_JOSE_JWS_SIGNATURES "signatures" + +/** + * "protected" Parameter + * + * https://tools.ietf.org/html/rfc7515#section-7.2.1 + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWSE_PROTECTED "protected" + +/** + * "header" Parameter + * + * https://tools.ietf.org/html/rfc7515#section-7.2.1 + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWSE_HEADER "header" + +/** + * "signature" Parameter + * + * https://tools.ietf.org/html/rfc7515#section-7.2.1 + */ +#define APR_JOSE_JWS_SIGNATURE "signature" + +/** + * "unprotected" Parameter + * + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWE_UNPROTECTED "unprotected" + +/** + * "ciphertext" Parameter + * + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWE_CIPHERTEXT "ciphertext" + +/** + * "recipients" Parameter + * + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWE_RECIPIENTS "recipients" + +/** + * "encrypted_key" Parameter + * + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWE_EKEY "encrypted_key" + +/** + * "iv" Parameter + * + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWE_IV "iv" + +/** + * "tag" Parameter + * + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWE_TAG "tag" + +/** + * "aad" Parameter + * + * https://tools.ietf.org/html/rfc7516#section-7.2.1 + */ +#define APR_JOSE_JWE_AAD "aad" + +/** + * "iss" (Issuer) Claim + * + * https://tools.ietf.org/html/rfc7519#section-4.1.1 + */ +#define APR_JOSE_JWT_ISSUER "iss" + +/** + * "sub" (Subject) Claim + * + * https://tools.ietf.org/html/rfc7519#section-4.1.2 + */ +#define APR_JOSE_JWT_SUBJECT "sub" + +/** + * "aud" (Audience) Claim + * + * https://tools.ietf.org/html/rfc7519#section-4.1.3 + */ +#define APR_JOSE_JWT_AUDIENCE "aud" + +/** + * "exp" (Expiration Time) Claim + * + * https://tools.ietf.org/html/rfc7519#section-4.1.4 + */ +#define APR_JOSE_JWT_EXPIRATION_TIME "exp" + +/** + * "nbf" (Not Before) Claim + * + * https://tools.ietf.org/html/rfc7519#section-4.1.5 + */ +#define APR_JOSE_JWT_NOT_BEFORE "nbf" + +/** + * "iat" (Issued At) Claim + * + * https://tools.ietf.org/html/rfc7519#section-4.1.6 + */ +#define APR_JOSE_JWT_ISSUED_AT "iat" + +/** + * "jti" (JWT ID) Claim + * + * https://tools.ietf.org/html/rfc7519#section-4.1.7 + */ +#define APR_JOSE_JWT_ID "jti" + +/** + * "typ" (Type) Header Parameter representing a JWT + * + * https://tools.ietf.org/html/rfc7519#section-5.1 + */ +#define APR_JOSE_JWSE_TYPE_JWT "JWT" + +/** + * Default options. + */ +#define APR_JOSE_FLAG_NONE 0 + +/** + * Return the full JOSE structure, instead of innermost nested structure. + */ +#define APR_JOSE_FLAG_DECODE_ALL 1 + +/** + * When verifying or decrypting, break out of processing. + * + * If the verification or decryption failed, processing will be aborted + * with the given error. + * + * If the verification or decryption succeeded, processing will be considered + * successful and will move on to the nested structure. + */ +#define APR_JOSE_FLAG_BREAK 2 + +/** + * Forward declaration of the apr_jose_t structure. + */ +typedef struct apr_jose_t apr_jose_t; + +/** + * Enum that represents the type of JOSE object. + */ +typedef enum apr_jose_type_e { + /** No specific type. */ + APR_JOSE_TYPE_NONE = 0, + /** JSON Web Key (JWK) */ + APR_JOSE_TYPE_JWK = 1, + /** JSON Web Key Set (JWKS) */ + APR_JOSE_TYPE_JWKS, + /** JSON Web Signature (JWS) - compact encoding */ + APR_JOSE_TYPE_JWS, + /** JSON Web Signature (JWS) - JSON encoding */ + APR_JOSE_TYPE_JWS_JSON, + /** JSON Web Encryption (JWE) - compact encoding */ + APR_JOSE_TYPE_JWE, + /** JSON Web Encryption (JWE) - JSON encoding */ + APR_JOSE_TYPE_JWE_JSON, + /** JSON Web Token (JWT) */ + APR_JOSE_TYPE_JWT, + /** Generic binary data */ + APR_JOSE_TYPE_DATA, + /** Generic text data */ + APR_JOSE_TYPE_TEXT, + /** Generic JSON structure */ + APR_JOSE_TYPE_JSON +} apr_jose_type_e; + +/** + * Unsigned char data of a given length + */ +typedef struct apr_jose_data_t { + /** Pointer to the data */ + const unsigned char *data; + /** Length of the data */ + apr_size_t len; +} apr_jose_data_t; + +/** + * Signed char data of a given length + */ +typedef struct apr_jose_text_t { + /** Pointer to the text */ + const char *text; + /** Length of the text */ + apr_size_t len; +} apr_jose_text_t; + +/** + * JSON object + */ +typedef struct apr_jose_json_t { + /** Parsed JSON structure. */ + apr_json_value_t *json; +} apr_jose_json_t; + +/** + * A JSON web key + */ +typedef struct apr_jose_jwk_t { + /** Parsed JWK JSON structure */ + apr_json_value_t *key; +} apr_jose_jwk_t; + +/** + * A JSON web key set + */ +typedef struct apr_jose_jwks_t { + /** Parsed JWK set JSON structure containing a JSON array */ + apr_json_value_t *keys; +} apr_jose_jwks_t; + +/** + * A single signature within a a JSON web signature. + */ +typedef struct apr_jose_signature_t { + /** JWS Header */ + apr_json_value_t *header; + /** JWS Protected Header */ + apr_json_value_t *protected_header; + /** JWS Signature */ + apr_jose_data_t sig; + /** Result of verification for this signature */ + apr_status_t status; +} apr_jose_signature_t; + +/** + * A JSON web signature + */ +typedef struct apr_jose_jws_t { + /** JWS Compact / Flattened Signature */ + apr_jose_signature_t *signature; + /** JWS General Signatures */ + apr_array_header_t *signatures; + /** JWS Payload */ + apr_jose_t *payload; +} apr_jose_jws_t; + +/** + * An encrypted payload within a a JSON web encryption. + */ +typedef struct apr_jose_encryption_t { + /** JWE Shared Header */ + apr_json_value_t *unprotected; + /** JWE Protected Header */ + apr_json_value_t *protected; + /** JWE Protected Header (basde64url) */ + apr_jose_text_t protected64; + /** JWE Initialization Vector */ + apr_jose_data_t iv; + /** JWE AAD */ + apr_jose_data_t aad; + /** JWE AAD (base64url)*/ + apr_jose_text_t aad64; + /** JWE Ciphertext */ + apr_jose_data_t cipher; + /** JWE Authentication Tag */ + apr_jose_data_t tag; +} apr_jose_encryption_t; + +/** + * A single recipient within a a JSON web encryption. + */ +typedef struct apr_jose_recipient_t { + /** JWE Header */ + apr_json_value_t *header; + /** JWE Encrypted Key */ + apr_jose_data_t ekey; + /** Result of decryption for this recipient */ + apr_status_t status; +} apr_jose_recipient_t; + +/** + * A JSON web encryption + */ +typedef struct apr_jose_jwe_t { + /** JWE Compact / Flattened Recipient */ + apr_jose_recipient_t *recipient; + /** JWE General Recipients */ + apr_array_header_t *recipients; + /** JWE Encryption Parameters */ + apr_jose_encryption_t *encryption; + /** JWE Payload */ + apr_jose_t *payload; +} apr_jose_jwe_t; + +/** + * A JSON web token + */ +typedef struct apr_jose_jwt_t { + /** Claims associated with the JWT. */ + apr_json_value_t *claims; +} apr_jose_jwt_t; + +/** + * One JOSE structure to rule them all. + */ +struct apr_jose_t { + /** pool used for allocation */ + apr_pool_t *pool; + /** content type of this structure */ + const char *typ; + /** content type of the payload */ + const char *cty; + /** result of the operation */ + apu_err_t result; + /** type of the value */ + apr_jose_type_e type; + /** actual value, depending on the type */ + union { + apr_jose_jwk_t *jwk; + apr_jose_jwks_t *jwks; + apr_jose_jws_t *jws; + apr_jose_jwe_t *jwe; + apr_jose_jwt_t *jwt; + apr_jose_data_t *data; + apr_jose_text_t *text; + apr_jose_json_t *json; + } jose; +}; + +/** + * Callbacks for encryption, decryption, signing and verifying. + */ +typedef struct apr_jose_cb_t { + /** + * Callback that encrypts the content of the bucket brigade bb based + * on the parameters provided by the jwe->protected_header, and writes + * the resulting encrypted key to recipient->ekey, the initialisation vector + * to encryption->iv, the additional authentication data to encryption->aad, the + * cipher text to encryption->cipher, and the tag to encryption->tag. + * + * The encrypt function is expected to perform some or all of the + * following steps: + * + * 1. Determine the Key Management Mode employed by the algorithm used + * to determine the Content Encryption Key value. (This is the + * algorithm recorded in the "alg" (algorithm) Header Parameter of + * the resulting JWE.) + * + * 2. When Key Wrapping, Key Encryption, or Key Agreement with Key + * Wrapping are employed, generate a random CEK value. See RFC + * 4086 [RFC4086] for considerations on generating random values. + * The CEK MUST have a length equal to that required for the + * content encryption algorithm. + * + * 3. When Direct Key Agreement or Key Agreement with Key Wrapping are + * employed, use the key agreement algorithm to compute the value + * of the agreed upon key. When Direct Key Agreement is employed, + * let the CEK be the agreed upon key. When Key Agreement with Key + * Wrapping is employed, the agreed upon key will be used to wrap + * the CEK. + * + * 4. When Key Wrapping, Key Encryption, or Key Agreement with Key + * Wrapping are employed, encrypt the CEK to the recipient and let + * the result be the JWE Encrypted Key. + * + * 5. When Direct Key Agreement or Direct Encryption are employed, let + * the JWE Encrypted Key be the empty octet sequence. + * + * 6. When Direct Encryption is employed, let the CEK be the shared + * symmetric key. + * + * 8. If the JWE JSON Serialization is being used, repeat this process + * (steps 1-7) for each recipient. + * + * 9. Generate a random JWE Initialization Vector of the correct size + * for the content encryption algorithm (if required for the + * algorithm); otherwise, let the JWE Initialization Vector be the + * empty octet sequence. + * + * 11. If a "zip" parameter was included, compress the plaintext using + * the specified compression algorithm and let M be the octet + * sequence representing the compressed plaintext; otherwise, let M + * be the octet sequence representing the plaintext. + * + * 12. Create the JSON object(s) containing the desired set of Header + * Parameters, which together comprise the JOSE Header: one or more + * of the JWE Protected Header, the JWE Shared Unprotected Header, + * and the JWE Per-Recipient Unprotected Header. + * + * 13. Compute the Encoded Protected Header value BASE64URL(UTF8(JWE + * Protected Header)). If the JWE Protected Header is not present + * (which can only happen when using the JWE JSON Serialization and + * no "protected" member is present), let this value be the empty + * string. + * + * 14. Let the Additional Authenticated Data encryption parameter be + * ASCII(Encoded Protected Header). However, if a JWE AAD value is + * present (which can only be the case when using the JWE JSON + * Serialization), instead let the Additional Authenticated Data + * encryption parameter be ASCII(Encoded Protected Header || '.' || + * BASE64URL(JWE AAD)). + * + * 15. Encrypt M using the CEK, the JWE Initialization Vector, and the + * Additional Authenticated Data value using the specified content + * encryption algorithm to create the JWE Ciphertext value and the + * JWE Authentication Tag (which is the Authentication Tag output + * from the encryption operation). + * + * @param bb Brigade containing data to be encrypted. + * @param jose The JOSE structure. + * @param recipient Structure containing details of the recipient of + * this message. + * @param encryption Structure to be filled out by the callback + * containing the encrypted message. + * @param ctx A context. + * @param pool The pool to use. + * @return APR_SUCCESS if encrypted successfully, APR_ENOTIMPL if + * encryption is not supported, or any other suitable error. The + * jose->result structure may be filled out with further details of + * any error. + */ + apr_status_t (*encrypt)(apr_bucket_brigade *bb, apr_jose_t *jose, + apr_jose_recipient_t *recipient, apr_jose_encryption_t *encryption, + void *ctx, apr_pool_t *pool); + /** + * Callback that decrypts the ciphertext based + * on the parameters provided by the recipient and encryption parameters, and writes + * the resulting decrypted value to the bucket brigade. Base64url versions of the + * protected header and the aad are provided as part of the JWE decryption + * mechanism. + * + * For security reasons, this callback MUST verify that the algorithm + * present in the JWE matches the algorithm expected by the decoder. + * + * The decrypt function is expected to perform some or all of the + * following steps: + * + * 6. Determine the Key Management Mode employed by the algorithm + * specified by the "alg" (algorithm) Header Parameter. + * + * 7. Verify that the JWE uses a key known to the recipient. + * + * 8. When Direct Key Agreement or Key Agreement with Key Wrapping are + * employed, use the key agreement algorithm to compute the value + * of the agreed upon key. When Direct Key Agreement is employed, + * let the CEK be the agreed upon key. When Key Agreement with Key + * Wrapping is employed, the agreed upon key will be used to + * decrypt the JWE Encrypted Key. + * + * 9. When Key Wrapping, Key Encryption, or Key Agreement with Key + * Wrapping are employed, decrypt the JWE Encrypted Key to produce + * the CEK. The CEK MUST have a length equal to that required for + * the content encryption algorithm. Note that when there are + * multiple recipients, each recipient will only be able to decrypt + * JWE Encrypted Key values that were encrypted to a key in that + * recipient's possession. It is therefore normal to only be able + * to decrypt one of the per-recipient JWE Encrypted Key values to + * obtain the CEK value. Also, see Section 11.5 for security + * considerations on mitigating timing attacks. + * + * 10. When Direct Key Agreement or Direct Encryption are employed, + * verify that the JWE Encrypted Key value is an empty octet + * sequence. + * + * 11. When Direct Encryption is employed, let the CEK be the shared + * symmetric key. + * + * 12. Record whether the CEK could be successfully determined for this + * recipient or not. + * + * 13. If the JWE JSON Serialization is being used, repeat this process + * (steps 4-12) for each recipient contained in the representation. + * + * 14. Compute the Encoded Protected Header value BASE64URL(UTF8(JWE + * Protected Header)). If the JWE Protected Header is not present + * (which can only happen when using the JWE JSON Serialization and + * no "protected" member is present), let this value be the empty + * string. + * + * 15. Let the Additional Authenticated Data encryption parameter be + * ASCII(Encoded Protected Header). However, if a JWE AAD value is + * present (which can only be the case when using the JWE JSON + * Serialization), instead let the Additional Authenticated Data + * encryption parameter be ASCII(Encoded Protected Header || '.' || + * BASE64URL(JWE AAD)). + * + * 16. Decrypt the JWE Ciphertext using the CEK, the JWE Initialization + * Vector, the Additional Authenticated Data value, and the JWE + * Authentication Tag (which is the Authentication Tag input to the + * calculation) using the specified content encryption algorithm, + * returning the decrypted plaintext and validating the JWE + * Authentication Tag in the manner specified for the algorithm, + * rejecting the input without emitting any decrypted output if the + * JWE Authentication Tag is incorrect. + * + * 17. If a "zip" parameter was included, uncompress the decrypted + * plaintext using the specified compression algorithm. + * + * @param bb Brigade where decrypted data is to be written. + * @param jose The JOSE structure. + * @param recipient Structure containing details of the recipient of + * this message, to be used to decrypt the message. + * @param encryption Structure containing the encrypted message. + * @param header The JOSE protected header. + * @param p64 The JOSE protected header in original BASE64URL format, + * for use during decryption. + * @param aad64 The JOSE additional authenticated data in original + * BASE64URL format, for use during decryption. + * @param ctx A context. + * @param dflags A pointer to a flag. Set to APR_JOSE_FLAG_NONE for + * decryption to continue to the next recipient in the JWE, or + * APR_JOSE_FLAG_BREAK to stop decrypting further recipients. + * @param pool The pool to use. + * @return APR_SUCCESS if decrypted successfully, APR_ENOTIMPL if + * decryption is not supported, or any other suitable error. The + * jose->result structure may be filled out with further details of + * any error. + */ + apr_status_t (*decrypt)(apr_bucket_brigade *bb, apr_jose_t *jose, + apr_jose_recipient_t *recipient, apr_jose_encryption_t *encryption, + apr_json_value_t *header, apr_jose_text_t *ph64, + apr_jose_text_t *aad64, void *ctx, int *dflags, apr_pool_t *pool); + /** + * Callback that signs the content of the bucket brigade bb based + * on the parameters provided by the signature protected header, and writes + * the resulting binary signature to signature->sig. + * + * The sign function is expected to perform some or all of the + * following steps: + * + * 5. Compute the JWS Signature in the manner defined for the + * particular algorithm being used over the JWS Signing Input + * ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || + * BASE64URL(JWS Payload)). The "alg" (algorithm) Header Parameter + * MUST be present in the JOSE Header, with the algorithm value + * accurately representing the algorithm used to construct the JWS + * Signature. + * + * @param bb Brigade containing data to be signed. + * @param jose The JOSE structure. + * @param signature Structure to be filled out by the callback + * containing the signature of the message. + * @param ctx A context. + * @param pool The pool to use. + * @return APR_SUCCESS if signed successfully, APR_ENOTIMPL if + * signing is not supported, or any other suitable error. The + * jose->result structure may be filled out with further details of + * any error. + */ + apr_status_t (*sign)(apr_bucket_brigade *bb, apr_jose_t *jose, + apr_jose_signature_t *signature, void *ctx, apr_pool_t *pool); + /** + * Callback that verifies the content of the bucket brigade bb based + * on the parameters provided by the signature protected header and + * signature->sig. + * + * For security reasons, this callback MUST verify that the algorithm + * present in the JWS matches the algorithm expected by the decoder. + * + * The verify function is expected to perform some or all of the + * following steps: + * + * 8. Validate the JWS Signature against the JWS Signing Input + * ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || + * BASE64URL(JWS Payload)) in the manner defined for the algorithm + * being used, which MUST be accurately represented by the value of + * the "alg" (algorithm) Header Parameter, which MUST be present. + * See Section 10.6 for security considerations on algorithm + * validation. Record whether the validation succeeded or not. + * + * 9. If the JWS JSON Serialization is being used, repeat this process + * (steps 4-8) for each digital signature or MAC value contained in + * the representation. + * + * 10. If none of the validations in step 9 succeeded, then the JWS MUST + * be considered invalid. Otherwise, in the JWS JSON Serialization + * case, return a result to the application indicating which of the + * validations succeeded and failed. In the JWS Compact + * Serialization case, the result can simply indicate whether or not + * the JWS was successfully validated. + * + * @param bb Brigade containing data to be verified. + * @param jose The JOSE structure. + * @param signature Structure containing the signature to be verified. + * @param ctx A context. + * @param dflags A pointer to a flag. Set to APR_JOSE_FLAG_NONE for + * verification to continue to the next recipient in the JWE, or + * APR_JOSE_FLAG_BREAK to stop verifying further recipients. + * @param pool The pool to use. + * @return APR_SUCCESS if verified successfully, APR_ENOTIMPL if + * verification is not supported, or any other suitable error. The + * jose->result structure may be filled out with further details of + * any error. + */ + apr_status_t (*verify)(apr_bucket_brigade *bb, apr_jose_t *jose, + apr_jose_signature_t *signature, void *ctx, int *vflags, + apr_pool_t *pool); + /** Context to be passed to the callback. */ + void *ctx; +} apr_jose_cb_t; + +/** + * @brief Get the result of the last operation on the jose. If the result + * is NULL, the operation was successful. + * @param jose - context pointer + * @return The apu_err_t is returned. + */ +APR_DECLARE(apu_err_t *) apr_jose_error(apr_jose_t *jose); + +/** + * Make a generic JOSE structure. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param type the type of structure to create. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_make(apr_jose_t **jose, apr_jose_type_e type, + apr_pool_t *pool); + +/** + * Make a JSON Web Key for encoding or decoding. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param key the json representing the key. May be NULL. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_jwk_make(apr_jose_t **jose, + apr_json_value_t *key, apr_pool_t *pool); + +/** + * Make a JSON Web Key Set. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param keys the array of keys in JSON format. May be NULL. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_jwks_make(apr_jose_t **jose, + apr_json_value_t *keys, apr_pool_t *pool); + +/** + * Make a signature structure for JWS. + * + * @param signature the result. + * @param header the unprotected header. + * @param protected the protected header. + * @param pool the pool to use. + */ +APR_DECLARE(apr_status_t) apr_jose_signature_make( + apr_jose_signature_t **signature, apr_json_value_t *header, + apr_json_value_t *protected, apr_pool_t *pool); + +/** + * Make a recipient structure for JWE. + * + * @param recipient the result. + * @param unprotected the unprotected header. + * @param pool the pool to use. + */ +APR_DECLARE(apr_status_t) apr_jose_recipient_make(apr_jose_recipient_t **recipient, + apr_json_value_t *unprotected, apr_pool_t *pool); + +/** + * Make an encryption structure for JWE. + * + * @param encryption the result. + * @param unprotected the unprotected shared header. + * @param protected the protected header. + * @param pool the pool to use. + */ +APR_DECLARE(apr_status_t) apr_jose_encryption_make(apr_jose_encryption_t **encryption, + apr_json_value_t *unprotected, apr_json_value_t *protected, + apr_pool_t *pool); + +/** + * Make a compact encoded JWE. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param recipient the recipient for compact / flattened JWE. + * @param recipients the recipients array for general JWE. + * @param encryption the encryption structure. + * @param payload the JOSE payload to encrypt. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_jwe_make(apr_jose_t **jose, + apr_jose_recipient_t *recipient, apr_array_header_t *recipients, + apr_jose_encryption_t *encryption, apr_jose_t *payload, + apr_pool_t *pool); + +/** + * Make a JSON encoded JWE. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param recipient the recipient for compact / flattened JWE. + * @param recipients the recipients array for general JWE. + * @param encryption the encryption structure. + * @param payload the JOSE payload to encrypt. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_jwe_json_make(apr_jose_t **jose, + apr_jose_recipient_t *recipient, + apr_array_header_t *recipients, apr_jose_encryption_t *encryption, + apr_jose_t *payload, apr_pool_t *pool); + +/** + * Make a compact encoded JWS. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param signature the header / protected header / signature used with compact or flattened syntax. May be NULL. + * @param signatures array of header / protected header / signature used with general JSON syntax. + * @param payload the payload to be wrapped by this JWS. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_jws_make(apr_jose_t **jose, + apr_jose_signature_t *signature, apr_array_header_t *signatures, + apr_jose_t *payload, apr_pool_t *pool); + +/** + * Make a JSON encoded JWS. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param signature the header / protected header / signature used with compact or flattened syntax. May be NULL. + * @param signatures array of header / protected header / signature used with general JSON syntax. + * @param payload the payload to be wrapped by this JWS. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_jws_json_make(apr_jose_t **jose, + apr_jose_signature_t *signature, apr_array_header_t *signatures, + apr_jose_t *payload, apr_pool_t *pool); + +/** + * Make a JWT claims payload. + * + * To create a useful JWT, this payload needs to be wrapped in a JWS + * or JWE (or both), as required by the caller. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param claims the claims to sign. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_jwt_make(apr_jose_t **jose, + apr_json_value_t *claims, apr_pool_t *pool); + +/** + * Make a data buffer for encoding from the given data and length. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param typ the content type of this data. + * @param in the plaintext to sign. + * @param inlen length of the plaintext. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_data_make(apr_jose_t **jose, const char *typ, + const unsigned char *in, apr_size_t inlen, apr_pool_t *pool); + +/** + * Make a UTF-8 text buffer for encoding from the given string + * and length. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param cty the content type. + * @param in the UTF-8 encoded text string. + * @param inlen length of the UTF-8 encoded text string. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_text_make(apr_jose_t **jose, const char *cty, + const char *in, apr_size_t inlen, apr_pool_t *pool); + +/** + * Make a json structure for encoding. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param cty the content type. + * @param json the json object to add. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_json_make(apr_jose_t **jose, const char *cty, + apr_json_value_t *json, apr_pool_t *pool); + +/** + * Sign or encrypt the apr_jose_t, and write it to the brigade. + * @param brigade brigade the result will be appended to. + * @param flush The flush function to use if the brigade is full + * @param ctx The structure to pass to the flush function + * @param jose the JOSE to encode. + * @param cb callbacks for sign and encrypt. + * @param pool pool to be used. + * @return APR_SUCCESS is returned if encoding was successful, otherwise + * an APR status code, along with an apu_err_t with an explanation + * allocated from jose->pool. + */ +APR_DECLARE(apr_status_t) apr_jose_encode(apr_bucket_brigade *brigade, + apr_brigade_flush flush, void *ctx, apr_jose_t *jose, + apr_jose_cb_t *cb, apr_pool_t *pool); + +/** + * Decode, decrypt and verify the utf8-encoded JOSE string into apr_jose_t. + * + * The JOSE structure may be nested to the given limit. + * @param jose If jose points at NULL, a JOSE structure will be + * created. If the jose pointer is not NULL, the structure will + * be reused. + * @param typ content type of this object. + * @param brigade the JOSE structure to decode. + * @param cb callbacks for verify and decrypt. + * @param level depth limit of JOSE and JSON nesting. + * @param flags APR_JOSE_FLAG_NONE to return payload only. APR_JOSE_FLAG_DECODE_ALL + * to return the full JWS/JWE structure. + * @param pool pool used to allocate the result from. + */ +APR_DECLARE(apr_status_t) apr_jose_decode(apr_jose_t **jose, const char *typ, + apr_bucket_brigade *brigade, apr_jose_cb_t *cb, int level, int flags, + apr_pool_t *pool); + + +#ifdef __cplusplus +} +#endif +/** @} */ +#endif /* APR_JOSE_H */ diff --git a/test/testjose.c b/test/testjose.c new file mode 100644 index 00000000000..411b7c5d010 --- /dev/null +++ b/test/testjose.c @@ -0,0 +1,1423 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +#include +#include +#include + +#include "apr_jose.h" + +#include "abts.h" +#include "testutil.h" + + +static apr_status_t sign_cb(apr_bucket_brigade *bb, apr_jose_t *jose, + apr_jose_signature_t *signature, void *ctx, apr_pool_t *pool) +{ + abts_case *tc = ctx; + apr_json_kv_t *alg = NULL; + + if (signature) { + apr_json_value_t *ph = signature->protected_header; + + ABTS_INT_EQUAL(tc, APR_JSON_OBJECT, ph->type); + + if (ph->type == APR_JSON_OBJECT) { + alg = apr_json_object_get(ph, APR_JOSE_JWKSE_ALGORITHM, + APR_JSON_VALUE_STRING); + } + } + + if (alg) { + + ABTS_INT_EQUAL(tc, APR_JSON_STRING, alg->v->type); + + /* unsecured jws/jwt */ + if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "none", + alg->v->value.string.len)) { + + signature->sig.data = (unsigned const char *) ""; + signature->sig.len = 0; + + return APR_SUCCESS; + } + + /* hs256 jws - https://tools.ietf.org/html/rfc7515#appendix-A.1 */ + else if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "HS256", + alg->v->value.string.len)) { + + const unsigned char hs256[] = { 116, 24, 223, 180, 151, 153, 224, + 37, 79, 250, 96, 125, 216, 173, 187, 186, 22, 212, 37, 77, + 105, 214, 191, 240, 91, 88, 5, 88, 83, 132, 141, 121 }; + signature->sig.data = apr_pmemdup(p, hs256, sizeof(hs256)); + signature->sig.len = sizeof(hs256); + + return APR_SUCCESS; + } + + /* rs256 jws - https://tools.ietf.org/html/rfc7515#appendix-A.2 */ + else if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "RS256", + alg->v->value.string.len)) { + + const unsigned char rs256[] = { 112, 46, 33, 137, 67, 232, 143, 209, + 30, 181, 216, 45, 191, 120, 69, 243, 65, 6, 174, 27, 129, + 255, 247, 115, 17, 22, 173, 209, 113, 125, 131, 101, 109, + 66, 10, 253, 60, 150, 238, 221, 115, 162, 102, 62, 81, 102, + 104, 123, 0, 11, 135, 34, 110, 1, 135, 237, 16, 115, 249, + 69, 229, 130, 173, 252, 239, 22, 216, 90, 121, 142, 232, + 198, 109, 219, 61, 184, 151, 91, 23, 208, 148, 2, 190, 237, + 213, 217, 217, 112, 7, 16, 141, 178, 129, 96, 213, 248, 4, + 12, 167, 68, 87, 98, 184, 31, 190, 127, 249, 217, 46, 10, + 231, 111, 36, 242, 91, 51, 187, 230, 244, 74, 230, 30, 177, + 4, 10, 203, 32, 4, 77, 62, 249, 18, 142, 212, 1, 48, 121, + 91, 212, 189, 59, 65, 238, 202, 208, 102, 171, 101, 25, 129, + 253, 228, 141, 247, 127, 55, 45, 195, 139, 159, 175, 221, + 59, 239, 177, 139, 93, 163, 204, 60, 46, 176, 47, 158, 58, + 65, 214, 18, 202, 173, 21, 145, 18, 115, 160, 95, 35, 185, + 232, 56, 250, 175, 132, 157, 105, 132, 41, 239, 90, 30, 136, + 121, 130, 54, 195, 212, 14, 96, 69, 34, 165, 68, 200, 242, + 122, 122, 45, 184, 6, 99, 209, 108, 247, 202, 234, 86, 222, + 64, 92, 178, 33, 90, 69, 178, 194, 85, 102, 181, 90, 193, + 167, 72, 160, 112, 223, 200, 163, 42, 70, 149, 67, 208, 25, + 238, 251, 71 }; + signature->sig.data = apr_pmemdup(p, rs256, sizeof(rs256)); + signature->sig.len = sizeof(rs256); + + return APR_SUCCESS; + } + + /* es256 jws - https://tools.ietf.org/html/rfc7515#appendix-A.3 */ + else if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "ES256", + alg->v->value.string.len)) { + + const unsigned char es256[] = { 14, 209, 33, 83, 121, 99, 108, 72, + 60, 47, 127, 21, 88, 7, 212, 2, 163, 178, 40, 3, 58, 249, + 124, 126, 23, 129, 154, 195, 22, 158, 166, 101, 197, 10, 7, + 211, 140, 60, 112, 229, 216, 241, 45, 175, 8, 74, 84, 128, + 166, 101, 144, 197, 242, 147, 80, 154, 143, 63, 127, 138, + 131, 163, 84, 213 }; + signature->sig.data = apr_pmemdup(p, es256, sizeof(es256)); + signature->sig.len = sizeof(es256); + + return APR_SUCCESS; + } + + else { + apr_errprintf(&jose->result, jose->pool, NULL, 0, + "Header 'alg' not recognised: %.*s", + (int) alg->v->value.string.len, alg->v->value.string.p); + return APR_ENOTIMPL; + } + + } + + else { + apr_errprintf(&jose->result, jose->pool, NULL, 0, + "Header 'alg' missing"); + return APR_ENOTIMPL; + } + + return APR_ENOTIMPL; +} + +static apr_status_t verify_cb(apr_bucket_brigade *bb, + apr_jose_t *jose, apr_jose_signature_t *signature, void *ctx, + int *vflags, apr_pool_t *pool) +{ + abts_case *tc = ctx; + apr_json_kv_t *alg = NULL; + + *vflags = APR_JOSE_FLAG_NONE; + + if (signature) { + apr_json_value_t *ph = signature->protected_header; + + ABTS_INT_EQUAL(tc, APR_JSON_OBJECT, ph->type); + + alg = apr_json_object_get(ph, APR_JOSE_JWKSE_ALGORITHM, + APR_JSON_VALUE_STRING); + } + + if (alg) { + + ABTS_INT_EQUAL(tc, APR_JSON_STRING, alg->v->type); + + /* unsecured jws/jwt */ + if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "none", + alg->v->value.string.len)) { + + if (!memcmp(signature->sig.data, (unsigned const char *) "", + signature->sig.len)) { + return APR_SUCCESS; + } + } + + /* hs256 jws - https://tools.ietf.org/html/rfc7515#appendix-A.1 */ + else if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "HS256", + alg->v->value.string.len)) { + + const unsigned char hs256[] = { 116, 24, 223, 180, 151, 153, 224, + 37, 79, 250, 96, 125, 216, 173, 187, 186, 22, 212, 37, 77, + 105, 214, 191, 240, 91, 88, 5, 88, 83, 132, 141, 121 }; + + if (!memcmp(signature->sig.data, hs256, sizeof(hs256))) { + return APR_SUCCESS; + } + + } + + /* rs256 jws - https://tools.ietf.org/html/rfc7515#appendix-A.2 */ + else if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "RS256", + alg->v->value.string.len)) { + + const unsigned char rs256[] = { 112, 46, 33, 137, 67, 232, 143, 209, + 30, 181, 216, 45, 191, 120, 69, 243, 65, 6, 174, 27, 129, + 255, 247, 115, 17, 22, 173, 209, 113, 125, 131, 101, 109, + 66, 10, 253, 60, 150, 238, 221, 115, 162, 102, 62, 81, 102, + 104, 123, 0, 11, 135, 34, 110, 1, 135, 237, 16, 115, 249, + 69, 229, 130, 173, 252, 239, 22, 216, 90, 121, 142, 232, + 198, 109, 219, 61, 184, 151, 91, 23, 208, 148, 2, 190, 237, + 213, 217, 217, 112, 7, 16, 141, 178, 129, 96, 213, 248, 4, + 12, 167, 68, 87, 98, 184, 31, 190, 127, 249, 217, 46, 10, + 231, 111, 36, 242, 91, 51, 187, 230, 244, 74, 230, 30, 177, + 4, 10, 203, 32, 4, 77, 62, 249, 18, 142, 212, 1, 48, 121, + 91, 212, 189, 59, 65, 238, 202, 208, 102, 171, 101, 25, 129, + 253, 228, 141, 247, 127, 55, 45, 195, 139, 159, 175, 221, + 59, 239, 177, 139, 93, 163, 204, 60, 46, 176, 47, 158, 58, + 65, 214, 18, 202, 173, 21, 145, 18, 115, 160, 95, 35, 185, + 232, 56, 250, 175, 132, 157, 105, 132, 41, 239, 90, 30, 136, + 121, 130, 54, 195, 212, 14, 96, 69, 34, 165, 68, 200, 242, + 122, 122, 45, 184, 6, 99, 209, 108, 247, 202, 234, 86, 222, + 64, 92, 178, 33, 90, 69, 178, 194, 85, 102, 181, 90, 193, + 167, 72, 160, 112, 223, 200, 163, 42, 70, 149, 67, 208, 25, + 238, 251, 71 }; + + if (!memcmp(signature->sig.data, rs256, sizeof(rs256))) { + return APR_SUCCESS; + } + + } + + /* es256 jws - https://tools.ietf.org/html/rfc7515#appendix-A.3 */ + else if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "ES256", + alg->v->value.string.len)) { + + const unsigned char es256[] = { 14, 209, 33, 83, 121, 99, 108, 72, + 60, 47, 127, 21, 88, 7, 212, 2, 163, 178, 40, 3, 58, 249, + 124, 126, 23, 129, 154, 195, 22, 158, 166, 101, 197, 10, 7, + 211, 140, 60, 112, 229, 216, 241, 45, 175, 8, 74, 84, 128, + 166, 101, 144, 197, 242, 147, 80, 154, 143, 63, 127, 138, + 131, 163, 84, 213 }; + + if (!memcmp(signature->sig.data, es256, sizeof(es256))) { + return APR_SUCCESS; + } + + return APR_SUCCESS; + } + + else { + apr_errprintf(&jose->result, jose->pool, NULL, 0, + "Header 'alg' not recognised: %.*s", + (int) alg->v->value.string.len, alg->v->value.string.p); + return APR_ENOTIMPL; + } + + } + + else { + apr_errprintf(&jose->result, jose->pool, NULL, 0, + "Header 'alg' missing"); + return APR_ENOTIMPL; + } + + return APR_ENOTIMPL; +} + +static apr_status_t encrypt_cb(apr_bucket_brigade *brigade, apr_jose_t *jose, + apr_jose_recipient_t *recipient, apr_jose_encryption_t *encryption, + void *ctx, apr_pool_t *p) +{ + abts_case *tc = ctx; + apr_json_value_t *protected_header; + apr_json_value_t *header; + apr_json_kv_t *alg = NULL; + + if (encryption) { + + if (encryption->protected) { + + protected_header = encryption->protected; + if (protected_header) { + + char buf[1024]; + apr_size_t len = sizeof(buf); + + apr_bucket_brigade *bb = apr_brigade_create(p, + brigade->bucket_alloc); + + apr_json_encode(bb, NULL, NULL, protected_header, + APR_JSON_FLAGS_WHITESPACE, p); + + apr_brigade_flatten(bb, buf, &len); + + /* RSAES-OAEP and AES GCM jwe - https://tools.ietf.org/html/rfc7516#appendix-A.1 */ + if (!strncmp("{\"alg\":\"RSA-OAEP\",\"enc\":\"A256GCM\"}", buf, len)) { + + const unsigned char iv[] = { 227, 197, 117, 252, 2, 219, 233, + 68, 180, 225, 77, 219 }; + + const unsigned char aad[] = { 101, 121, 74, 104, 98, 71, 99, + 105, 79, 105, 74, 83, 85, 48, 69, 116, 84, 48, 70, 70, + 85, 67, 73, 115, 73, 109, 86, 117, 89, 121, 73, 54, 73, + 107, 69, 121, 78, 84, 90, 72, 81, 48, 48, 105, 102, 81 }; + + const unsigned char cipher[] = { 229, 236, 166, 241, 53, 191, + 115, 196, 174, 43, 73, 109, 39, 122, 233, 96, 140, 206, + 120, 52, 51, 237, 48, 11, 190, 219, 186, 80, 111, 104, + 50, 142, 47, 167, 59, 61, 181, 127, 196, 21, 40, 82, + 242, 32, 123, 143, 168, 226, 73, 216, 176, 144, 138, + 247, 106, 60, 16, 205, 160, 109, 64, 63, 192 }; + + const unsigned char tag[] = { 92, 80, 104, 49, 133, 25, 161, + 215, 173, 101, 219, 211, 136, 91, 210, 145 }; + + encryption->iv.data = apr_pmemdup(p, iv, sizeof(iv)); + encryption->iv.len = sizeof(iv); + + encryption->aad.data = apr_pmemdup(p, aad, sizeof(aad)); + encryption->aad.len = sizeof(aad); + + encryption->cipher.data = apr_pmemdup(p, cipher, sizeof(cipher)); + encryption->cipher.len = sizeof(cipher); + + encryption->tag.data = apr_pmemdup(p, tag, sizeof(tag)); + encryption->tag.len = sizeof(tag); + + } + + /* RSAES-PKCS1-v1_5 - https://tools.ietf.org/html/rfc7516#appendix-A.2 */ + else if (!strncmp("{\"alg\":\"RSA1_5\",\"enc\":\"A128CBC-HS256\"}", buf, len)) { + + const unsigned char iv[] = { 3, 22, 60, 12, 43, 67, 104, 105, + 108, 108, 105, 99, 111, 116, 104, 101 }; + + const unsigned char cipher[] = { 40, 57, 83, 181, 119, 33, 133, + 148, 198, 185, 243, 24, 152, 230, 6, 75, 129, 223, 127, + 19, 210, 82, 183, 230, 168, 33, 215, 104, 143, 112, 56, + 102 }; + + const unsigned char tag[] = { 246, 17, 244, 190, 4, 95, 98, 3, + 231, 0, 115, 157, 242, 203, 100, 191 }; + + encryption->iv.data = apr_pmemdup(p, iv, sizeof(iv)); + encryption->iv.len = sizeof(iv); + + encryption->cipher.data = apr_pmemdup(p, cipher, sizeof(cipher)); + encryption->cipher.len = sizeof(cipher); + + encryption->tag.data = apr_pmemdup(p, tag, sizeof(tag)); + encryption->tag.len = sizeof(tag); + + } + + /* A128KW A128CBC-HS256 - https://tools.ietf.org/html/rfc7516#appendix-A.3 */ + else if (!strncmp("{\"alg\":\"A128KW\",\"enc\":\"A128CBC-HS256\"}", buf, len)) { + + const unsigned char iv[] = { 3, 22, 60, 12, 43, 67, 104, 105, + 108, 108, 105, 99, 111, 116, 104, 101 }; + + const unsigned char cipher[] = { 40, 57, 83, 181, 119, 33, 133, + 148, 198, 185, 243, 24, 152, 230, 6, 75, 129, 223, 127, + 19, 210, 82, 183, 230, 168, 33, 215, 104, 143, 112, 56, + 102 }; + + const unsigned char tag[] = { 83, 73, 191, 98, 104, 205, 211, + 128, 201, 189, 199, 133, 32, 38, 194, 85 }; + + encryption->iv.data = apr_pmemdup(p, iv, sizeof(iv)); + encryption->iv.len = sizeof(iv); + + encryption->cipher.data = apr_pmemdup(p, cipher, sizeof(cipher)); + encryption->cipher.len = sizeof(cipher); + + encryption->tag.data = apr_pmemdup(p, tag, sizeof(tag)); + encryption->tag.len = sizeof(tag); + + } + + /* General JWE JSON - https://tools.ietf.org/html/rfc7516#appendix-A.4 */ + else if (!strncmp("{\"enc\":\"A128CBC-HS256\"}", buf, len)) { + + const unsigned char iv[] = { 3, 22, 60, 12, 43, 67, 104, 105, + 108, 108, 105, 99, 111, 116, 104, 101 }; + + const unsigned char cipher[] = { 40, 57, 83, 181, 119, 33, 133, + 148, 198, 185, 243, 24, 152, 230, 6, 75, 129, 223, 127, + 19, 210, 82, 183, 230, 168, 33, 215, 104, 143, 112, 56, + 102 }; + + const unsigned char tag[] = { 51, 63, 149, 60, 252, 148, + 225, 25, 92, 185, 139, 245, 35, 2, 47, 207 }; + + encryption->iv.data = apr_pmemdup(p, iv, sizeof(iv)); + encryption->iv.len = sizeof(iv); + + encryption->cipher.data = apr_pmemdup(p, cipher, sizeof(cipher)); + encryption->cipher.len = sizeof(cipher); + + encryption->tag.data = apr_pmemdup(p, tag, sizeof(tag)); + encryption->tag.len = sizeof(tag); + + } + + else { + apr_errprintf(&jose->result, jose->pool, NULL, 0, + "Protected header not recognised: %.*s", (int)len, buf); + return APR_ENOTIMPL; + } + + + } + } + + + if (recipient) { + header = recipient->header; + if (header) { + + ABTS_INT_EQUAL(tc, APR_JSON_OBJECT, header->type); + + alg = apr_json_object_get(header, APR_JOSE_JWKSE_ALGORITHM, + APR_JSON_VALUE_STRING); + } + } + + if (!alg) { + protected_header = encryption->protected; + if (protected_header) { + + ABTS_INT_EQUAL(tc, APR_JSON_OBJECT, protected_header->type); + + alg = apr_json_object_get(protected_header, + APR_JOSE_JWKSE_ALGORITHM, APR_JSON_VALUE_STRING); + } + } + + if (alg && recipient) { + + ABTS_INT_EQUAL(tc, APR_JSON_STRING, alg->v->type); + + /* RSAES-OAEP and AES GCM jwe - https://tools.ietf.org/html/rfc7516#appendix-A.1 */ + if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "RSA-OAEP", + alg->v->value.string.len)) { + + const unsigned char ekey[] = { 56, 163, 154, 192, 58, 53, 222, + 4, 105, 218, 136, 218, 29, 94, 203, 22, 150, 92, 129, + 94, 211, 232, 53, 89, 41, 60, 138, 56, 196, 216, 82, 98, + 168, 76, 37, 73, 70, 7, 36, 8, 191, 100, 136, 196, 244, + 220, 145, 158, 138, 155, 4, 117, 141, 230, 199, 247, + 173, 45, 182, 214, 74, 177, 107, 211, 153, 11, 205, 196, + 171, 226, 162, 128, 171, 182, 13, 237, 239, 99, 193, 4, + 91, 219, 121, 223, 107, 167, 61, 119, 228, 173, 156, + 137, 134, 200, 80, 219, 74, 253, 56, 185, 91, 177, 34, + 158, 89, 154, 205, 96, 55, 18, 138, 43, 96, 218, 215, + 128, 124, 75, 138, 243, 85, 25, 109, 117, 140, 26, 155, + 249, 67, 167, 149, 231, 100, 6, 41, 65, 214, 251, 232, + 87, 72, 40, 182, 149, 154, 168, 31, 193, 126, 215, 89, + 28, 111, 219, 125, 182, 139, 235, 195, 197, 23, 234, 55, + 58, 63, 180, 68, 202, 206, 149, 75, 205, 248, 176, 67, + 39, 178, 60, 98, 193, 32, 238, 122, 96, 158, 222, 57, + 183, 111, 210, 55, 188, 215, 206, 180, 166, 150, 166, + 106, 250, 55, 229, 72, 40, 69, 214, 216, 104, 23, 40, + 135, 212, 28, 127, 41, 80, 175, 174, 168, 115, 171, 197, + 89, 116, 92, 103, 246, 83, 216, 182, 176, 84, 37, 147, + 35, 45, 219, 172, 99, 226, 233, 73, 37, 124, 42, 72, 49, + 242, 35, 127, 184, 134, 117, 114, 135, 206 }; + + recipient->ekey.data = apr_pmemdup(p, ekey, sizeof(ekey)); + recipient->ekey.len = sizeof(ekey); + + return APR_SUCCESS; + } + + /* RSAES-PKCS1-v1_5 - https://tools.ietf.org/html/rfc7516#appendix-A.2 */ + if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "RSA1_5", + alg->v->value.string.len)) { + + const unsigned char ekey[] = { 80, 104, 72, 58, 11, 130, 236, + 139, 132, 189, 255, 205, 61, 86, 151, 176, 99, 40, 44, + 233, 176, 189, 205, 70, 202, 169, 72, 40, 226, 181, 156, + 223, 120, 156, 115, 232, 150, 209, 145, 133, 104, 112, + 237, 156, 116, 250, 65, 102, 212, 210, 103, 240, 177, + 61, 93, 40, 71, 231, 223, 226, 240, 157, 15, 31, 150, + 89, 200, 215, 198, 203, 108, 70, 117, 66, 212, 238, 193, + 205, 23, 161, 169, 218, 243, 203, 128, 214, 127, 253, + 215, 139, 43, 17, 135, 103, 179, 220, 28, 2, 212, 206, + 131, 158, 128, 66, 62, 240, 78, 186, 141, 125, 132, 227, + 60, 137, 43, 31, 152, 199, 54, 72, 34, 212, 115, 11, + 152, 101, 70, 42, 219, 233, 142, 66, 151, 250, 126, 146, + 141, 216, 190, 73, 50, 177, 146, 5, 52, 247, 28, 197, + 21, 59, 170, 247, 181, 89, 131, 241, 169, 182, 246, 99, + 15, 36, 102, 166, 182, 172, 197, 136, 230, 120, 60, 58, + 219, 243, 149, 94, 222, 150, 154, 194, 110, 227, 225, + 112, 39, 89, 233, 112, 207, 211, 241, 124, 174, 69, 221, + 179, 107, 196, 225, 127, 167, 112, 226, 12, 242, 16, 24, + 28, 120, 182, 244, 213, 244, 153, 194, 162, 69, 160, + 244, 248, 63, 165, 141, 4, 207, 249, 193, 79, 131, 0, + 169, 233, 127, 167, 101, 151, 125, 56, 112, 111, 248, + 29, 232, 90, 29, 147, 110, 169, 146, 114, 165, 204, 71, + 136, 41, 252 }; + + recipient->ekey.data = apr_pmemdup(p, ekey, sizeof(ekey)); + recipient->ekey.len = sizeof(ekey); + + return APR_SUCCESS; + } + + /* A128KW A128CBC-HS256 - https://tools.ietf.org/html/rfc7516#appendix-A.3 */ + if (alg->v->type == APR_JSON_STRING + && !strncmp(alg->v->value.string.p, "A128KW", + alg->v->value.string.len)) { + + const unsigned char ekey[] = { 232, 160, 123, 211, 183, 76, 245, + 132, 200, 128, 123, 75, 190, 216, 22, 67, 201, 138, 193, + 186, 9, 91, 122, 31, 246, 90, 28, 139, 57, 3, 76, 124, + 193, 11, 98, 37, 173, 61, 104, 57 }; + + recipient->ekey.data = apr_pmemdup(p, ekey, sizeof(ekey)); + recipient->ekey.len = sizeof(ekey); + + return APR_SUCCESS; + } + + } + } + + return APR_ENOTIMPL; +} + +static apr_status_t decrypt_cb(apr_bucket_brigade *brigade, apr_jose_t *jose, + apr_jose_recipient_t *recipient, apr_jose_encryption_t *encryption, + apr_json_value_t *header, apr_jose_text_t *ph64, apr_jose_text_t *aad64, + void *ctx, int *dflags, apr_pool_t *pool) +{ + + *dflags = APR_JOSE_FLAG_NONE; + + if (encryption && recipient && header) { + + char buf[1024]; + apr_size_t len = sizeof(buf); + + apr_bucket_brigade *bb = apr_brigade_create(p, + brigade->bucket_alloc); + + apr_json_encode(bb, NULL, NULL, header, + APR_JSON_FLAGS_WHITESPACE, p); + + apr_brigade_flatten(bb, buf, &len); + + /* RSAES-OAEP and AES GCM jwe - https://tools.ietf.org/html/rfc7516#appendix-A.1 */ + if (!strncmp("{\"alg\":\"RSA-OAEP\",\"enc\":\"A256GCM\"}", buf, len)) { + + const char plaintext[] = { 84, 104, 101, 32, 116, 114, 117, + 101, 32, 115, 105, 103, 110, 32, 111, 102, 32, 105, 110, + 116, 101, 108, 108, 105, 103, 101, 110, 99, 101, 32, 105, + 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108, 101, + 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105, + 110, 97, 116, 105, 111, 110, 46 }; + + apr_brigade_write(brigade, NULL, NULL, plaintext, sizeof(plaintext)); + + return APR_SUCCESS; + } + + /* RSAES-PKCS1-v1_5 - https://tools.ietf.org/html/rfc7516#appendix-A.2 */ + else if (!strncmp("{\"jku\":\"https://server.example.com/keys.jwks\",\"enc\":\"A128CBC-HS256\",\"alg\":\"RSA1_5\",\"kid\":\"2011-04-29\"}", buf, len)) { + + const char plaintext[] = { 76, 105, 118, 101, 32, 108, 111, 110, + 103, 32, 97, 110, 100, 32, 112, 114, 111, 115, 112, 101, + 114, 46 }; + + apr_brigade_write(brigade, NULL, NULL, plaintext, sizeof(plaintext)); + + return APR_SUCCESS; + } + + /* Flattened JWE JSON - https://tools.ietf.org/html/rfc7516#appendix-A.5 */ + else if (!strncmp("{\"jku\":\"https://server.example.com/keys.jwks\",\"enc\":\"A128CBC-HS256\",\"alg\":\"A128KW\",\"kid\":\"7\"}", buf, len)) { + + const char plaintext[] = { 76, 105, 118, 101, 32, 108, 111, 110, + 103, 32, 97, 110, 100, 32, 112, 114, 111, 115, 112, 101, + 114, 46 }; + + apr_brigade_write(brigade, NULL, NULL, plaintext, sizeof(plaintext)); + + return APR_SUCCESS; + } + + + else { + apr_errprintf(&jose->result, pool, NULL, 0, + "Header not recognised: %.*s", (int)len, buf); + return APR_ENOTIMPL; + } + + } + + return APR_ENOTIMPL; +} + +/** + * Test from https://tools.ietf.org/html/rfc7515#appendix-A.5 + */ +static void test_jose_encode_jws_compact_unsecured(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_jose_t *jdata = NULL; + apr_jose_signature_t signature; + char buf[1024]; + apr_size_t len = sizeof(buf); + apr_off_t offset; + apr_status_t status; + + const char *ph = "{\"alg\":\"none\"}"; + const unsigned char pl[] = {123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, + 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56, + 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, + 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111, + 111, 116, 34, 58, 116, 114, 117, 101, 125}; + const char *expect = "eyJhbGciOiJub25lIn0" + "." + "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" + "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ" + "."; + + apr_jose_cb_t cb; + + cb.sign = sign_cb; + cb.ctx = tc; + + signature.header = NULL; + apr_json_decode(&signature.protected_header, ph, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_jose_data_make(&jdata, "JWT", pl, sizeof(pl), p); + apr_jose_jws_make(&jose, &signature, NULL, jdata, p); + + status = apr_jose_encode(bb, NULL, NULL, jose, &cb, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + apr_brigade_flatten(bb, buf, &len); + ABTS_STR_NEQUAL(tc, expect, buf, len); +} + +/** + * Test from https://tools.ietf.org/html/rfc7515#appendix-A.1 + */ +static void test_jose_encode_jws_compact_hs256(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_jose_t *jdata = NULL; + apr_jose_signature_t signature; + char buf[1024]; + apr_size_t len = sizeof(buf); + apr_off_t offset; + apr_status_t status; + + const unsigned char ph[] = { 123, 34, 116, 121, 112, 34, 58, 34, 74, 87, 84, + 34, 44, 13, 10, 32, 34, 97, 108, 103, 34, 58, 34, 72, 83, 50, 53, + 54, 34, 125 }; + + const unsigned char pl[] = {123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, + 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56, + 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, + 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111, + 111, 116, 34, 58, 116, 114, 117, 101, 125}; + const char *expect = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9" + "." + "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" + "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ" + "." + "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + + apr_jose_cb_t cb; + + cb.sign = sign_cb; + cb.ctx = tc; + + signature.header = NULL; + apr_json_decode(&signature.protected_header, (const char *) ph, sizeof(ph), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_jose_data_make(&jdata, "JWT", pl, sizeof(pl), p); + apr_jose_jws_make(&jose, &signature, NULL, jdata, p); + + status = apr_jose_encode(bb, NULL, NULL, jose, &cb, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + apr_brigade_flatten(bb, buf, &len); + ABTS_STR_NEQUAL(tc, expect, buf, len); +} + +/** + * Test from https://tools.ietf.org/html/rfc7515#appendix-A.6 + */ +static void test_jose_encode_jws_json_general(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_jose_t *jdata = NULL; + apr_jose_signature_t **signature; + apr_jose_signature_t signature1; + apr_jose_signature_t signature2; + apr_array_header_t *signatures = apr_array_make(p, 2, + sizeof(apr_jose_signature_t *)); + char buf[1024]; + apr_size_t len = sizeof(buf); + apr_off_t offset; + apr_status_t status; + + const unsigned char pl[] = { 123, 34, 105, 115, 115, 34, 58, 34, 106, 111, + 101, 34, 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, + 56, 49, 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, + 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, + 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125 }; + + const char *s1h = "{\"kid\":\"2010-12-29\"}"; + const char *s1ph = "{\"alg\":\"RS256\"}"; + const char *s2h = "{\"kid\":\"e9bc097a-ce51-4036-9562-d2ade882db0d\"}"; + const char *s2ph = "{\"alg\":\"ES256\"}"; + + const char *expect = "{" + "\"payload\":" + "\"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF" + "tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ\"," + "\"signatures\":[" + "{\"protected\":\"eyJhbGciOiJSUzI1NiJ9\"," + "\"header\":" + "{\"kid\":\"2010-12-29\"}," + "\"signature\":" + "\"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ" + "mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb" + "KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl" + "b1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES" + "c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX" + "LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw\"}," + "{\"protected\":\"eyJhbGciOiJFUzI1NiJ9\"," + "\"header\":" + "{\"kid\":\"e9bc097a-ce51-4036-9562-d2ade882db0d\"}," + "\"signature\":" + "\"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS" + "lSApmWQxfKTUJqPP3-Kg6NU1Q\"}]" + "}"; + + apr_jose_cb_t cb; + + cb.sign = sign_cb; + cb.ctx = tc; + + apr_json_decode(&signature1.header, (const char *) s1h, strlen(s1h), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + apr_json_decode(&signature1.protected_header, (const char *) s1ph, strlen(s1ph), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + apr_json_decode(&signature2.header, (const char *) s2h, strlen(s2h), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + apr_json_decode(&signature2.protected_header, (const char *) s2ph, strlen(s2ph), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + signature = apr_array_push(signatures); + *signature = &signature1; + signature = apr_array_push(signatures); + *signature = &signature2; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_jose_data_make(&jdata, "JWT", pl, sizeof(pl), p); + apr_jose_jws_json_make(&jose, NULL, signatures, jdata, p); + + status = apr_jose_encode(bb, NULL, NULL, jose, &cb, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + apr_brigade_flatten(bb, buf, &len); + ABTS_STR_NEQUAL(tc, expect, buf, len); + +} + +/** + * Test from https://tools.ietf.org/html/rfc7515#appendix-A.7 + */ +static void test_jose_encode_jws_json_flattened(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_jose_t *jdata = NULL; + apr_jose_signature_t signature2; + char buf[1024]; + apr_size_t len = sizeof(buf); + apr_off_t offset; + apr_status_t status; + + const unsigned char pl[] = { 123, 34, 105, 115, 115, 34, 58, 34, 106, 111, + 101, 34, 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, + 56, 49, 57, 51, 56, 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, + 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, + 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125 }; + + const char *s2h = "{\"kid\":\"e9bc097a-ce51-4036-9562-d2ade882db0d\"}"; + const char *s2ph = "{\"alg\":\"ES256\"}"; + + const char *expect = "{" + "\"payload\":" + "\"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF" + "tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ\"," + "\"protected\":\"eyJhbGciOiJFUzI1NiJ9\"," + "\"header\":" + "{\"kid\":\"e9bc097a-ce51-4036-9562-d2ade882db0d\"}," + "\"signature\":" + "\"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS" + "lSApmWQxfKTUJqPP3-Kg6NU1Q\"" + "}"; + + apr_jose_cb_t cb; + + cb.sign = sign_cb; + cb.ctx = tc; + + apr_json_decode(&signature2.header, (const char *) s2h, strlen(s2h), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + apr_json_decode(&signature2.protected_header, (const char *) s2ph, strlen(s2ph), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_jose_data_make(&jdata, "JWT", pl, sizeof(pl), p); + apr_jose_jws_json_make(&jose, &signature2, NULL, jdata, p); + + status = apr_jose_encode(bb, NULL, NULL, jose, &cb, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + apr_brigade_flatten(bb, buf, &len); + ABTS_STR_NEQUAL(tc, expect, buf, len); + +} + +/** + * Test from https://tools.ietf.org/html/rfc7516#appendix-A.1 + */ +static void test_jose_encode_jwe_compact_rsaes_oaep_aes_gcm(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_jose_t *jdata = NULL; + apr_jose_encryption_t *encryption; + apr_jose_recipient_t *recipient; + apr_json_value_t *header = NULL; + apr_json_value_t *protected_header = NULL; + char buf[1024]; + apr_size_t len = sizeof(buf); + apr_off_t offset; + apr_status_t status; + + const char *ph = "{\"alg\":\"RSA-OAEP\",\"enc\":\"A256GCM\"}"; + + const unsigned char pl[] = { 84, 104, 101, 32, 116, 114, 117, 101, 32, 115, + 105, 103, 110, 32, 111, 102, 32, 105, 110, 116, 101, 108, 108, 105, + 103, 101, 110, 99, 101, 32, 105, 115, 32, 110, 111, 116, 32, 107, + 110, 111, 119, 108, 101, 100, 103, 101, 32, 98, 117, 116, 32, 105, + 109, 97, 103, 105, 110, 97, 116, 105, 111, 110, 46 }; + + const char *expect = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ." + "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe" + "ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb" + "Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV" + "mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8" + "1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi" + "6UklfCpIMfIjf7iGdXKHzg." + "48V1_ALb6US04U3b." + "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji" + "SdiwkIr3ajwQzaBtQD_A." + "XFBoMYUZodetZdvTiFvSkQ"; + + apr_jose_cb_t cb; + + cb.encrypt = encrypt_cb; + cb.ctx = tc; + + apr_json_decode(&protected_header, ph, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_jose_data_make(&jdata, "JWT", pl, sizeof(pl), p); + apr_jose_recipient_make(&recipient, header, p); + apr_jose_encryption_make(&encryption, NULL, protected_header, p); + apr_jose_jwe_make(&jose, recipient, NULL, encryption, jdata, p); + + status = apr_jose_encode(bb, NULL, NULL, jose, &cb, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + apr_brigade_flatten(bb, buf, &len); + ABTS_STR_NEQUAL(tc, expect, buf, len); +} + +/** + * Test from https://tools.ietf.org/html/rfc7516#appendix-A.4 + */ +static void test_jose_encode_jwe_json_general(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_jose_t *jdata = NULL; + apr_json_value_t *header = NULL; + apr_json_value_t *protected_header = NULL; + apr_jose_recipient_t **recipient; + apr_jose_recipient_t recipient1; + apr_jose_recipient_t recipient2; + apr_array_header_t *recipients = apr_array_make(p, 2, + sizeof(apr_jose_recipient_t *)); + apr_jose_encryption_t *encryption; + char buf[1024]; + apr_size_t len = sizeof(buf); + apr_off_t offset; + apr_status_t status; + + const char *r1h = "{\"alg\":\"RSA1_5\",\"kid\":\"2011-04-29\"}"; + const char *r2h = "{\"alg\":\"A128KW\",\"kid\":\"7\"}"; + + const char *ph = "{\"enc\":\"A128CBC-HS256\"}"; + const char *h = "{\"jku\":\"https://server.example.com/keys.jwks\"}"; + + const unsigned char pl[] = { 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, + 97, 110, 100, 32, 112, 114, 111, 115, 112, 101, 114, 46 }; + + const char *expect = "{" + "\"protected\":" + "\"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\"," + "\"unprotected\":" + "{\"jku\":\"https://server.example.com/keys.jwks\"}," + "\"recipients\":[" + "{\"header\":" + "{\"alg\":\"RSA1_5\",\"kid\":\"2011-04-29\"}," + "\"encrypted_key\":" + "\"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-" + "kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx" + "GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3" + "YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh" + "cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg" + "wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A\"}," + "{\"header\":" + "{\"alg\":\"A128KW\",\"kid\":\"7\"}," + "\"encrypted_key\":" + "\"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ\"}]," + "\"iv\":" + "\"AxY8DCtDaGlsbGljb3RoZQ\"," + "\"ciphertext\":" + "\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\"," + "\"tag\":" + "\"Mz-VPPyU4RlcuYv1IwIvzw\"" + "}\";"; + + apr_jose_cb_t cb; + + cb.encrypt = encrypt_cb; + cb.ctx = tc; + + apr_json_decode(&recipient1.header, (const char *) r1h, strlen(r1h), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + apr_json_decode(&recipient2.header, (const char *) r2h, strlen(r2h), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + recipient = apr_array_push(recipients); + *recipient = &recipient1; + recipient = apr_array_push(recipients); + *recipient = &recipient2; + + + + + apr_json_decode(&header, h, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + apr_json_decode(&protected_header, ph, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_jose_data_make(&jdata, "plain", pl, sizeof(pl), p); + apr_jose_encryption_make(&encryption, header, protected_header, p); + apr_jose_jwe_json_make(&jose, NULL, recipients, encryption, jdata, p); + + status = apr_jose_encode(bb, NULL, NULL, jose, &cb, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + apr_brigade_flatten(bb, buf, &len); + ABTS_STR_NEQUAL(tc, expect, buf, len); +} + +/** + * Test from https://tools.ietf.org/html/rfc7516#appendix-A.5 + */ +static void test_jose_encode_jwe_json_flattened(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_jose_t *jdata = NULL; + apr_json_value_t *header = NULL; + apr_json_value_t *protected_header = NULL; + apr_jose_recipient_t recipient; + apr_jose_encryption_t *encryption; + char buf[1024]; + apr_size_t len = sizeof(buf); + apr_off_t offset; + apr_status_t status; + + const char *rh = "{\"alg\":\"A128KW\",\"kid\":\"7\"}"; + + const char *ph = "{\"enc\":\"A128CBC-HS256\"}"; + const char *h = "{\"jku\":\"https://server.example.com/keys.jwks\"}"; + + const unsigned char pl[] = { 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, + 97, 110, 100, 32, 112, 114, 111, 115, 112, 101, 114, 46 }; + + const char *expect = "{" + "\"protected\":" + "\"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\"," + "\"unprotected\":" + "{\"jku\":\"https://server.example.com/keys.jwks\"}," + "\"header\":" + "{\"alg\":\"A128KW\",\"kid\":\"7\"}," + "\"encrypted_key\":" + "\"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ\"," + "\"iv\":" + "\"AxY8DCtDaGlsbGljb3RoZQ\"," + "\"ciphertext\":" + "\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\"," + "\"tag\":" + "\"Mz-VPPyU4RlcuYv1IwIvzw\"" + "}"; + + apr_jose_cb_t cb; + + cb.encrypt = encrypt_cb; + cb.ctx = tc; + + apr_json_decode(&recipient.header, (const char *) rh, strlen(rh), &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + apr_json_decode(&header, h, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + apr_json_decode(&protected_header, ph, APR_JSON_VALUE_STRING, &offset, + APR_JSON_FLAGS_WHITESPACE, 10, p); + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_jose_data_make(&jdata, "plain", pl, sizeof(pl), p); + apr_jose_encryption_make(&encryption, header, protected_header, p); + apr_jose_jwe_json_make(&jose, &recipient, NULL, encryption, jdata, p); + + status = apr_jose_encode(bb, NULL, NULL, jose, &cb, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + + apr_brigade_flatten(bb, buf, &len); + ABTS_STR_NEQUAL(tc, expect, buf, len); +} + +/** + * Test from https://tools.ietf.org/html/rfc7515#appendix-A.5 + */ +static void test_jose_decode_jws_compact_unsecured(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_json_kv_t *kv; + apr_status_t status; + + const char *source = "eyJhbGciOiJub25lIn0" + "." + "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" + "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ" + "."; + + apr_jose_cb_t cb; + + cb.verify = verify_cb; + cb.decrypt = decrypt_cb; + cb.ctx = tc; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_brigade_write(bb, NULL, NULL, source, strlen(source)); + + status = apr_jose_decode(&jose, "JWT", bb, &cb, 10, APR_JOSE_FLAG_NONE, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + ABTS_PTR_NOTNULL(tc, jose); + ABTS_INT_EQUAL(tc, APR_JOSE_TYPE_JWT, jose->type); + + kv = apr_json_object_get(jose->jose.jwt->claims, "iss", + APR_JSON_VALUE_STRING); + ABTS_PTR_NOTNULL(tc, kv); + ABTS_INT_EQUAL(tc, APR_JSON_STRING, kv->v->type); + +} + +/** + * Test from https://tools.ietf.org/html/rfc7515#appendix-A.1 + */ +static void test_jose_decode_jws_compact_hs256(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_json_kv_t *kv; + apr_status_t status; + + const char *source = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9" + "." + "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" + "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ" + "." + "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + + apr_jose_cb_t cb; + + cb.verify = verify_cb; + cb.decrypt = decrypt_cb; + cb.ctx = tc; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_brigade_write(bb, NULL, NULL, source, strlen(source)); + + status = apr_jose_decode(&jose, "JWT", bb, &cb, 10, APR_JOSE_FLAG_NONE, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + ABTS_PTR_NOTNULL(tc, jose); + ABTS_INT_EQUAL(tc, APR_JOSE_TYPE_JWT, jose->type); + + kv = apr_json_object_get(jose->jose.jwt->claims, "iss", + APR_JSON_VALUE_STRING); + ABTS_PTR_NOTNULL(tc, kv); + ABTS_INT_EQUAL(tc, APR_JSON_STRING, kv->v->type); + +} + +/** + * Test from https://tools.ietf.org/html/rfc7515#appendix-A.6 + */ +static void test_jose_decode_jws_json_general(abts_case *tc, void *data) +{ + const char *source = "{" + "\"payload\":" + "\"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF" + "tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ\"," + "\"signatures\":[" + "{\"protected\":\"eyJhbGciOiJSUzI1NiJ9\"," + "\"header\":" + "{\"kid\":\"2010-12-29\"}," + "\"signature\":" + "\"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ" + "mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb" + "KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl" + "b1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES" + "c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX" + "LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw\"}," + "{\"protected\":\"eyJhbGciOiJFUzI1NiJ9\"," + "\"header\":" + "{\"kid\":\"e9bc097a-ce51-4036-9562-d2ade882db0d\"}," + "\"signature\":" + "\"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS" + "lSApmWQxfKTUJqPP3-Kg6NU1Q\"}]" + "}"; + + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_status_t status; + + apr_jose_cb_t cb; + + cb.verify = verify_cb; + cb.decrypt = decrypt_cb; + cb.ctx = tc; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_brigade_write(bb, NULL, NULL, source, strlen(source)); + + status = apr_jose_decode(&jose, "JOSE+JSON", bb, &cb, 10, APR_JOSE_FLAG_NONE, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + ABTS_PTR_NOTNULL(tc, jose); + + /* + * There is nothing in this structure to identify the MIME type of the payload, + * so raw data is returned. + */ + ABTS_INT_EQUAL(tc, APR_JOSE_TYPE_DATA, jose->type); + +} + +/** + * Test from https://tools.ietf.org/html/rfc7515#appendix-A.7 + */ +static void test_jose_decode_jws_json_flattened(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_status_t status; + + const char *source = "{" + "\"payload\":" + "\"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF" + "tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ\"," + "\"protected\":\"eyJhbGciOiJFUzI1NiJ9\"," + "\"header\":" + "{\"kid\":\"e9bc097a-ce51-4036-9562-d2ade882db0d\"}," + "\"signature\":" + "\"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS" + "lSApmWQxfKTUJqPP3-Kg6NU1Q\"" + "}"; + + apr_jose_cb_t cb; + + cb.verify = verify_cb; + cb.decrypt = decrypt_cb; + cb.ctx = tc; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_brigade_write(bb, NULL, NULL, source, strlen(source)); + + status = apr_jose_decode(&jose, "JOSE+JSON", bb, &cb, 10, APR_JOSE_FLAG_NONE, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + ABTS_PTR_NOTNULL(tc, jose); + ABTS_INT_EQUAL(tc, APR_JOSE_TYPE_DATA, jose->type); + +} + +/** + * Test from https://tools.ietf.org/html/rfc7516#appendix-A.1 + */ +static void test_jose_decode_jwe_compact_rsaes_oaep_aes_gcm(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_status_t status; + + const char *source = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ." + "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe" + "ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb" + "Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV" + "mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8" + "1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi" + "6UklfCpIMfIjf7iGdXKHzg." + "48V1_ALb6US04U3b." + "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji" + "SdiwkIr3ajwQzaBtQD_A." + "XFBoMYUZodetZdvTiFvSkQ"; + + apr_jose_cb_t cb; + + cb.verify = verify_cb; + cb.decrypt = decrypt_cb; + cb.ctx = tc; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_brigade_write(bb, NULL, NULL, source, strlen(source)); + + status = apr_jose_decode(&jose, "JWE", bb, &cb, 10, APR_JOSE_FLAG_NONE, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + ABTS_PTR_NOTNULL(tc, jose); + ABTS_INT_EQUAL(tc, APR_JOSE_TYPE_DATA, jose->type); + +} + +/** + * Test from https://tools.ietf.org/html/rfc7516#appendix-A.4 + */ +static void test_jose_decode_jwe_json_general(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_status_t status; + + const char *source = "{" + "\"protected\":" + "\"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\"," + "\"unprotected\":" + "{\"jku\":\"https://server.example.com/keys.jwks\"}," + "\"recipients\":[" + "{\"header\":" + "{\"alg\":\"RSA1_5\",\"kid\":\"2011-04-29\"}," + "\"encrypted_key\":" + "\"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-" + "kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKx" + "GHZ7PcHALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3" + "YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPh" + "cCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPg" + "wCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A\"}," + "{\"header\":" + "{\"alg\":\"A128KW\",\"kid\":\"7\"}," + "\"encrypted_key\":" + "\"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ\"}]," + "\"iv\":" + "\"AxY8DCtDaGlsbGljb3RoZQ\"," + "\"ciphertext\":" + "\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\"," + "\"tag\":" + "\"Mz-VPPyU4RlcuYv1IwIvzw\"" + "}"; + + apr_jose_cb_t cb; + + cb.verify = verify_cb; + cb.decrypt = decrypt_cb; + cb.ctx = tc; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_brigade_write(bb, NULL, NULL, source, strlen(source)); + + status = apr_jose_decode(&jose, "JOSE+JSON", bb, &cb, 10, + APR_JOSE_FLAG_NONE, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + ABTS_PTR_NOTNULL(tc, jose); + ABTS_INT_EQUAL(tc, APR_JOSE_TYPE_DATA, jose->type); + +} + +/** + * Test from https://tools.ietf.org/html/rfc7516#appendix-A.5 + */ +static void test_jose_decode_jwe_json_flattened(abts_case *tc, void *data) +{ + apr_bucket_alloc_t *ba; + apr_bucket_brigade *bb; + apr_jose_t *jose = NULL; + apr_status_t status; + + const char *source = "{" + "\"protected\":" + "\"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\"," + "\"unprotected\":" + "{\"jku\":\"https://server.example.com/keys.jwks\"}," + "\"header\":" + "{\"alg\":\"A128KW\",\"kid\":\"7\"}," + "\"encrypted_key\":" + "\"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ\"," + "\"iv\":" + "\"AxY8DCtDaGlsbGljb3RoZQ\"," + "\"ciphertext\":" + "\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\"," + "\"tag\":" + "\"Mz-VPPyU4RlcuYv1IwIvzw\"" + "}"; + + apr_jose_cb_t cb; + + cb.verify = verify_cb; + cb.decrypt = decrypt_cb; + cb.ctx = tc; + + ba = apr_bucket_alloc_create(p); + bb = apr_brigade_create(p, ba); + + apr_brigade_write(bb, NULL, NULL, source, strlen(source)); + + status = apr_jose_decode(&jose, "JOSE+JSON", bb, &cb, 10, + APR_JOSE_FLAG_NONE, p); + + ABTS_INT_EQUAL(tc, APR_SUCCESS, status); + ABTS_PTR_NOTNULL(tc, jose); + ABTS_INT_EQUAL(tc, APR_JOSE_TYPE_DATA, jose->type); + +} + +abts_suite *testjose(abts_suite *suite) +{ + suite = ADD_SUITE(suite); + + abts_run_test(suite, test_jose_decode_jws_compact_unsecured, NULL); + abts_run_test(suite, test_jose_decode_jws_compact_hs256, NULL); + abts_run_test(suite, test_jose_decode_jws_json_general, NULL); + abts_run_test(suite, test_jose_decode_jws_json_flattened, NULL); + abts_run_test(suite, test_jose_decode_jwe_compact_rsaes_oaep_aes_gcm, NULL); + abts_run_test(suite, test_jose_decode_jwe_json_general, NULL); + abts_run_test(suite, test_jose_decode_jwe_json_flattened, NULL); + + abts_run_test(suite, test_jose_encode_jws_compact_unsecured, NULL); + abts_run_test(suite, test_jose_encode_jws_compact_hs256, NULL); + abts_run_test(suite, test_jose_encode_jws_json_general, NULL); + abts_run_test(suite, test_jose_encode_jws_json_flattened, NULL); + abts_run_test(suite, test_jose_encode_jwe_compact_rsaes_oaep_aes_gcm, NULL); + abts_run_test(suite, test_jose_encode_jwe_json_general, NULL); + abts_run_test(suite, test_jose_encode_jwe_json_flattened, NULL); + + return suite; +}