function sign_encrypt($payload, $sig, $alg, $enc, $jwks_uri = null, $jwks = null, $client_secret = null, &$cryptoError = null) { global $signing_alg_values_supported, $encryption_alg_values_supported, $encryption_enc_values_supported; log_debug("sign_encrypt sig = %s alg = %s enc = %s", $sig, $alg, $enc); $jwt = is_array($payload) ? json_encode($payload) : $payload; if (isset($sig)) { $sig_param = array('alg' => 'none'); $sig_key = NULL; if (in_array($sig, $signing_alg_values_supported)) { $sig_param['alg'] = $sig; if (substr($sig, 0, 2) == 'HS') { $sig_key = $client_secret; } elseif (substr($sig, 0, 2) == 'RS') { $sig_param['kid'] = OP_SIG_KID; $sig_param['jku'] = OP_JWK_URL; $sig_key = array('key_file' => OP_SIG_PKEY, 'password' => OP_SIG_PKEY_PASSPHRASE); } } else { log_error("sig alg %s not supported", $sig); if ($cryptoError) { $cryptoError = 'error_sig'; } return null; } $jwt = jwt_sign($jwt, $sig_param, $sig_key); if (!$jwt) { if ($cryptoError) { $cryptoError = 'error_sig'; } log_error("Unable to sign payload %s", $jwt); return null; } log_debug('jws = %s', $jwt); } if (isset($alg) && isset($enc)) { if (in_array($alg, $encryption_alg_values_supported) && in_array($enc, $encryption_enc_values_supported)) { $jwk_uri = ''; $encryption_keys = NULL; if ($jwks_uri) { $jwk = get_url($jwks_uri); if ($jwk) { $jwk_uri = $jwks_uri; $encryption_keys = jwk_get_keys($jwk, 'RSA', 'enc', NULL); if (!$encryption_keys || !count($encryption_keys)) { $encryption_keys = NULL; } } } if (!$encryption_keys && !empty($jwks)) { $encryption_keys = jwk_get_keys($jwks, 'RSA', 'enc', NULL); if (!$encryption_keys || !count($encryption_keys)) { $encryption_keys = NULL; } $jwk_uri = NULL; } if (!$encryption_keys) { if ($cryptoError) { $cryptoError = 'error_enc'; } log_error("Unable to get enc keys"); return null; } if (!empty($jwk_uri)) { $header_params = array('jku' => $jwk_uri); } if (isset($encryption_keys[0]['kid'])) { $header_params['kid'] = $encryption_keys[0]['kid']; } $jwt = jwt_encrypt2($jwt, $encryption_keys[0], false, NULL, $header_params, NULL, $alg, $enc, false); if (!$jwt) { if ($cryptoError) { $cryptoError = 'error_enc'; } log_error("Unable to encrypt %s", $jwt); return null; } log_debug('jwe = %s', $jwt); } else { $cryptoError = 'error_enc'; log_error("encryption algs not supported %s %s", $alg, $enc); return null; } } return $jwt; }
function handle_start() { global $g_error; $update = false; if ($_REQUEST['submit'] == 'Logout') { handle_logout(); exit; } unset($_SESSION['id_token']); unset($_SESSION['session_state']); unset($_SESSION['code_verifier']); remember_session_form_options($_REQUEST); log_debug("handle_start : %s", print_r($_REQUEST, true)); $provider = $_REQUEST['provider']; $identifier = $_REQUEST['identifier']; if (!$provider && !$identifier) { $g_error .= "No Identity Provider"; return; } if ($identifier) { $discovery = webfinger_get_provider_info($identifier); if (!$discovery) { $g_error .= "Unable to perform discovery"; return; } if (!check_client_update_options($_REQUEST, $discovery)) { return; } $db_provider = db_get_provider_by_url($discovery['url']); $provider = $db_provider; if (!$provider || !(isset($provider['client_id']) && isset($provider['client_secret']))) { if (!isset($discovery['registration_endpoint'])) { $g_error .= "Provider not found in db for {$discovery['issuer']} and no registration endpoint"; return; } $client_options = get_update_options($_REQUEST); $client_info = register_client($discovery['registration_endpoint'], $client_options); if (!$client_info) { $g_error .= "Unable to register client"; return; } $provider = array_merge(array('name' => $discovery['issuer'], 'url' => $discovery['url'], 'issuer' => $discovery['issuer'], 'client_id' => $client_info['client_id'], 'client_id_issued_at' => $client_info['client_id_issued_at'], 'client_secret' => $client_info['client_secret'], 'registration_access_token' => $client_info['registration_access_token'], 'registration_client_uri' => $client_info['registration_client_uri'], 'client_secret_expires_at' => $client_info['client_secret_expires_at']), $client_options); db_save_provider($discovery['issuer'], $provider); $provider = array_merge($provider, $client_info); $provider = array_merge($provider, $discovery); } else { $provider->delete(); if (!isset($discovery['registration_endpoint'])) { $g_error .= "Provider not found in db for {$discovery['issuer']} and no registration endpoint"; return; } $client_options = get_update_options($_REQUEST); $client_info = register_client($discovery['registration_endpoint'], $client_options); if (!$client_info) { $g_error .= "Unable to register client"; return; } $provider = array_merge(array('name' => $discovery['issuer'], 'url' => $discovery['url'], 'issuer' => $discovery['issuer'], 'client_id' => $client_info['client_id'], 'client_id_issued_at' => $client_info['client_id_issued_at'], 'client_secret' => $client_info['client_secret'], 'registration_access_token' => $client_info['registration_access_token'], 'registration_client_uri' => $client_info['registration_client_uri'], 'client_secret_expires_at' => $client_info['client_secret_expires_at']), $client_options); db_save_provider($discovery['issuer'], $provider); $provider = array_merge($provider, $client_info); $provider = array_merge($provider, $discovery); } } elseif ($provider) { $db_provider = db_get_provider($provider); if (!$db_provider) { $g_error .= "Unregistered Identity Provider"; return; } $p_info = $db_provider->toArray(); if ($p_info['authorization_endpoint']) { $provider = $p_info; if (!$provider['client_id'] || !$provider['client_secret']) { $client_options = get_update_options($_REQUEST); log_debug('update options = %s', print_r($client_options, true)); $client_info = register_client($provider['registration_endpoint'], $client_options); if (!$client_info) { $g_error .= "Unable to register client"; return; } $provider = array_merge(array('client_id' => $client_info['client_id'], 'client_secret' => $client_info['client_secret']), $client_options); db_save_provider($db_provider['name'], $provider); } if ($p_info['name'] == RP_PROTOCOL . 'self-issued.me') { $provider['client_id'] = RP_REDIRECT_URI; $provider['client_secret'] = ''; } } else { $provider_url = $p_info['url']; log_info("Provider URL = %s", $provider_url); $discovery = webfinger_get_provider_info($p_info['url']); if (!check_client_update_options($_REQUEST, $discovery)) { return; } if (!isset($discovery['registration_endpoint'])) { $g_error .= "Provider not found in db for {$discovery['issuer']} and no registration endpoint"; return; } $client_options = get_update_options($_REQUEST); $client_info = register_client($discovery['registration_endpoint'], $client_options); log_debug('update options = %s', print_r($client_options, true)); if (!$client_info) { $g_error .= "Unable to register client"; return; } $provider = array_merge(array('name' => $discovery['issuer'], 'url' => $discovery['url'], 'issuer' => $discovery['issuer'], 'client_id' => $client_info['client_id'], 'client_id_issued_at' => $client_info['client_id_issued_at'], 'client_secret' => $client_info['client_secret'], 'registration_access_token' => $client_info['registration_access_token'], 'registration_client_uri' => $client_info['registration_client_uri'], 'client_secret_expires_at' => $client_info['client_secret_expires_at']), $client_options); $db_provider->delete(); db_save_provider($discovery['issuer'], $provider); $provider = array_merge($provider, $client_info); $provider = array_merge($provider, $discovery); } } $_SESSION['provider'] = $provider; log_debug('final provider info %s', print_r($provider, true)); $state = bin2hex(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)); $nonce = bin2hex(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)); $response_type = ''; if ($_REQUEST['response_type']) { $response_type = $_REQUEST['response_type']; } if (!$response_type) { if ($_REQUEST['response_type1']) { $response_type = $_REQUEST['response_type1']; } } if (!$response_type) { $g_error = 'No response type'; return; } $query_params = array('state' => $state, 'redirect_uri' => RP_REDIRECT_URI, 'response_type' => $response_type, 'client_id' => $provider['client_id'], 'nonce' => $nonce); $scope_types = array('openid', 'profile', 'email', 'address', 'phone', 'offline_access'); $scopes = array(); foreach ($scope_types as $scope_type) { $param_name = 'scope_' . $scope_type; if ($_REQUEST[$param_name] == 'on') { $scopes[] = $scope_type; } } if (isset($provider['scopes_supported'])) { $provider_scopes = $provider['scopes_supported']; if (!is_array($provider_scopes)) { $provider_scopes = explode(' ', $provider_scopes); } log_debug('provider scopes = %s', print_r($provider_scopes, true)); $diff = array_diff($provider_scopes, $scope_types); log_debug('diff = %s', print_r($diff, true)); if (isset($db_provider) && isset($db_provider['authorization_endpoint'])) { $provider_scopes = $diff; } else { $provider_scopes = array(); } } else { $provider_scopes = array(); } $unique_scopes = array_unique(array_merge($scopes, $provider_scopes), SORT_STRING); $query_params['scope'] = implode(' ', $unique_scopes); log_debug('scopes = %s', print_r($query_params['scope'], true)); $code_verifier = base64url_encode(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); $_SESSION['code_verifier'] = $code_verifier; $code_challenge = base64url_encode(hash('sha256', $code_verifier, true)); $query_params['code_challenge_method'] = 'S256'; $query_params['code_challenge'] = $code_challenge; log_debug("code verifier : %s challenge : %s method : %s", $code_verifier, $code_challenge, $query_params['code_challenge_method']); if ($_REQUEST['response_mode']) { $query_params['response_mode'] = $_REQUEST['response_mode']; } if ($_REQUEST['page']) { $query_params['page'] = $_REQUEST['page']; } $prompt_types = array('none', 'login', 'consent', 'select_account'); $prompt = array(); foreach ($prompt_types as $prompt_type) { $param_name = 'prompt_' . $prompt_type; if ($_REQUEST[$param_name] == 'on') { $prompt[] = $prompt_type; } } if (count($prompt)) { $query_params['prompt'] = implode(' ', $prompt); } if ($_REQUEST['id_token']) { $query_params['id_token_hint'] = $_REQUEST['id_token']; } if ($_REQUEST['login_hint']) { $query_params['login_hint'] = $_REQUEST['login_hint']; } $custom_params = array(); if ($_REQUEST['request_option']) { // $custom_query = array(); if (strstr($_REQUEST['request_option'], 'Custom') !== false) { switch ($_REQUEST['request_option']) { case 'Custom 19': $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true))); break; case 'Custom 20': $custom_params['claims'] = array('userinfo' => array('email' => NULL, 'picture' => NULL)); break; case 'Custom 21': $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true), 'email' => NULL, 'picture' => NULL)); break; case 'Custom 22': $custom_params['claims'] = array('id_token' => array('auth_time' => array('essential' => true))); break; case 'Custom 23': $custom_params['claims'] = array('id_token' => array('acr' => array('values' => array('0', '1', '2'), 'essential' => true))); break; case 'Custom 24': $custom_params['claims'] = array('id_token' => array('acr' => array('values' => array('0', '1', '2')))); break; case 'Custom 25a': $query_params['max_age'] = 1; break; case 'Custom 25b': $query_params['max_age'] = 10; break; case 'Custom Dist': $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true), 'email' => NULL, 'picture' => NULL, 'undergrad_school' => array('essential' => true), 'graduate_school' => array('essential' => true), 'undergrad_degrees' => array('essential' => true), 'graduate_degrees' => array('essential' => true))); break; case 'Custom Req 1': $query_params['max_age'] = 1 * 60; $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true), 'given_name' => array('essential' => true), 'family_name' => array('essential' => true), 'middle_name' => array('essential' => true), 'address' => array('essential' => true), 'nickname' => NULL, 'profile' => NULL, 'given_name#ja-Kana-JP' => NULL, 'given_name#ja_Hani-JP' => NULL), 'id_token' => array('given_name' => array('essential' => true), 'family_name' => array('essential' => true), 'email' => array('essential' => true), 'gender' => array('essential' => true), 'address' => array('essential' => true), 'auth_time' => array('essential' => true))); break; case 'Custom Req 2': $query_params['max_age'] = 1 * 60; $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true)), 'id_token' => array('auth_time' => array('essential' => true))); break; default: break; } } } $request_method = $_REQUEST['request_method']; if ($_REQUEST['request_option']) { if ($request_method == 'GET') { if (count($custom_params)) { $query_params['claims'] = json_encode($custom_params['claims']); } } else { if (strstr($request_method, 'Request File') !== false) { $fileid = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); // $query_params['request_uri'] = RP_INDEX_PAGE . "/reqfile?fileid={$fileid}"; } else { $fileid = NULL; } $sig_param = array('alg' => 'none'); if ($_REQUEST['request_object_signing_alg']) { $sig_param['alg'] = $_REQUEST['request_object_signing_alg']; } if (substr($sig_param['alg'], 0, 2) == 'HS') { $sig_key = $provider['client_secret']; } elseif (substr($sig_param['alg'], 0, 2) == 'RS') { $sig_param['jku'] = RP_JWK_URL; $sig_param['kid'] = RP_SIG_KID; $sig_key = array('key_file' => RP_SIG_PKEY, 'password' => RP_SIG_PKEY_PASSPHRASE); } log_debug("Request Object Using Sig Alg %s", $sig_param['alg']); $request_jwt = jwt_sign(array_merge(array_diff_key($query_params, array('id_token' => 0)), $custom_params), $sig_param, $sig_key); if (!$request_jwt) { $g_error .= 'Unable to sign request object'; return; } if ($_REQUEST['request_object_encrypted_response_alg'] && $_REQUEST['request_object_encrypted_response_enc']) { $supported_cek_algs = array('RSA1_5', 'RSA-OAEP'); $supported_plaintext_algs = array('A128GCM', 'A256GCM', 'A128CBC-HS256', 'A256CBC-HS512'); if (!in_array($_REQUEST['request_object_encrypted_response_alg'], $supported_cek_algs)) { $g_error .= "Unsupported Request Object CEK Alg"; return; } if (!in_array($_REQUEST['request_object_encrypted_response_enc'], $supported_plaintext_algs)) { $g_error .= "Unsupported Request Object Plaintext Alg"; return; } $jwk_uri = ''; $encryption_keys = NULL; if ($provider['jwks_uri']) { $jwk = get_url($provider['jwks_uri']); if ($jwk) { $jwk_uri = $provider['jwks_uri']; $encryption_keys = jwk_get_keys($jwk, 'RSA', 'enc', NULL); if (!$encryption_keys || !count($encryption_keys)) { $encryption_keys = NULL; } } } if (!$encryption_keys) { $g_error .= 'No JWK key for encryption'; return NULL; } $header_params = array('jku' => $jwk_uri); if (isset($encryption_keys[0]['kid'])) { $header_params['kid'] = $encryption_keys[0]['kid']; } $encrypted_jwt = jwt_encrypt2($request_jwt, $encryption_keys[0], false, NULL, $header_params, NULL, $_REQUEST['request_object_encrypted_response_alg'], $_REQUEST['request_object_encrypted_response_enc'], false); if (!$encrypted_jwt) { $g_error .= "Unable to encrypt request object."; return; } else { $request_jwt = $encrypted_jwt; } } else { // $custom_query['request'] = $request_jwt; } // if(isset($query_params['request_uri'])) { // save file to db if (isset($fileid)) { // save file to db $query_params['request_uri'] = RP_INDEX_PAGE . "/reqfile?fileid={$fileid}"; $reqfile = array('type' => $encrypted_jwt ? 1 : 0, 'request' => json_encode(array_merge($query_params, $custom_params)), 'jwt' => $request_jwt); log_debug('query_params = %s custom = %s', print_r($query_params, true), print_r($custom_params, true)); db_save_request_file($fileid, $reqfile); } else { $query_params['request'] = $request_jwt; } } } $url = $provider['authorization_endpoint'] . '?' . http_build_query($query_params); log_info("redirect to %s", $url); header("Location: {$url}"); exit; }