/* * check a provided hash value (at_hash|c_hash) against a corresponding hash calculated for a specified value and algorithm */ static apr_byte_t oidc_proto_validate_hash(request_rec *r, const char *alg, const char *hash, const char *value, const char *type) { /* hash the provided access_token */ char *calc = NULL; unsigned int hash_len = 0; apr_jws_hash_string(r->pool, alg, value, &calc, &hash_len); /* calculate the base64url-encoded value of the hash */ char *encoded = NULL; oidc_base64url_encode(r, &encoded, calc, apr_jws_hash_length(alg) / 2, 1); /* compare the calculated hash against the provided hash */ if ((apr_strnatcmp(encoded, hash) != 0)) { oidc_error(r, "provided \"%s\" hash value (%s) does not match the calculated value (%s)", type, hash, encoded); return FALSE; } oidc_debug(r, "successfully validated the provided \"%s\" hash value (%s) against the calculated value (%s)", type, hash, encoded); return TRUE; }
/* * check a provided hash value (at_hash|c_hash) against a corresponding hash calculated for a specified value and algorithm */ static apr_byte_t oidc_proto_validate_hash(request_rec *r, const char *alg, const char *hash, const char *value, const char *type) { /* hash the provided access_token */ char *calc = NULL; unsigned int hash_len = 0; apr_jws_hash_string(r->pool, alg, value, &calc, &hash_len); /* calculate the base64url-encoded value of the hash */ char *encoded = NULL; int enc_len = oidc_base64url_encode(r, &encoded, calc, apr_jws_hash_length(alg) / 2); /* remove /0 and padding */ enc_len--; if (encoded[enc_len - 1] == ',') enc_len--; if (encoded[enc_len - 1] == ',') enc_len--; /* compare the calculated hash against the provided hash */ if ((strncmp(encoded, hash, enc_len) != 0)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_proto_validate_hash: provided \"%s\" hash value (%s) does not match the calculated value (%s)", type, hash, encoded); return FALSE; } ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_validate_hash: successfully validated the provided \"%s\" hash value (%s) against the calculated value (%s)", type, hash, encoded); return TRUE; }
/* * generate a random value (nonce) to correlate request/response through browser state */ apr_byte_t oidc_proto_generate_nonce(request_rec *r, char **nonce) { unsigned char *nonce_bytes = apr_pcalloc(r->pool, OIDC_PROTO_NONCE_LENGTH); if (apr_generate_random_bytes(nonce_bytes, OIDC_PROTO_NONCE_LENGTH) != APR_SUCCESS) { oidc_error(r, "apr_generate_random_bytes returned an error"); return FALSE; } if (oidc_base64url_encode(r, nonce, (const char *) nonce_bytes, OIDC_PROTO_NONCE_LENGTH, TRUE) <= 0) { oidc_error(r, "oidc_base64url_encode returned an error"); return FALSE; } return TRUE; }
static char * test_proto_validate_jwt(request_rec *r) { apr_jwt_t *jwt = NULL; apr_jwt_error_t err; const char *s_secret = "secret"; const char *s_issuer = "https://localhost"; apr_time_t now = apr_time_sec(apr_time_now()); const char *s_jwt_header = "{" "\"alg\": \"HS256\"" "}"; const char *s_jwt_payload = "{" "\"nonce\": \"543210,\"," "\"iat\": %" APR_TIME_T_FMT "," "\"sub\": \"alice\"," "\"iss\": \"%s\"," "\"aud\": \"bob\"," "\"exp\": %" APR_TIME_T_FMT "}"; s_jwt_payload = apr_psprintf(r->pool, s_jwt_payload, now, s_issuer, now + 600); char *s_jwt_header_encoded = NULL; oidc_base64url_encode(r, &s_jwt_header_encoded, s_jwt_header, strlen(s_jwt_header), 1); char *s_jwt_payload_encoded = NULL; oidc_base64url_encode(r, &s_jwt_payload_encoded, s_jwt_payload, strlen(s_jwt_payload), 1); char *s_jwt_message = apr_psprintf(r->pool, "%s.%s", s_jwt_header_encoded, s_jwt_payload_encoded); unsigned int md_len = 0; unsigned char md[EVP_MAX_MD_SIZE]; const EVP_MD *digest = EVP_get_digestbyname("sha256"); TST_ASSERT("HMAC", HMAC(digest, (const unsigned char * )s_secret, strlen(s_secret), (const unsigned char * )s_jwt_message, strlen(s_jwt_message), md, &md_len) != 0); char *s_jwt_signature_encoded = NULL; oidc_base64url_encode(r, &s_jwt_signature_encoded, (const char *) md, md_len, 1); char *s_jwt = apr_psprintf(r->pool, "%s.%s.%s", s_jwt_header_encoded, s_jwt_payload_encoded, s_jwt_signature_encoded); TST_ASSERT_ERR("apr_jwt_parse", apr_jwt_parse(r->pool, s_jwt, &jwt, NULL, &err), r->pool, err); TST_ASSERT_ERR("apr_jws_verify", apr_jws_verify(r->pool, jwt, oidc_util_merge_symmetric_key(r->pool, NULL, s_secret, NULL), &err), r->pool, err); TST_ASSERT_ERR("oidc_proto_validate_jwt", oidc_proto_validate_jwt(r, jwt, s_issuer, TRUE, TRUE, 10), r->pool, err); apr_jwt_destroy(jwt); return 0; }