diff --git a/matcher/CMakeLists.txt b/matcher/CMakeLists.txt new file mode 100644 index 0000000..206c020 --- /dev/null +++ b/matcher/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.30) +project(Matcher) + +# Add include directories +include_directories(. cJSON) + +# Common library source files +set(COMMON_LIB_SRCS + cJSON/cJSON.c + credentialmanager.c + base64.c +) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Target: openid4vp1_0.wasm +add_library(openid4vp1_0 openid4vp1_0.c dcql.c ${COMMON_LIB_SRCS}) +set_target_properties(openid4vp1_0 PROPERTIES SUFFIX ".wasm") + +# Target: issuance_provision.wasm +add_library(issuance_provision issuance/provision.c dcql.c ${COMMON_LIB_SRCS}) +set_target_properties(issuance_provision PROPERTIES SUFFIX ".wasm") + +# Target: pnv_openid4vp1_0.wasm +add_library(pnv_openid4vp1_0 pnv/openid4vp1_0.c pnv/dcql.c ${COMMON_LIB_SRCS}) +set_target_properties(pnv_openid4vp1_0 PROPERTIES SUFFIX ".wasm") + +# Native library for testing +add_library(pnv_openid4vp1_0_native pnv/openid4vp1_0.c pnv/dcql.c ${COMMON_LIB_SRCS}) + +# Googletest setup +enable_testing() + +find_package(doctest REQUIRED) + +# Test for pnv/openid4vp1_0 +add_executable(pnv_openid4vp1_0_test pnv/test/openid4vp1_0_test.cpp pnv/test/common.cpp) +target_link_libraries(pnv_openid4vp1_0_test PRIVATE doctest::doctest pnv_openid4vp1_0_native) +add_test(NAME pnv_openid4vp1_0_test COMMAND pnv_openid4vp1_0_test) + diff --git a/matcher/base64.h b/matcher/base64.h index daa7f9c..c0c373f 100644 --- a/matcher/base64.h +++ b/matcher/base64.h @@ -1,6 +1,14 @@ #ifndef BASE64_H #define BASE64_H +#ifdef __cplusplus +extern "C" { +#endif + int B64DecodeURL(char* input, char** output); +#ifdef __cplusplus +} #endif + +#endif \ No newline at end of file diff --git a/matcher/credentialmanager.h b/matcher/credentialmanager.h index ca21af7..df06ddc 100644 --- a/matcher/credentialmanager.h +++ b/matcher/credentialmanager.h @@ -1,6 +1,10 @@ #ifndef CREDENTIALMANAGER_H #define CREDENTIALMANAGER_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include @@ -108,4 +112,8 @@ __attribute__((import_module("credman_v4"), import_name("SelfDeclarePackageInfo" #endif void SelfDeclarePackageInfo(char *package_display_name, char* package_icon, size_t package_icon_len); -#endif +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/matcher/dcql.h b/matcher/dcql.h index 4edeb2c..5e6e96a 100644 --- a/matcher/dcql.h +++ b/matcher/dcql.h @@ -1,8 +1,16 @@ #ifndef DCQL_H #define DCQL_H +#ifdef __cplusplus +extern "C" { +#endif + #include "cJSON/cJSON.h" cJSON* dcql_query(const int request_id, cJSON* query, cJSON* credential_store); -#endif \ No newline at end of file +#ifdef __cplusplus +} +#endif + +#endif diff --git a/matcher/pnv/dcql.c b/matcher/pnv/dcql.c index c4daac3..25abad1 100644 --- a/matcher/pnv/dcql.c +++ b/matcher/pnv/dcql.c @@ -1,5 +1,6 @@ #include #include +#include #include "../base64.h" #include "../dcql.h" @@ -24,7 +25,22 @@ int AddAllClaims(cJSON *matched_claim_names, cJSON *candidate_paths) return 0; } -cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) +cJSON *CreateSingleStringArrayJson(char *string) +{ + cJSON *array = cJSON_CreateArray(); + cJSON_AddItemReferenceToArray(array, cJSON_CreateString(string)); + return array; +} + +enum ClaimMatchResult +{ + CLAIM_MATCH_UNKNOWN = 0, + CLAIM_MATCH_YES = 1, + CLAIM_MATCH_NO = -1, +}; + +cJSON * +MatchCredential(cJSON *credential, cJSON *credential_store) { cJSON *matched_credentials = cJSON_CreateArray(); char *format = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(credential, "format")); @@ -86,14 +102,16 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) cJSON_AddItemReferenceToArray(candidates, curr_candidate); } else - { + { cJSON *allowed_iss; - cJSON_ArrayForEach(allowed_iss, iss_allowlist) { - if (cJSON_Compare(allowed_iss, iss_value, cJSON_True)) { - printf("A candidate credential of type %s passed iss allowlist check.\n", cJSON_GetStringValue(vct_value)); - cJSON_AddItemReferenceToArray(candidates, curr_candidate); - break; - } + cJSON_ArrayForEach(allowed_iss, iss_allowlist) + { + if (cJSON_Compare(allowed_iss, iss_value, cJSON_True)) + { + printf("A candidate credential of type %s passed iss allowlist check.\n", cJSON_GetStringValue(vct_value)); + cJSON_AddItemReferenceToArray(candidates, curr_candidate); + break; + } } } } @@ -155,6 +173,19 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) } else { + cJSON *matched_claim_paths = cJSON_CreateArray(); + + cJSON *phone_number_matched_candidates = cJSON_CreateArray(); + cJSON *carrier_and_subscription_matched_candidates = cJSON_CreateArray(); + cJSON *carrier_matched_candidates = cJSON_CreateArray(); + cJSON *sub_matched_candidates = cJSON_CreateArray(); + cJSON *other_candidates = cJSON_CreateArray(); + + cJSON *phone_number_hint_paths = CreateSingleStringArrayJson("phone_number_hint"); + cJSON *subscription_hint_paths = CreateSingleStringArrayJson("subscription_hint"); + cJSON *carrier_hint_paths = CreateSingleStringArrayJson("carrier_hint"); + cJSON *android_carrier_hint_paths = CreateSingleStringArrayJson("android_carrier_hint"); + if (claim_sets == NULL) { printf("Matching based on provided claims\n"); @@ -176,7 +207,10 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) cJSON *claim; cJSON *candidate_claims = cJSON_GetObjectItemCaseSensitive(candidate, "paths"); - int matched_claim_count = 0; + enum ClaimMatchResult phone_number_matched = CLAIM_MATCH_UNKNOWN; + enum ClaimMatchResult carrier_matched = CLAIM_MATCH_UNKNOWN; + enum ClaimMatchResult android_carrier_matched = CLAIM_MATCH_UNKNOWN; + enum ClaimMatchResult subscription_matched = CLAIM_MATCH_UNKNOWN; cJSON_ArrayForEach(claim, claims) { cJSON *claim_values = cJSON_GetObjectItemCaseSensitive(claim, "values"); @@ -201,6 +235,7 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) break; } } + bool match_claim = 0; if (matched != 0 && curr_claim != NULL) { if (claim_values != NULL) @@ -211,7 +246,7 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) if (cJSON_Compare(v, cJSON_GetObjectItemCaseSensitive(curr_claim, "value"), cJSON_True)) { printf("- claim value matched.\n"); - ++matched_claim_count; + match_claim = 1; break; } } @@ -219,19 +254,55 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) else { printf("- claim matched.\n"); - ++matched_claim_count; + match_claim = 1; } - } else { + } + else + { printf("- claim did not match\n."); } + if (matched) + { + enum ClaimMatchResult result = match_claim ? CLAIM_MATCH_YES : CLAIM_MATCH_NO; + if (cJSON_Compare(paths, phone_number_hint_paths, cJSON_True)) + { + phone_number_matched = result; + } + else if (cJSON_Compare(paths, subscription_hint_paths, cJSON_True)) + { + subscription_matched = result; + } + else if (cJSON_Compare(paths, carrier_hint_paths, cJSON_True)) + { + carrier_matched = result; + } + else if (cJSON_Compare(paths, android_carrier_hint_paths, cJSON_True)) + { + android_carrier_matched = result; + } + } } + bool carrier_matched_final = carrier_matched + android_carrier_matched >= 1; cJSON_AddItemReferenceToObject(matched_credential, "matched_claim_names", matched_claim_names); - if (matched_claim_count == cJSON_GetArraySize(claims)) + if (phone_number_matched == CLAIM_MATCH_YES) + { + cJSON_AddItemReferenceToArray(phone_number_matched_candidates, matched_credential); + } + else if (carrier_matched_final && subscription_matched == CLAIM_MATCH_YES) + { + cJSON_AddItemReferenceToArray(carrier_and_subscription_matched_candidates, matched_credential); + } + else if (carrier_matched_final) + { + cJSON_AddItemReferenceToArray(carrier_matched_candidates, matched_credential); + } + else if (subscription_matched == CLAIM_MATCH_YES) { - printf("Cred matched.\n"); - cJSON_AddItemReferenceToArray(matched_credentials, matched_credential); - } else { - printf("Cred did not match. Matched claim count: %d, Expected match count: %d\n.", matched_claim_count, cJSON_GetArraySize(claims)); + cJSON_AddItemReferenceToArray(sub_matched_candidates, matched_credential); + } + else + { + cJSON_AddItemReferenceToArray(other_candidates, matched_credential); } } } @@ -255,6 +326,10 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) cJSON *claim; cJSON *candidate_claims = cJSON_GetObjectItemCaseSensitive(candidate, "paths"); + enum ClaimMatchResult phone_number_matched = CLAIM_MATCH_UNKNOWN; + enum ClaimMatchResult carrier_matched = CLAIM_MATCH_UNKNOWN; + enum ClaimMatchResult android_carrier_matched = CLAIM_MATCH_UNKNOWN; + enum ClaimMatchResult subscription_matched = CLAIM_MATCH_UNKNOWN; cJSON_ArrayForEach(claim, claims) { cJSON *claim_values = cJSON_GetObjectItemCaseSensitive(claim, "values"); @@ -276,6 +351,7 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) break; } } + bool match_claim = 0; if (matched != 0 && curr_claim != NULL) { if (claim_values != NULL) @@ -285,17 +361,41 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) { if (cJSON_Compare(v, cJSON_GetObjectItemCaseSensitive(curr_claim, "value"), cJSON_True)) { - cJSON_AddItemReferenceToObject(matched_claim_ids, claim_id, cJSON_CreateString("PLACEHOLDER")); + match_claim = 1; break; } } } else { - cJSON_AddItemReferenceToObject(matched_claim_ids, claim_id, cJSON_CreateString("PLACEHOLDER")); + match_claim = 1; + } + } + if (matched) + { + enum ClaimMatchResult result = CLAIM_MATCH_YES; + cJSON_AddItemReferenceToObject(matched_claim_ids, claim_id, cJSON_CreateString("PLACEHOLDER")); + if (cJSON_Compare(paths, phone_number_hint_paths, cJSON_True)) + { + phone_number_matched = result; + } + else if (cJSON_Compare(paths, subscription_hint_paths, cJSON_True)) + { + subscription_matched = result; + } + else if (cJSON_Compare(paths, carrier_hint_paths, cJSON_True)) + { + carrier_matched = result; + } + else if (cJSON_Compare(paths, android_carrier_hint_paths, cJSON_True)) + { + android_carrier_matched = result; } } } + + bool carrier_matched_final = carrier_matched + android_carrier_matched >= 1; + cJSON *claim_set; cJSON_ArrayForEach(claim_set, claim_sets) { @@ -317,8 +417,50 @@ cJSON *MatchCredential(cJSON *credential, cJSON *credential_store) break; } } + if (phone_number_matched == CLAIM_MATCH_YES) + { + cJSON_AddItemReferenceToArray(phone_number_matched_candidates, matched_credential); + } + else if (carrier_matched_final && subscription_matched == CLAIM_MATCH_YES) + { + cJSON_AddItemReferenceToArray(carrier_and_subscription_matched_candidates, matched_credential); + } + else if (carrier_matched_final) + { + cJSON_AddItemReferenceToArray(carrier_matched_candidates, matched_credential); + } + else if (subscription_matched == CLAIM_MATCH_YES) + { + cJSON_AddItemReferenceToArray(sub_matched_candidates, matched_credential); + } + else + { + cJSON_AddItemReferenceToArray(other_candidates, matched_credential); + } } } + + cJSON *c; + cJSON_ArrayForEach(c, phone_number_matched_candidates) + { + cJSON_AddItemReferenceToArray(matched_credentials, c); + } + cJSON_ArrayForEach(c, carrier_and_subscription_matched_candidates) + { + cJSON_AddItemReferenceToArray(matched_credentials, c); + } + cJSON_ArrayForEach(c, carrier_matched_candidates) + { + cJSON_AddItemReferenceToArray(matched_credentials, c); + } + cJSON_ArrayForEach(c, sub_matched_candidates) + { + cJSON_AddItemReferenceToArray(matched_credentials, c); + } + cJSON_ArrayForEach(c, other_candidates) + { + cJSON_AddItemReferenceToArray(matched_credentials, c); + } } return matched_credentials; diff --git a/matcher/pnv/openid4vp1_0.c b/matcher/pnv/openid4vp1_0.c index afa9222..d72446d 100644 --- a/matcher/pnv/openid4vp1_0.c +++ b/matcher/pnv/openid4vp1_0.c @@ -31,7 +31,11 @@ cJSON *GetCredsJson() return cJSON_Parse(creds_json); } +#if defined(__wasm__) int main() +#else +int openid_main() +#endif { uint32_t credentials_size; GetCredentialsSize(&credentials_size); diff --git a/matcher/pnv/test/common.cpp b/matcher/pnv/test/common.cpp new file mode 100644 index 0000000..5eb2f25 --- /dev/null +++ b/matcher/pnv/test/common.cpp @@ -0,0 +1,210 @@ +#include "credentialmanager.h" +#include "common.hpp" + +#include + +#include +#include + +using json = nlohmann::json; + +TestCredmanState &TestCredmanState::instance() +{ + static TestCredmanState state; + return state; +} + +RequestGenerator::RequestGenerator() +{ + request_json_ = json::parse(R"({ + "requests": [ + { + "protocol": "openid4vp-v1-unsigned", + "data": { + "dcql_query": { + "credentials": [ + { + "claims": [], + "format": "dc-authorization+sd-jwt", + "id": "aggregator1", + "meta": { + "credential_authorization_jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QiLCJ4NWMiOlsiTUlJQ3BUQ0NBa3VnQXdJQkFnSVVDOWZOSnBkVU1RWWRCbDFuaDgrUml0UndNRDh3Q2dZSUtvWkl6ajBFQXdJd2VERUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZqQVVCZ05WQkFjTURVMXZkVzUwWVdsdUlGWnBaWGN4R3pBWkJnTlZCQW9NRWtWNFlXMXdiR1VnUVdkbmNtVm5ZWFJ2Y2pFZk1CMEdBMVVFQXd3V1pYaGhiWEJzWlMxaFoyZHlaV2RoZEc5eUxtUmxkakFlRncweU5UQTFNVEV5TWpRd01EVmFGdzB6TlRBME1qa3lNalF3TURWYU1IZ3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SWXdGQVlEVlFRSERBMU5iM1Z1ZEdGcGJpQldhV1YzTVJzd0dRWURWUVFLREJKRmVHRnRjR3hsSUVGblozSmxaMkYwYjNJeEh6QWRCZ05WQkFNTUZtVjRZVzF3YkdVdFlXZG5jbVZuWVhSdmNpNWtaWFl3V1RBVEJnY3Foa2pPUFFJQkJnZ3Foa2pPUFFNQkJ3TkNBQVJRcW5LTGw5U2g4dFcwM0h5aVBnOVRUcGlyQVg2V2haKzlJSWhVWFJGcDlxRFM0eW5YeG1GbjMzWk5nMTlQR1VzRWpxNGwzam9Penh2cHhqWDRoL1JlbzRHeU1JR3ZNQjBHQTFVZERnUVdCQlFBV1I5czRrWFRjeHJPeTFLSE12UldTSkg5YmpBZkJnTlZIU01FR0RBV2dCUUFXUjlzNGtYVGN4ck95MUtITXZSV1NKSDliakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTRHQTFVZER3RUIvd1FFQXdJSGdEQXBCZ05WSFJJRUlqQWdoaDVvZEhSd2N6b3ZMMlY0WVcxd2JHVXRZV2RuY21WbllYUnZjaTVqYjIwd0lRWURWUjBSQkJvd0dJSVdaWGhoYlhCc1pTMWhaMmR5WldkaGRHOXlMbU52YlRBS0JnZ3Foa2pPUFFRREFnTklBREJGQWlCeERROUZiby9EUVRkbVNaS0NURUlHOXZma0JkWU5jVHcxUkkzT0k2L25KUUloQUw1NmU3YkVNOTlSTTFTUDAyd3gzbHhxZFZCWnhiVEhJcllCQkY3Y0FzYjMiXX0.eyJpc3MiOiAiZGNhZ2dyZWdhdG9yLmRldiIsICJub25jZSI6ICJrazQzSkthUHNjYWpqWHAzNGZSOHB1SGp0UE1yY09CMzJLNXdLTUQ1Q2J3IiwgImVuY3J5cHRlZF9yZXNwb25zZV9lbmNfdmFsdWVzX3N1cHBvcnRlZCI6IFsiQTEyOEdDTSJdLCAiandrcyI6IHsia2V5cyI6IFt7Imt0eSI6ICJFQyIsICJ1c2UiOiAiZW5jIiwgImFsZyI6ICJFQ0RILUVTIiwgImtpZCI6ICIxIiwgImNydiI6ICJQLTI1NiIsICJ4IjogIjl5TGgtNkJJQ1pMUWdKcGEzdl9FQS1ZbkIyU2FhV1BLWGZQWGNKa2EwMGciLCAieSI6ICJKNkRFWXV5SW90NDM0WG5WOE5GTWppb1cxLUFtSkVCRHdwTW9wRUt4WUdrIn1dfSwgImNvbnNlbnRfZGF0YSI6ICJleUpqYjI1elpXNTBYM1JsZUhRaU9pQWlVbWxrWlhJZ2NISnZZMlZ6YzJWeklIbHZkWElnY0dWeWMyOXVZV3dnWkdGMFlTQmhZMk52Y21ScGJtY2dkRzhnYjNWeUlIQnlhWFpoWTNrZ2NHOXNhV041SWl3Z0luQnZiR2xqZVY5c2FXNXJJam9nSW1oMGRIQnpPaTh2WkdWMlpXeHZjR1Z5TG1GdVpISnZhV1F1WTI5dEwybGtaVzUwYVhSNUwyUnBaMmwwWVd3dFkzSmxaR1Z1ZEdsaGJITXZZM0psWkdWdWRHbGhiQzEyWlhKcFptbGxjaUlzSUNKd2IyeHBZM2xmZEdWNGRDSTZJQ0pNWldGeWJpQmhZbTkxZENCd2NtbDJZV041SUhCdmJHbGplU0o5IiwgInN0YXRlIjogIm9wdGlvbmFsX3N0YXRlX3ZhbHVlIn0.w7_X5hLwjDxw26GguGjxuJnhxfcmqtbcCPiTobUrGpoFIvYWat9Luqi5r8ZTu_CIfC3rismGsYZH6ozNQwXgnw" + } + } + ] + }, + "nonce": "kk43JKaPscajjXp34fR8puHjtPMrcOB32K5wKMD5Cbw", + "response_mode": "dc_api", + "response_type": "vp_token" + } + } + ] + })"); +} + +RequestGenerator &RequestGenerator::with_phone_number_hint(const std::vector &hints) +{ + if (!hints.empty()) + { + request_json_["requests"][0]["data"]["dcql_query"]["credentials"][0]["claims"].push_back({{"path", {"phone_number_hint"}}, {"values", hints}}); + } + return *this; +} + +RequestGenerator &RequestGenerator::with_carrier_hint(const std::vector &hints) +{ + if (!hints.empty()) + { + request_json_["requests"][0]["data"]["dcql_query"]["credentials"][0]["claims"].push_back({{"path", {"carrier_hint"}}, {"values", hints}}); + } + return *this; +} + +RequestGenerator &RequestGenerator::with_android_carrier_hint(const std::vector &hints) +{ + if (!hints.empty()) + { + request_json_["requests"][0]["data"]["dcql_query"]["credentials"][0]["claims"].push_back({{"path", {"android_carrier_hint"}}, {"values", hints}}); + } + return *this; +} + +RequestGenerator &RequestGenerator::with_subscription_hint(const std::vector &hints) +{ + if (!hints.empty()) + { + request_json_["requests"][0]["data"]["dcql_query"]["credentials"][0]["claims"].push_back({{"path", {"subscription_hint"}}, {"values", hints}}); + } + return *this; +} + +RequestGenerator &RequestGenerator::with_vct_values(const std::vector &values) +{ + if (!values.empty()) + { + request_json_["requests"][0]["data"]["dcql_query"]["credentials"][0]["meta"]["vct_values"] = values; + } + return *this; +} + +std::string RequestGenerator::build() +{ + return request_json_.dump(4); +} + +extern "C" +{ + void GetCredentialsSize(uint32_t *size) + { + *size = (uint32_t)TestCredmanState::instance().credentials_buffer.size(); + } + size_t ReadCredentialsBuffer(void *buffer, size_t offset, size_t len) + { + if (offset + len > TestCredmanState::instance().credentials_buffer.size()) + { + len = TestCredmanState::instance().credentials_buffer.size() - offset; + } + memcpy(buffer, TestCredmanState::instance().credentials_buffer.data() + offset, len); + return len; + } + void GetWasmVersion(uint32_t *version) + { + *version = -1; + } + void GetRequestSize(uint32_t *size) + { + *size = (uint32_t)TestCredmanState::instance().request_buffer.size(); + } + void GetRequestBuffer(void *buffer) + { + memcpy(buffer, TestCredmanState::instance().request_buffer.data(), TestCredmanState::instance().request_buffer.size()); + } + + void AddStringIdEntry(char *cred_id, char *icon, size_t icon_len, char *title, char *subtitle, char *disclaimer, char *warning) + { + if (!cred_id) + return; + StringIdEntry entry; + entry.id = cred_id; + if (icon) + entry.icon = std::string(icon, icon_len); + if (title) + entry.title = title; + if (subtitle) + entry.subtitle = subtitle; + if (disclaimer) + entry.disclaimer = disclaimer; + if (warning) + entry.warning = warning; + TestCredmanState::instance().string_id_entries.push_back(entry); + } + + void SetAdditionalDisclaimerAndUrlForVerificationEntry(char *cred_id, char *secondary_disclaimer, char *url_display_text, char *url_value) + { + if (!cred_id) + return; + auto &entries = TestCredmanState::instance().string_id_entries; + auto it = std::find_if(entries.begin(), entries.end(), [&](const StringIdEntry &entry) + { return entry.id == cred_id; }); + if (it != entries.end()) + { + if (secondary_disclaimer) + it->secondary_disclaimer = secondary_disclaimer; + if (url_display_text) + it->url_display_text = url_display_text; + if (url_value) + it->url_value = url_value; + } + } + + void AddFieldForStringIdEntry(char *cred_id, char *field_display_name, char *field_display_value) + { + if (!cred_id) + return; + auto &entries = TestCredmanState::instance().string_id_entries; + auto it = std::find_if(entries.begin(), entries.end(), [&](const StringIdEntry &entry) + { return entry.id == cred_id; }); + if (it != entries.end()) + { + it->fields.emplace_back( + field_display_name ? field_display_name : "", + field_display_value ? field_display_value : ""); + } + } + + void AddPaymentEntry(char *cred_id, char *merchant_name, char *payment_method_name, char *payment_method_subtitle, char *payment_method_icon, size_t payment_method_icon_len, char *transaction_amount, char *bank_icon, size_t bank_icon_len, char *payment_provider_icon, size_t payment_provider_icon_len) + { + if (!cred_id) + return; + PaymentEntry entry; + entry.id = cred_id; + if (merchant_name) + entry.merchant_name = merchant_name; + if (payment_method_name) + entry.payment_method_name = payment_method_name; + if (payment_method_subtitle) + entry.payment_method_subtitle = payment_method_subtitle; + if (payment_method_icon) + entry.payment_method_icon = std::string(payment_method_icon, payment_method_icon_len); + if (transaction_amount) + entry.transaction_amount = transaction_amount; + if (bank_icon) + entry.bank_icon = std::string(bank_icon, bank_icon_len); + if (payment_provider_icon) + entry.payment_provider_icon = std::string(payment_provider_icon, payment_provider_icon_len); + TestCredmanState::instance().payment_entries.push_back(entry); + } +} + +doctest::String toString(const TestCredmanState &state) +{ + doctest::String s; + s += "string_id_entries:\n"; + for (const auto &entry : state.string_id_entries) + { + s += " id: "; + s += entry.id.c_str(); + s += "\n"; + } + return s; +} \ No newline at end of file diff --git a/matcher/pnv/test/common.hpp b/matcher/pnv/test/common.hpp new file mode 100644 index 0000000..f83446e --- /dev/null +++ b/matcher/pnv/test/common.hpp @@ -0,0 +1,71 @@ +#pragma once +#pragma once +#include +#include +#include + +#include +#include + +struct StringIdEntry +{ + std::string id; + std::string icon; + std::string title; + std::string subtitle; + std::string disclaimer; + std::string warning; + std::string secondary_disclaimer; + std::string url_display_text; + std::string url_value; + std::vector> fields; +}; + +struct PaymentEntry +{ + std::string id; + std::string merchant_name; + std::string payment_method_name; + std::string payment_method_subtitle; + std::string payment_method_icon; + std::string transaction_amount; + std::string bank_icon; + std::string payment_provider_icon; +}; + +struct TestCredmanState +{ + std::string request_buffer, credentials_buffer; + std::vector string_id_entries; + std::vector payment_entries; + + static TestCredmanState &instance(); +}; + +doctest::String toString(const TestCredmanState &state); + +struct TestCredmanStateGuard +{ + ~TestCredmanStateGuard() + { + TestCredmanState::instance().string_id_entries.clear(); + TestCredmanState::instance().payment_entries.clear(); + } +}; + +class RequestGenerator +{ +public: + RequestGenerator(); + RequestGenerator &with_phone_number_hint(const std::vector &hints); + RequestGenerator &with_carrier_hint(const std::vector &hints); + RequestGenerator &with_android_carrier_hint(const std::vector &hints); + RequestGenerator &with_subscription_hint(const std::vector &hints); + RequestGenerator &with_vct_values(const std::vector &values); + std::string build(); + +private: + nlohmann::json request_json_; +}; + +extern TestCredmanState testCredmanState; diff --git a/matcher/pnv/test/data/pnv_registry.json b/matcher/pnv/test/data/pnv_registry.json new file mode 100644 index 0000000..e74d150 --- /dev/null +++ b/matcher/pnv/test/data/pnv_registry.json @@ -0,0 +1,842 @@ +{ + "credentials": { + "dc-authorization+sd-jwt": { + "number-verification/verify/ts43": [ + { + "shared_attribute_display_name": "Phone number", + "id": "verify_1", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_2", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_3", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_4", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_5", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_6", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_7", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_8", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_9", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_10", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_11", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_12", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_13", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_14", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_15", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "verify_16", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + } + ], + "number-verification/phone_number/ts43": [ + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_1", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_2", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_3", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_4", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_5", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_6", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_7", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_8", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 1 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_9", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_10", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_11", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_12", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "310250" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_13", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_14", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 3 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_15", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+16502154321" + } + } + }, + { + "shared_attribute_display_name": "Phone number", + "id": "phone_number_16", + "title": "Terrific Telecom", + "subtitle": "+1 (650) 215-4321", + "disclaimer": "CMWallet will enable your carrier (Terrific Telecom) to share your phone number with ${CALLER_DISPLAY_NAME}", + "verifier_terms_prefix": "Provider Terms:\n", + "icon": { + "start": 0, + "length": 0 + }, + "paths": { + "subscription_hint": { + "value": 11 + }, + "carrier_hint": { + "value": "110999" + }, + "android_carrier_hint": { + "value": 33 + }, + "phone_number_hint": { + "value": "+19211104544" + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/matcher/pnv/test/openid4vp1_0_test.cpp b/matcher/pnv/test/openid4vp1_0_test.cpp new file mode 100644 index 0000000..37190bc --- /dev/null +++ b/matcher/pnv/test/openid4vp1_0_test.cpp @@ -0,0 +1,114 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include + +#include "common.hpp" + +#include +#include +#include +#include // Required for std::ostringstream + +extern "C" int openid_main(); + +// Helper function to get the path to a test data file relative to the current source file +std::string getTestDataPath(const std::string &relative_path) +{ + std::filesystem::path source_path = __FILE__; + std::filesystem::path source_dir = source_path.parent_path(); + return (source_dir / relative_path).string(); +} + +// Helper function to read the entire content of a file into a std::string +std::string readFileToString(const std::string &file_path) +{ + std::ifstream input_file(file_path, std::ios::binary); + if (!input_file.is_open()) + { + return ""; // Return empty string if file cannot be opened + } + std::ostringstream ss; + ss << input_file.rdbuf(); + return ss.str(); +} + +TEST_CASE("OpenID4VP") +{ + using namespace std::string_literals; + TestCredmanState::instance().credentials_buffer = + std::string({'\4', '\0', '\0', '\0'}) + + readFileToString(getTestDataPath("data/pnv_registry.json")); + + SUBCASE("Only filter by phone number") + { + TestCredmanStateGuard guard; + TestCredmanState::instance().request_buffer = + RequestGenerator() + .with_phone_number_hint({"+16502154321", "+16502154322", "+16502154323"}) + .with_vct_values({"number-verification/verify/ts43"}) + .build(); + REQUIRE_EQ(0, openid_main()); + CAPTURE(TestCredmanState::instance()); + + REQUIRE(TestCredmanState::instance().string_id_entries.size() == 16); + REQUIRE(TestCredmanState::instance().string_id_entries[0].id == R"({"entry_id":"verify_1","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[1].id == R"({"entry_id":"verify_3","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[2].id == R"({"entry_id":"verify_5","dcql_cred_id":"aggregator1","req_idx":0})"s); + + REQUIRE(TestCredmanState::instance().string_id_entries[8].id == R"({"entry_id":"verify_2","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[9].id == R"({"entry_id":"verify_4","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[10].id == R"({"entry_id":"verify_6","dcql_cred_id":"aggregator1","req_idx":0})"s); + } + + SUBCASE("Filter by both carrier and android carrier hint requiring both matches") + { + TestCredmanStateGuard guard; + TestCredmanState::instance().request_buffer = + RequestGenerator() + .with_carrier_hint({"22222", "110999"}) + .with_android_carrier_hint({11, 22, 33}) + .with_vct_values({"number-verification/verify/ts43"}) + .build(); + REQUIRE_EQ(0, openid_main()); + CAPTURE(TestCredmanState::instance()); + + REQUIRE(TestCredmanState::instance().string_id_entries.size() == 16); + REQUIRE(TestCredmanState::instance().string_id_entries[0].id == R"({"entry_id":"verify_7","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[1].id == R"({"entry_id":"verify_8","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[2].id == R"({"entry_id":"verify_15","dcql_cred_id":"aggregator1","req_idx":0})"s); + } + + SUBCASE("Filter by carrier only") + { + TestCredmanStateGuard guard; + TestCredmanState::instance().request_buffer = + RequestGenerator() + .with_carrier_hint({"22222", "110999"}) + .with_vct_values({"number-verification/phone_number/ts43"}) + .build(); + REQUIRE_EQ(0, openid_main()); + CAPTURE(TestCredmanState::instance()); + + REQUIRE(TestCredmanState::instance().string_id_entries.size() == 16); + REQUIRE(TestCredmanState::instance().string_id_entries[0].id == R"({"entry_id":"phone_number_5","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[1].id == R"({"entry_id":"phone_number_6","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[2].id == R"({"entry_id":"phone_number_7","dcql_cred_id":"aggregator1","req_idx":0})"s); + } + + SUBCASE("Filter by both carrier and subscription hint ordering carrier matches first") + { + TestCredmanStateGuard guard; + TestCredmanState::instance().request_buffer = + RequestGenerator() + .with_carrier_hint({"22222", "110999"}) + .with_subscription_hint({11, 22, 33}) + .with_vct_values({"number-verification/verify/ts43"}) + .build(); + REQUIRE_EQ(0, openid_main()); + CAPTURE(TestCredmanState::instance()); + REQUIRE(TestCredmanState::instance().string_id_entries.size() == 16); + REQUIRE(TestCredmanState::instance().string_id_entries[0].id == R"({"entry_id":"verify_13","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[1].id == R"({"entry_id":"verify_14","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[2].id == R"({"entry_id":"verify_15","dcql_cred_id":"aggregator1","req_idx":0})"s); + REQUIRE(TestCredmanState::instance().string_id_entries[4].id == R"({"entry_id":"verify_5","dcql_cred_id":"aggregator1","req_idx":0})"s); + } +} \ No newline at end of file